In one of my first days of my very first CS class, my classmates and I were asked to "write step-by-step directions on how to change a tire." After we were given some time to work on it individually, the professor (shoutout to Mr. B!) went on to explain that the steps we listed could be considered our "algorithm" for changing a tire which at the time, was a slightly intimidating word. We were told the exercise was designed to demystify the word and was meant as an intro into approaching problems we would run into while programming.
Together as a class, we began to break down the process together. We started listing what we deemed as larger tasks first on a whiteboard with plenty of space between list items, and as we progressed, more details were scrawled in-between these tasks and those tasks themselves became sets of bite-sized smaller tasks. I didn’t know it at the time, but lessons learned from this activity would later be scaled and modified into a process I’d use to break down entire projects many years later.
Note: while this post is applicable to projects of many sizes and requirements, it’s geared towards medium-to-large projects that necessitate some engineering effort in the UI.
Prior to a project making it through the necessary rounds of scrutiny by project stakeholders, the engineer(s) that will be responsible for breaking down tasks should already be involved in the conversation. There’s certainly some wiggle room depending on the size of the project, but especially if the end-goal consists of multiple features and/or potentially complex changes spanning weeks to months of work, it’s important for at least one experienced engineer to be involved early on, even if sparingly, to help smooth out potential pain points or identify large problems with expectations, answer questions, and to ask clarifying questions early on that may identify additional work on the Product or UX/Design side that should be done before task breakdown should begin. In some cases, engineers may need to work through a spike at this stage to determine if and how a particular request will be do-able before committing to the idea.
Frequent communication between UX/Design and Engineering is increasingly important as user interface changes or additions are crafted out. Engineering should scrutinize the design not only from a level-of-effort and security standpoint, but from an accessibility-first mindset. Identifying accessibility issues and recommending accessible design preferences early on is imperative to preventing major a11y issues that could potentially call for design changes and/or refactoring later. In fact, accessibility can impact designs so significantly that in some cases, an outside accessibility expert should be consulted before designs are finalized. If you want your product to be accessible, a11y should never be an afterthought in the design phase and engineers must help uphold these standards.
As designs inch closer to completion (as well as during development), Engineering will likely need to work even closer with Design to account for various application states, some that Design may not be aware of should exist. The more Design can provide upfront, including not just all user experience flows, but more complex flows in mobile, tablet, desktop, and XL desktop viewports to establish design patterns across device sizes, error message expectations, form validation expectations, what every interaction with the UI will look like, and beyond, the more polished the end product will be before maintenance iterations, and the easier it will be to get a full picture of expectations before work breakdown begins.
At this point and depending on team size and the level of effort the project will require, it could be a good idea to share designs with the entire Engineering team to get the team on the same page, encourage additional feedback, and open the floor to fresh eyes to catch anything that’s been missed by those close to the project. Additionally, ensuring that front-end and back-end engineers are on the same page can save headaches later.
While final details are being worked out with Product and/or Design, or prior to depending on when certain requirements become solidified, it’s time to begin ironing out a plan of attack by maximizing the visibility of architectural and other major decisions to the rest of the team.
Architecture Decision Records (ADRs) aren’t for every team or every project. However, in my experience, they’re a fantastic way to weigh pros and cons of various decisions, outline why a specific direction was chosen, and receive and address peer feedback on impactful decisions from engineers that are not necessarily as close to the project at this stage. ADRs can be referenced months and years later when someone inevitably asks why something was done and what alternatives were considered.
Some software projects will reach this stage much more quickly than others because less prework and planning iterations are needed for smaller projects, while others are forced into planning with a greater level of uncertainty due to time restraints. Regardless, it’s time to begin breaking down the work into tasks that any engineer on the team can understand and work on.
Thinking back to the aforementioned tire changing exercise, you’ll start by listing large, sweeping tasks. This could be as crude as beginning with “back-end work” and “front-end work” but as details are added, smaller clusters of work within sweeping categories will become more apparent and clear. As it can be counter-intuitive to start filing issues before the plan is more solidified, I’ve found success in hacking away at breakdowns in a new document or note in small-to-medium projects, but have also graduated to a spreadsheet divided into sections for large projects.
Begin with a single-sentence summary of tasks which will later become the title of new issues. If it feels natural to add descriptions to tasks along the way that can definitely be beneficial in further aiding the full picture of work needed, but be aware that titles and descriptions will continue to evolve as the work continues to be broken down.
As progress is made in listing out tasks, it becomes increasingly important to ask yourself several questions.
1. Can this task be broken down into smaller pieces?
Most software engineering teams use some sort of scale to determine what the level of effort is for tasks. Your team should consider what would typically be considered “too large” to be a single task (on my team, it’s beyond a 5 pointer on the Fibonacci scale) and if during breakdown you would estimate a task as higher than that threshold, consider how it can be broken up further.
There can be rare exceptions to this rule because sometimes it just makes sense to do a larger task in one go, but generally speaking, look for ways to break up distinct pieces of functionality or simply separate building and styling UI components from making them functional/interactive. Tasks should ideally be straightforward and concise.
On the flip side, some very small teams that work together on small isolated projects or tasks can sometimes get away with hacking away at large tasks without taking time to break them down extensively, only to file follow-up issues on what is still remaining after a large chunk of work has been completed and reviewed. While atypical, this sort of impromptu workflow worked well on a team I’ve previously been on where each of us would work on separate, non-complex web pages.
2. Should I make the call on how a task should be done?
One of the most difficult aspects of breaking down large projects can be making this call. When you know there’s a series of tasks that need to be broken down to complete a piece of functionality, but how they’re broken down is dependent upon the approach laid out in the first one or two tickets and the solution isn’t obvious, you have a couple of options.
If there’s a lot of uncertainty with the project or it has potentially changing requirements, the best approach may be to file a placeholder ticket with some initial thoughts and a high estimation to encompass all of the work needed to make the decision and all follow up tasks. Then, as other work is completed and certainly before that ticket is next in line to begin engineering work on, the path and solution will likely be much clearer than it was before any engineering had started.
Another solution could be to make the call, file a ticket to implement what you’re thinking with details (if it seems warranted, a mini ADR write-up could help other engineers understand why you went with this approach), and send that ticket (or a description of the problem and your solution) around to get more sets of eyes on it. If other engineers don’t see any glaring problems and agree with your approach, it should be fairly safe to break down the remaining issues under the assumption this is the solution that will be used. If something changes, tickets can be adjusted later.
If other tasks are not necessarily dependent on the decision outcome of an issue but an outcome must be decided, such as “decide on a state management library” or “decide on our error-handling pattern,” an issue can be filed and can be considered a spike with clear instructions on how to close the ticket (with a comment, implementation, documentation, etc.).
3. Should I ask other engineers their opinion or set up a meeting to help flush out a set of tasks?
The engineer breaking down tasks should be able to rely on the expertise of their colleagues when needed. If you’re not strong in accessibility, it would be ideal to have someone with that domain of expertise help you review designs. If you know that l10n is important in your project but don’t know what the steps are to implement it in your project, consider setting up a meeting with someone that does to explain what you need and to help you break down those issues.
Knowing when to reach out for help can be a struggle for everyone, but not doing so when breaking down large projects can lead to a lot of wasted time - if you don’t know how to change a tire, you can’t break down how to. Your team is a quick ping or meeting away.
Writing detailed descriptions will not only set whomever works on the task up for success, but will help minimize follow up tasks and ensure that other tasks dependent on this work will be unblocked. Descriptions should provide any context needed to understand what the ticket is asking, link to other related issues and link relevant documentation, and be clear on what is needed to close the issue. If the description is pretty lengthy, a TLDR with what’s needed to close the issue above the more detailed description will be appreciated by the pull request reviewer as well as by management when thumbing through what needs doing.
Generally speaking the more descriptive a task is the better, however, depending on the project, you may also not want to be too specific. If requirements are subject to change or one task wasn’t done exactly as expected, you ideally shouldn’t need to update the description of 5 others.
If your team uses Jira, be sure to take advantage of “issue links” such as “relates to,” “blocks,” “split to” etc. This can help with identifying priorities, leave breadcrumbs which can almost serve as project progress documentation, and help engineers find solutions to related problems which may help them with the current one.
If the project being broken down requires infrastructure work before beginning on main tasks, there will almost certainly be a cluster of issues involved in setting the project up (and an ADR or three, did I mention those yet?). Depending on the size of the project and what infrastructure work needs to be done, I’ve found that it can be more efficient to keep entire engineering team involvement more minimal at this point - 2 to 4 engineers seems to be the sweet spot to avoid code conflicts, keep up with incoming code changes, and establish and document patterns early. If you throw 10 engineers on 20 initial setup tasks, parallel work may be difficult to find and there may be too many cooks in the kitchen.
Throughout infrastructure work and into the meat of the project, one important factor often overlooked is documentation, documentation, documentation. It’s imperative to avoid tribal knowledge, to assist in understanding code later, and to minimize the ramp-up time of other engineers joining the project later, but it’s oftentimes skipped in favor of hacking away at another task.
If the culture around closing tickets on your team or project isn’t also updating documentation alongside architecture development (and ensuring this happens at code review time), at least consider filing tickets for needed documentation work and prioritize working on them as soon as possible, before new features stack up. While documentation can quickly become out-of-date, this can be mitigated by prioritizing it and requiring a documentation check when the foundation of a project is laid out and into early changes.
Highest priority tasks may not be as obvious as they may seem at first glance. Oftentimes, tasks are blocked by other tasks because clusters of work have a general order they have to be done in, building on top of each other as the work is completed.
Identifying this pattern and batching issues together based on what tasks unblock others can be a fantastic way to create user stories (or epics, or however you’ve chosen to aggregate tasks under umbrellas) to establish what sections of work can be done by engineering in parallel, allowing for maximum efficiency and minimal conflicts.
Once the tasks are divvied up into sections, those sections of work can then be prioritized by what must be completed first (infrastructure, perhaps authentication), what makes sense to complete next before beginning more isolated work (UI stub out or placeholder pages, routing, shared components that will be used in many sections), and then by feature/section priority set by project stakeholders or whatever cluster of work appears like it will take the longest, and finally by what can be completed towards the end of the project (localization, accessibility bugs, end-to-end tests).
If teams prefer a kanban board approach, or to visualize what a flat priority list would look like, the first few tasks of each top priority section would be at the top of the list. The first 10 tasks may consist of tasks that must be completed first in the user stories for feature A, B, and C.
While this approach may not ship high priority feature A in one sprint, it can ship feature A, B, and C in two sprints while leading to a sense of ownership for engineers working on sets of tasks and avoiding stepping on each other's toes.
Note that sections of work don’t necessarily have to be composed of issues that build on each other. It may make sense to put, for example, development of a common header, footer, and navigation into one section.
Unexpected tickets will inevitably be filed as projects are worked through. These may be quick follow ups, low priority bugs, missed tickets during planning, or needed refactors as patterns continue to emerge that make sense to complete before moving onto another section.
It can be difficult to predict when newly filed tickets will be considered higher priority than what was originally planned to do next, which makes padding timelines important to keep expectations realistic and code quality high.
Breaking down a project or set of features may seem intimidating, but beginning with high-level sections of tasks and working through the plan in an iterative process can do wonders.
Diving in head-first and finding what works for you and your team will only make you grow as an engineer. Seeing projects through fruition is immensely satisfying and I hope the next time the opportunity arises, you'll be inspired to break down that tire-changing process (er, I mean, software engineering work).