“Nothing is certain, except death, taxes, and two inevitable software engineering antipatterns.”
— Benjamin Franklin
I’ll be honest, I’m only 1% sure that quote is from Benjamin Franklin (actually it might have been Nelson Mandela), but I agree with him 100%. These antipatterns I’m going to talk about are, I assert, 100% inevitable. Knowing that they are inevitable will not protect you from them. They will happen, and best to accept the fact, be on the lookout, be prepared for them, and try to figure out what to do when they strike.
Ok, so this much is certain: these two things will happen to you as a software engineering practitioner on teams of more than trivial size. One, you’ll end up with very long-lived work streams with only one person working on them. And two, you’ll end up with more unique work streams than team members (something especially likely if you have small teams).
Both antipatterns — long-lived single-person works streams and more work streams than team members — stem from a belief in efficiency over effectiveness.
Efficiency is relatively easy to see (at least that’s what we tell ourselves), whereas effectiveness is… well, what, exactly? Effectiveness refers to working on the right thing (in the right way, at the right time, etc.), which we often can’t tell for sure until we see the effects of the work. So one way to frame the problem is to think of efficiency as generally in the present tense, and effectiveness as exclusively in the past tense. “Are we being effective?” is a question we can only answer in the future by considering whether we got where we wanted, and trying to establish a causal relationship between the work and the outcome. Trying to be effective is a matter of having conviction in and trusting in a process, then adjusting if needed after we’ve seen an outcome.
Ok, before that takes you down a YouTube rabbit hole, let’s look at the antipatterns.
Antipattern #1 — long-lived single-person work streams
Many projects and even entire products start off as a spark of an idea in one engineer’s brain. Its a proof-of-concept, or a hackathon project, or just an experiment. Because everyone else is staffed on a different project, this engineer keeps going when the experiment shows promise. Before you know it, the company has decided to productize the experiment and its still only the one engineer working on it.
At the end, it seems like the work has been done pretty efficiently. After all, there was close to zero coordination cost. But things start to fray at the seams once the engineer wants to take some time off, once the product needs 24x7 on-call, once the product does well and its drowning in feature requests, or a one of a myriad of other reasons.
Well, the story obviously doesn’t have a happy ending.
So what do we do?
We ask, regularly and often, whether any project currently staffed with only one engineer is showing promise. (The more general form of this question is to ask whether any project could be practically staffed with more engineers and if yes, why are we not doing it?) At the earliest signs that the project might turn into something “permanent”, we find ways to involve others. “Involvement” could be as lightweight as an additional engineer buddying up for design sessions and code reviews and test strategies, or as heavyweight as bringing on more engineers full time. Broadly, this is about establishing teams rather than individuals as the unit that owns any given work.
(A short aside: also consider another important reason to involve more engineers, which is knowledge and expertise in some specific area — QA, say, or frontend or AI. Yes, most engineers can do anything, but it’s unlikely you have a unicorn engineer on your team who knows everything. Sorry, that’s just math.)
Antipattern #2 — more work streams than team members
Typical deal: we fire up a couple of projects and things are humming along. Something promising shows up on our radar and we go exploring. Cut to a sudden market change that forces us to stop what we were doing and fire up another couple of projects. Some ongoing projects, meanwhile, have reached the “boring” phase of productization, and engineers are looking for more fun things to work on.
Yeah, it doesn’t take much out of the ordinary to end up with multiple work streams that each team member is involved with. More than just the number of projects, we can easily also end up with projects whose staffing looks sort of like this, with engineers belonging to multiple overlapping teams:
This leads to a number of obvious problems from divided attention to unclear priorities to the sheer amount of communication needed, all of which we tend to accept out of inertia (if there’s a better reason, I haven’t found it).
The idea of limiting work in progress is well-trodden ground whether you gravitate towards something more intuitive like the cost of context switching or less intuitive like what queuing theory tells us. But there’s something innate — sure, I’m not a psychologist but it really does feel innate! — that seems to work against the idea in practice. It seems to take inordinate amounts of discipline to move an organization towards this. Which is why, I suppose, organizations that manage to do this become the subject of fascinated study. (I’m looking at you, 1980’s Toyota.)
So what do we do?
Quite simply, we need to prioritize our projects and then actually use those priorities to determine which ones get full attention, ending up at something like this, perhaps:
This type of reorganization does have a significant cost, so it is always better to avoid getting into this situation in the first place, of course. But also keep in mind that not doing it has significant opportunity cost! Much like the effectiveness/efficiency argument, opportunity cost may be harder to see but it is no less real.
Bonus antipattern: long-lived horizontally-focused teams
Specialization is often needed in the early stages of product creation. Canonical examples of this are frontend/backend/database, API/backend, and development/testing. But as Team Topologies so wonderfully lays out, this is not a recipe for long-term success. Rather, by creating what Matthew Skelton and Manuel Pais call stream-aligned teams (and other types from their topologies), teams can find configurations that empirically turn out to be optimal to the job of sustainably delivering customer value.
Caveat
Something to keep in mind: these antipatterns are not “bad” in some objective sense of the word, but they are risky. Any endeavor carries risk, and perhaps these are risks that you’re willing to take under one or another set of circumstances. Just go in with your eyes wide open.
Being effective over the long run is a matter of continuous learning, adaptation, and improvement. There is rarely a right and wrong that holds across different individuals, disparate domains, and even normal changes over time like organizational growth or team composition changes. The antipatterns mentioned here — and others! — will emerge, whether through design or through mere carelessness. Our job is to watch for them and react.
Rome wasn’t built in a day. A good thing too, so I can get paychecks for longer than a day.