Putting the work into development workflows

For the last 6 months, I’ve been focused on putting some work into revamping my team’s development workflows.

I lead the AffiliateWP development team (Team Alf) at Sandhills Development. In the past 6 months, we’ve grown from three developers up to five and most recently back down to four when one of our seniors left to lead development full time on another Sandhills product, the Payouts Service.

Growing pains

With the personnel changes came growing pains that have ultimately tested the viability of our development workflows. Turns out that while the gap between three and four developers might seem small, it actually is much more difficult to manage.

One of the ways we’ve seen our development workflows tested is in the code review space. When the team was two seniors and I sharing the work, me keeping up with code reviews was manageable. When we jumped to five (three seniors, a junior, and me), things soon became untenable.

For instance, we quickly realized the single point of review wasn’t going to work any longer. Turns out I can’t stay on top of four other developers’ code reviews and simultaneously make myself available as a support resource. At least not without working a whole lot more hours in the evenings and weekends.

Sandhills believes it doesn’t have to be crazy at work, so throwing more of my time at the problem wasn’t really an option.

Working on how we work

The team growth began to surface other problems as we went on, particularly in the area of managing our resources. At any given time, the team is typically juggling the next major or minor core release, various add-on updates, and other projects.

As I mentioned in my post yesterday about writing APIs, Team Alf’s sole focus right now is finishing the first release of our new Affiliate Dashboard pro add-on.

In theory, the four of us on the development team are effectively dividing the work. In practice, however, the three developers are largely dividing the main tasks and I’m pitching in where needed, shipping releases and generally picking up the slack.

Collaborative development workflows

Oh, and I’m still doing code reviews, but not like before. For example, we recently started to experiment with promoting more team collaboration:

  • Each major task is scoped for 4-5 days of work max
  • Each task has an “owner” but sometimes multiple people working on it
  • Every task gets reviewed and tested by every developer

So far this approach seems to be having positive effects, which is encouraging! More eyes are on what’s changing than ever before, and confidence in meeting our deadlines has never been higher.

Managing the management

In addition to task management changes, I’ve been looking inward at the work I do. I’ve been playing around with handing off some of my responsibilities to other developers on the team. That effort of lifting others up seems to be going well.

I can now be more available to work with my team instead of my team working with me.

I’m no longer herding cats 24/7 and the team is better for it. I can now be more available to work with my team instead of my team working with me.

I’m certain problems large and small will continue to crop up as we go. Moreover, I’m confident we’re on the right track to finding development workflows to work for us, instead of the other way around.

WYSIWYCE: What you see is what you can extend (in APIs)

What you see is what you can extend in APIs is the mantra I live by while writing my own APIs and when evaluating others’.

In my career as a web developer, I’ve interacted with probably hundreds of APIs, even written a fair few. Through all of that experience, I’ve come to one very specific conclusion about what constitutes “good” even “delightful” API design: Public access is key.

Public vs private APIs

The one marker, for me at least, that makes an API stand out is just how much the actual API code can be extended. That is, how much of the API is public and how much is private? And how much of the public vs private functionality is the API itself using?

Now, obviously, this discussion is heavily focused on FOSS APIs. This is largely because you can literally see and interact with the internals.

When examining public vs private, often the best approach is to consider the rules of the API itself. What constitutes “extendable” and what doesn’t? Does the core use a bunch of stuff hidden behind barriers or can you extend the same code core does? What is the core’s policy on backward compatibility?

Once you’ve set a public vs private baseline, then deciding openness is usually pretty simple. I’ve always felt that open and public should nearly always be preferred. It almost feels a little bit lazy for an API to bake in special goodies for itself then mark it private like it’s unfinished or something.

Examples in practice

We’re right in the middle of a big project at Sandhills right now. We’re building a new pro add-on for AffiliateWP called Affiliate Dashboard. It introduces a portal-like experience for affiliates, largely intended as a premium replacement for the standard core affiliate area.

The standard affiliate area API is template-based. Themes can override core templates, even introduce custom ones. It’s a very free and open extension environment but also very difficult to enforce high standards in the long term.

On the other hand, Affiliate Dashboard API is control-based. This means first- and third-party developers register display elements through the API and the add-on itself controls rendering them.

A dynamically-built Statistics view in Affiliate Dashboard.

At the time of writing, approximately 95% of the add-on dogfoods its own public APIs to render itself. We register views, sections, and controls the exact same way any other developer would. This is in part to prove a point that amazing things are possible, and in part to provide a living example for best practice.

For instance, want to see how we did something, maybe make your own version? The add-on source code is your best, first reference.

To be fair, there’s still a very small percentage of functionality we haven’t yet converted from its MVP form. By the time we ship the 1.0.0 stable in a few weeks, we’ll be entirely rendering on our own public APIs. Pretty sweet!

Delight and deliver

I like to think that we’ve designed the Affiliate Dashboard APIs with an intent to delight and deliver. Within the boundaries of what the API itself allows, what you can see is what you can extend.

Moreover, we’ve gone to great pains to make it feel consistent from control to control. For instance, some controls output very simple things, others do incredibly complex things under the hood like rendering a table using REST and Alpine JS.

The main point is that in trying to delight our extension community, we’ve forced ourselves to think simply when it comes to writing extensions:

  • All controls have a similar entry point
  • All controls use the same structure of attributes and arguments
  • Pretty much all controls support Alpine JS directives
  • All controls support a limited set of TailwindCSS classes

This consistency begs the question, “What is the fastest way to get developers from A to B and promise a really high quality result?

After some reflection, I think it’s to write a whole lot of excellent example code that oh hey, by the way, happens to run the add-on itself. Surprise!

Breaking backward compatibility should not be about convenience

A big part of WordPress’ philosophies is embracing backward compatibility. And that commitment, for the most part, largely spills over to plugins and themes extending it. At least it should.

So when I read on the WooCommerce development blog about upcoming changes in 2.7+, I was at first encouraged that they’re embracing an abstraction layer for meta handling. Nice! Then I got to the part where they said they essentially plan to break backward compatibility in a future version for meta handling.

If you do anything with product, customer, orders, and coupons, you will be affected in some way. Even if you do a simple update meta call. This won’t break immediately, but your code will not be future proof. As soon as the schema changes in another update, your code will fail.


There are several good, even necessary reasons to break backward compatibility.  For instance, sometimes a product will force a backward incompatible change because something is being deliberately misused outside of the original intent. That’s not what’s going on here.

Frankly, the WooCommerce team’s decision smacks of convenience more than anything. Supporting backward compatibility is sometimes hard, but rarely impossible. And by choosing to break it, they may be unnecessarily playing with the fire that is user trust.

Won’t somebody please think of the users?

User trust isn’t something you earn and then just get to keep forever. It’s a maintenance relationship. So for WooCommerce – an extremely popular product with immense reach in the WordPress ecosystem – I would consider a backward compatibility break of this magnitude to be borderline irresponsible.

There are likely hundreds, probably even thousands of commercial and custom extensions for WooCommerce. Most, if not all of them, up until 2.7, will have probably relied on its usage of post meta for everything from products and coupons, to customer and order data.

Coming out and saying that at some point you will stop using post meta is completely fine. Coming out and saying that some point you will stop supporting post meta is not.

Use the hooks, Luke

There are more than enough hooks in the Post Meta API to facilitate backward-compatibility for previously-post-meta-now-something else data.

I know for a fact that the Easy Digital Downloads team are using those hooks since moving to a custom schema from post meta. Disclosure: I work for AffiliateWP, a sister product of Easy Digital Downloads.

There’s nothing saying that the WooCommerce team has to encourage use of post meta. Feel free to toss deprecated notices or use _doing_it_wrong()s, but don’t break what used to work before.

It’s easy enough for developers to shift to using the abstraction layers – I for one am looking forward to it – but WooCommerce wasn’t built for me. It was built for the people I (and an army of others) build things for, and it was built using post meta.

By all means, improve your code, but keep in mind: sometimes you gotta dance with the one that brung yuh.

“Chain Links” image by Danny Hope, used under Creative Commons.