I think tasks matter a lot, I mean, obviously they matter to the extent that you need something to do your work from… But above and beyond that a good task can actually be responsible for avoiding issues and wasting time and I don’t think great tasks are ever not worth it, even for small things or bugs or work that’s more of a technical nature than a User’s requirement.
Doing tasks good is important, it’s a knowledge sharing exercise to both collaboratively craft a task and to pick one up. I think it’s important to focus on what matters (conveying the core requirement) rather than any particular format (trying to sculpt the appropriate riddle). That said, there’s a pattern to what I tend to like to see on most tasks:
- Requirement
- Acceptance Criteria
- Suggested Implementation Detail
I’ve found a lot of benefit in driving team task refinement with the following values and ethoses in place, which I conveniently categorise into this ‘pattern’.
Requirement
There’s a large part of me that feels like if a person of my level and experience is writing “you need to capture the requirement”, there’s something ghastly wrong with them. I don’t mean to express this like it’s a revelation that I’m bestowing on the unknowing masses: rather it’s actually painfully easy to miss the point when writing down what needs to be done, especially (in my experience) if you’re focussing on meeting a particular format of describing a task to fulfil a Definition Of Something rather than capturing what matters. I still do it today, capturing a requirement is hard!
Here’s the format I like to take:
Requirement:
{Write the requirement here}
Seriously. I believe that’s the most flair you need to put into capturing it and the least likely to detract from what matters. That’s not to say that requirements cannot benefit from being crafted with a style in mind (even the loathsome User Story has its place… probably), but it’s not the most important thing to me. Expressing it in a couple of clear and concise sentences should be the goal, so that the people doing the work know what’s driving their implementation.
The other thing I find distracts from the requirement: too many times people mistakenly capture the assumed or agreed implementation. They describe what the software should do exactly, not what problem it needs to solve. While I think there’s value in capturing this, it’s not appropriate to muddy the core requirement.
To me, the requirement is the one core part of a task that is non negotiable. You cannot bargain with someone over what they require from the software. You can tell them you aren’t going to do it, you can deprioritise it as less valuable than other requirements. You can invalidate/void it in favour of fulfilling a different requirement or by working with your customers to figure out better ways of working, but ultimately what you write here should not be something you can tweak and amend as you refine the task (save for adding more clarity or expressing it better). I’d argue that if you have written a requirement that feels negotiable, it’s likely not well understood, or you’ve captured an implementation spec instead of a requirement, or you’re the source of the requirement!
Acceptance Criteria
To define it in terms of itself, I think good acceptance criteria is Criteria that, when met, means we are sufficiently happy to Accept that the work done meets the requirement. It’s not strictly a task list, it’s not a list of every single step, sometimes it’s more of an ethos to capture while working on something like “Under no circumstances should the user flow be interrupted by this work”. A clean list of things that can be examined and objectively met works best. This can be negotiated, and compared to the requirement to assess if the acceptance criteria is good quality.
I am a huge fan of reviewing acceptance criteria during a code review - if you’ve not been too closely involved with a piece of work that has been done, it’s very easy to approve a pull request because it works in terms of itself. The code looks good, the commits describe what they resolve, the task title seems like its been met… But having a list of acceptance criteria to review as I’m looking at the code helps me also review that the work captures some of the key things we wanted to achieve in order to fulfil the requirement. I’ve looked on perfectly good work before only to realise it actually doesn’t do what we want! Sometimes it’s hard to match the criteria to the changes that fulfil it, but in situations like that I’ve often used the criteria to have a quick conversation and ask how a point has been met. Satisfied, I can approve the work and let it proceed down the path to “done”. That use case for me helps drive how I craft these things, If I can’t objectively tick it off either through inspecting something or having a conversation about how the work was done, it’s not good criteria.
The format I like here is:
Acceptance criteria:
- {Some criteria}
- {Some other criteria}
- {Sub-point to this criteria}
- {etc}
Suggested Implementation Details
Perhaps going the other way from points I’ve made earlier, I’ve found it’s valuable to dedicate a little bit of text to implementation detail where it may truly matter - this is the most optional and most negotiable section imo. It shouldn’t be a senior declaring a technical choice for a junior to pick up, it should be a professional adding a suggestion with a reason for it that makes sense in the context of the work. What I don’t mean you should do here is decide for all tasks ahead of time how they will be done. The majority of tasks I’ve written in the past don’t need suggestions here and are probably better off without any, but where it helps, it really helps. If you have design meetings for some features, this would be a useful place to put some output from that.
The format I like here is:
Suggested implementation:
- {Implementation note}
- {Another implementation note}
- {Sub-note to this note}
- {Etc}
Other thoughts
Overall the only other major thing I have to add is that tasks should be sufficiently small enough to reduce risk and allow a keen and precise approach to capturing the requirement, acceptance criteria and implementation detail. Bigger tasks naturally lead to more ambiguity and instability when refining them and heavyweight process also hinders and harms more than it helps in my experience.
Furthermore, examples really help. Capture some written scenarios of real usage, draw a little sequence diagram to illustrate control flow (I’ve had a lot of fun with PlantUML for this in the past but I’m planning to pick up Mermaid to fill that niche going forwards since Github supports it natively these days), paste a little snippet of json to draft what an API request should look like. Do what is valuable and take the opportunity to provide things that feed heavily into collaboration when refining here, but don’t be a gatekeeper decreeing exactly how things will work all the time.
The perfect task to me is one that is written appropriately for the work it describes and while I find most tasks merit these three sections highlighted above, I’m not going to apply it to every single task I am involved with crafting by default. If you find yourself writing information that feels obvious or redundant or pointless, stop and consider what could be more valuable ways of shaping something.
I also encourage avoiding Death By Subtask - making a ton of subtasks or checklists to make sure we methodically tick off every conceivable part of a piece of work. “Subtasks” should be implicitly covered by your base standards for tasks and the acceptance criteria. I feel making them explicit can easily overdo and detract from the elegance of a well-crafted task. My take is that subtasks should be an exception to the norm, and if the norm is “tasks are always so big they need subtasks” - consider splitting them up into separate achievable chunks of value or refining common subtasks into your standards (Definition of Done or whatever).