A workflow dependency is when an item of work must wait for another item of work to be started or completed before it can begin. This means waiting. So, workflow dependencies mean that there is a delay built into the system. Delays lengthen both the feedback loop on the product (e.g. finding out if we are building what we wanted) as well as delivery to market, essentially increasing costs and diminishing ROI.
Examples of dependencies
Let’s examine some scenarios that cause dependencies. Some are good, some bad, some simply unavoidable.
- A new feature requires a vendor to add something to their product. Our feature cannot be released until they finish and deliver their part.
- Team A’s software uses hardware being developed by team B. Team A cannot begin writing their new feature until team B delivers the hardware that could support it.
- A large feature has been split to be developed by multiple teams.
- An epic has been broken down into multiple individual backlog items. These build on each other and must be done sequentially.
Impacts of dependencies
There can be some positive uses from certain kinds of dependencies:
- Breaking epics down into individual backlog items can leave dependencies in order, but has the upside of having the work in manageable and testable chunks to shorten the feedback loop.
- Using a vendor’s product saves time and resources, allowing teams to focus on the company’s core products and value.
- Using multiple teams to swarm on a feature may allow (but doesn’t guarantee, and sometimes hinders) a faster delivery of a given feature.
While there can be positive uses, most dependencies bring a number of negative impacts, which can be subtle but far reaching. Examples include:
- Not all teams have the same prioritization – what is a high priority for team A may be a lower priority for team B, so team A’s highest priority may be stuck waiting on team B.
- Dependencies cause complexity in the backlog. This complexity adds risk, and risk makes accurate forecasting more difficult.
- Dependencies can create an external dependency for bug fixes, which makes meeting service level agreements (SLA) more difficult. This happens because team A owns the feature and is responsible for the SLA, but the bug may reside in team B’s product. Team A must now work with team B to address this issue. This also increases the cost of the bug fix since it involves effort from multiple teams.
- Shifting priorities in the backlog becomes more difficult, since the order of a backlog must always work around these dependencies. Ordering dependencies and commitments to other teams must be kept intact. This limits the ability of the organization to adapt to new information or situations.
- Workflow dependencies increase the project complexity, and the number of post-release defects increases.
In addition to the above impacts, dependencies are often a symptom of other dysfunctions, which may include:
- Component focused teams (rather than cross-functional feature-based teams)
- Specialization of skills (e.g. siloing)
- Communication channels that are too rigid (both intra- and inter-team)
- Improper amount of design
- Too detailed and inflexible – this leads to an inability for individual teams to communicate and work together
- Not enough design – development standards and project boundaries are not defined, so team A must complete their part just so team B can see what the inputs/outputs are
- Tight coupling of projects – similar to improper design, the projects must know how the other project works internally instead of using an agreed upon API, limiting the organization’s ability to work on them separately. An internal change in one project may add work to other projects. For example, a product exposes SQL queries to look up objects instead of having a proper request syntax. If they decide to move to a different database, all users of the project must now update their SQL requests to be requests for the new database. Tight coupling also adds to technical debt.
- Ineffective product backlog transparency and forecasting – finding out too late the team won’t be able to deliver, so other teams were brought in to help deliver faster
This doesn’t mean that all dependencies are wrong (though many are). Dependencies may occur when dealing with vendors or external products or when crossing significant boundaries like hardware and software development. Ordering dependencies when breaking down an epic into smaller backlog items are also acceptable. There can also be times when a decision will affect subsequent backlog items, like creating a prototype to determine which architecture will better serve the needs of the project.
Like technical debt, there are times when using a dependency (like using a third-party service) can enable an organization to move faster and accomplish more. However, also like technical debt, unwise use of dependencies (e.g. locking into a long-term dependency on a third party over which you have no control) can slow down development, cost more money, and lead to a less favorable user experience.
Telling the good dependencies from the bad
Each dependency is different and will need to be evaluated on its own merits (and perils). Here are some guideline questions to use when evaluating a dependency:
- Is this dependency easily avoidable?
- Is this dependency a symptom of a dysfunction?
- How many communication channels are needed to address this dependency?
- What is the benefit that we get from this dependency?
- If a customer finds an issue with the feature as a whole, what will the process look like to address it?
If there are specific benefits to having the dependency, then move forward. If the dependency seems to be covering up other issues, or causes undue complexity without significant benefit, then it is better to find out how to avoid the dependency altogether.
Address existing dependencies
When dependencies are valid, there are still techniques that can minimize any negative impact.
Keep the dependencies in one team
If it is possible to keep the dependent backlog items in one team, this lowers the number of communication channels needed to address changes. This allows dependent backlog items to be modified more quickly.
This comes back to core practice of cross-functional teams. When a team is cross-functional, it is less likely that they will need external help (a dependency) to accomplish the item.
Follow INVEST criteria for creating a backlog item
The acronym INVEST can be useful in determining the quality of a backlog item description. INVEST stands for independent, negotiable, valuable, estimable, small, testable.
Sometimes dependencies exist because of the way stories are written. For example, imagine a team that has a backlog item to add items to a cart and then checkout. They may see the checkout as dependent on having items in the cart. However, a backlog item could be created to bill a user a certain amount. That could be developed (and tested) without a cart. This allows one team to work on the functionality to add items to the cart, and another team to work on doing billing (taking credit card info, running the charge, etc.). Keeping backlog items independent has allowed two teams to work together and minimized the dependency. The final dependent item of billing for the cart could then take place.
The more independent backlog items are, the easier it is for different teams to grab different items without stepping on each other’s’ toes or requiring extra coordination.
Although you might have to complete one backlog item before completing another one (which is often an appropriate dependency), having to start one in order to start another one, requiring both to be in flight in order to accomplish either one, is not a normal or appropriate dependency. This implies a dysfunction in decomposing PBIs.
Keeping stories as loose as possible allows teams to quickly adapt. If every detail is needlessly locked down as a requirement, if a team finds a better way to accomplish the backlog item, they must now track down the product owner, or even outside stakeholders, in order to rewrite the backlog item. Allowing the team to negotiate details makes conversations and decision making faster.
Focusing on a backlog item being valuable tends to produce backlog items that are vertical slices of functionality. Vertical slicing reduces ordering dependencies and shortens the feedback loop, since each backlog item can be tested, reviewed, and used.
If the team can’t estimate the size, it is probably not defined well enough and there may be unknown dependencies lurking within.
Smaller backlog items are less complex. If it’s too large to estimate, it will be harder to know if there are other dependencies in the backlog item.
The key to being able to break down backlog items is to make sure that each item is independently testable. As long as a piece has automated testing written for it to guarantee the functionality is working (be it unit testing, or full integration and system testing), then it can be considered delivered within the sprint. And if a team can guarantee the entirety of the backlog item, that means that there wasn’t a dependency.
Define APIs and boundaries
One major action for addressing dependencies is to make sure APIs and boundaries are well defined. This allows different team members or even different teams to work in parallel, as long as they uphold the API definition. This also allows for easier testing of each piece.
For example, a team is writing a new feature that will allow their e-commerce website to use paging to show a certain number of products at a time. In sprint planning the team can decide what this request will look like (e.g. /products?page=3), and what the result will look like (a JSON list of products). They can then have someone working on the backend to implement the API, someone working on the front-end using that API (using mocked results), and someone can be writing integration tests that will test that API. This can be taken further to define a CSS class that each paged object will have, so frontend tests can be written to make sure that only 50 items are on the page (by searching for objects using that CSS class). This example can be extended across team boundaries as well.
One note on defining APIs: the communication channel for modifying the API must be clearly defined. Sometimes working on the API will bring out details that show the original design is insufficient, and so the team(s) must know how to communicate to modify the API to fit the functionality’s needs.
Abstract the dependencies in the code
Similar to the above two items, if the boundaries are well defined and the item itself is testable, then the code can be written in such a way that the majority of the main codebase is unaware of the details of the code of the dependent backlog item. For example, this can be done with wrapper classes, and using mocking in the unit testing. This means that even if the code or product from the dependent backlog item changes, the change in the main code is only in one class or area instead of crawling through multiple files. This is often called the Dependency Inversion Principle. This tends to lead to more maintainable code.
As mentioned above, many dependencies can be avoided by using common agile practices. These practices help not only to avoid dependencies, but also tend to lead to better quality and improved speed in general.
If teams are not cross functional, then any backlog item that crosses any logical boundary (UI and database, for example) automatically becomes a cross-team dependency. By having cross functional teams that are able to work across an entire vertical slice, the need for any backlog item to extend to multiple teams drops dramatically. Technical dependencies are often a reflection of social dependencies.
T-shaped team members
Like having cross functional teams, having cross-functional, multi-skilled, or T-shaped team members makes it so that no single team member becomes a dependency (i.e. single point of failure) for the rest of the team. This allows the entire team to be able to move to work on whatever is the highest priority for the team.
No team really starts off this way. Team members must continually watch for opportunities to expand their skill set. This can include taking tasks in areas that they are unfamiliar with, pair programming, or shadowing. Sprints should accommodate and plan for these activities.
By watching for which backlog items have external dependencies in backlog refinement, the team can begin working with any other needed teams to communicate and refine designs, APIs, and other choices that need to be coordinated. These conversations may include design meetings, coordinating with other product owners so that dependencies will be completed in a timely manner, or even just defining how the teams want to work with each other (for example, holding a scrum of scrums meeting—an inter-team daily scrum during key times). Beginning this communication process before the item is to be worked on allows time for the proper discussions to happen and not hold up work.
Many who misunderstand agile techniques believe they are anti-design. This is not true. Agile values and principles discourage over-design (i.e. designing details before you have the needed information to make those decisions). Avoiding over-design also allows teams to make design decisions when they are more informed. Appropriate prior design includes defining general functionality outcomes and underlying architectural decisions in order to move forward. Specific designs should emerge according to what is learned and when it is needed.
Like product backlog items, the further out the need for a design, the less detail it requires now. The closer a design comes to being implemented, the more detailed it should become. Product backlog refinement is the process of identifying what items need additional design before they can be considered ready for development, and progressively elaborating those designs as need and priority increase.
Managing dependencies is good for all
Dependencies, when managed correctly, can allow multiple teams to work together to get a product out earlier. Mismanaged dependencies can cause technical debt, slower bug resolutions, and delay product releases. It is safest to minimize dependencies unless the benefits clearly outweigh the costs and the plan to address those costs is clear.
If dependencies are slowing you down and limiting your ROI, contact us today.