Dependencies, teams, and microservices
The Candidate LeSS trainers mailing list is where passionate people have interesting discussions. This post is a result of one of those discussions.
An assumption in the agile community is that scaling agile is mainly about managing and reducing dependencies, either between teams or with others outside of a product group. This necessitates additional structures and roles, such as architecture owner, program/project manager, integration, or system team. These groups will manage dependencies and have an overall picture.
An alternative is presented: a microservice-driven team organization. Each team works relatively independently from other teams and owns a set of microservices. Well, let's see.
Just like embracing instead of managing changes (one of the Agile principles), this post explains why we should embrace dependencies instead of trying to avoid or "manage" them.
Therefore, LeSS doesn't have roles, structures, or architectural solutions to manage or reduce dependencies.
Of course, dependencies do not magically cease to exist in LeSS. There is something else going on. Let's examine common types of dependencies.
Dependencies between Product Backlog items
A customer problem is split into Product Backlog items. This act of refinement can introduce dependencies, especially when split improperly. In other words, from an architectural point of view, a larger item from the customer becomes a change in the front end, a change in the back end, and a change in connectivity.
On the other hand, splitting from a customer point of view reduces dependencies between constituent parts.
Nevertheless, no matter how much we try, items in the product backlog are still often interrelated. In fact, this dependency is a good thing. It also says something about coherence as we continuously deliver a single product.
Dependencies in design
At some point, items are small enough to be delivered comfortably by a single team in a single sprint. It sounds as if dependencies are resolved. Each team can deliver one or multiple items independently from other teams.
Well..., not really. There is also something called coherent design or architecture. Having each team independently design its solution is not a good idea unless it concerns localized changes.
Therefore, again, we want our design to be consistent. Reducing dependencies in design activities is not a good idea. Coordination or shared design activities with multiple teams should be encouraged.
Dependency in code
A coder in one team makes changes in the same place as a coder in another team. We used to deal with this by having branches to prevent conflicts in code. We have also learned that this "conflict prevention" makes problems larger later when we need to merge.
Also, cycle time is not going to look good. Every change needs to be followed up by a possible merge, checking, fixing, testing, etc...
Instead, we favor "trunk-based development." All teams, regardless of the number of teams, commit code into the same trunk and integrate continuously. Therefore, conflicts are not avoided anymore but are resolved immediately. Coders talk to each other as soon as a conflict occurs. Since each conflict is limited in size, resolution is relatively easy. It even triggers useful cross-team coordination.
In other words, we want teams to coordinate as much as possible to continuously integrate their work into a single, potentially shippable increment.
But what about microservices?
Aren't microservices solving the dependencies problem?
First, we need to mention a misconception about teams and microservices. The idea of each team owning (set of) microservices is NOT driven by microservices architecture but by a bounded context in the customer domain. The team focuses on a specific part of the customer domain that entails several microservices. There is a clear segregation through interfaces with other bounded contexts. Therefore, one team and nobody else will own and change specific microservices. A big assumption is that there is no shared code, shared services, or minimal sharing.
Therefore, a difference in the feature team concept is shared ownership of microservices. Sharing is encouraged in feature teams and discouraged in microservices-based teams organization.
A customer wants a product that fulfills a need. A customer doesn't want a microservice. Microservices could be a nice architectural approach, but we are mistakenly equating individual parts (microservices) to the combination of parts (a set of microservices that provide together value to a customer). A customer request will cross the boundaries of one group of microservices, regardless of how well we design those groups. The question is, how do we act in such a situation.
"We have resolved this by a better segregation. Our features don't cross bounded contexts"
No, you most probably started duplicating code and data to avoid coupling between services and creating a big mess in the process. We already need to deal with too many solutions originally defined by a simplistic architecture. Let's not go that way.
Since delivering a feature impacts groups of microservices, multiple teams will be required to deliver it. In such a situation, the speed of delivery of a single microservice is irrelevant if one needs to wait for other services to be changed anyway.
Second, in large products / in time, microservices and their contexts have a relatively stable structure, while this can hardly be said for business needs / new requirements. Changing needs will start to mismatch the structure of microservices. Also, some areas (groups of microservices) require no change. What do we do with teams that focus only on their own group of microservices?
The main argument against a microservices-based organizational structure is that teams that are not bounded by microservices can get stuff completely done and also limit themselves to a group of microservices for better focus if they want to. At the same time, a microservice-bounded team is limited in its capability to deliver. One has the flexibility of both, while the other does not.
Sam Newman, in his book "Building Microservices," proposes several solutions as an alternative to what he calls the "shared services" model (multiple teams working on a service). These are not literal statements, but summarized by me:
Microservice teams “move on to something else” until their dependency on other microservices is resolved.
This reasoning not only increases the overall time to deliver but also kills the idea of working on the most important. In other words, the team invents its own priority.
Shift people between teams in order to speed up development in the bottleneck.
This is a bad suggestion since it not only barely solves anything short-term, but it also messes up team organization, stability, and the way of working.
Split request that affects multiple microservices even further until each of them touches only one microservice.
This implies that parts become technical, therefore not meaningful anymore to the customer. Besides, what would you rather have: One team making sure tiny parts are working together or many teams making sure tiny parts are working together.
Internal Open Source: If above is insufficient or unwanted, then we let teams change any service they need to change where code custodians make sure things are done properly.
In other words, teams are no longer bound by a single group of microservices and have started sharing ownership. 🙂
Nevertheless, if a microservice is truly a business capability, which is not just a technical component, it could be okay for the team to specialize in one or a limited set of microservices. This is not driven by the idea of microservices but by customer-domain specialization that happens to correlate with microservices. Therefore, it is not a dichotomy of having microservice or feature (cross-component) teams. It could mean the same since a feature team doesn't deal with all components, especially when the product is large.
In LeSS, we like dependencies….euhm, coordination.
As each type concludes, dependencies are not a problem. In fact, we want and need teams to share work and coordinate since that improves our products. This behavior encourages teams to focus on the whole product and deliver fully integrated products instead of only parts.
At the same time, the real challenge is the ways we coordinate. Postponed or asynchronous coordination through intermediate roles, groups, and fixed structures causes many problems, driving a need for managing or resolving dependencies, while direct, free-flowing, and immediate coordination has the opposite effect.
LeSS strongly encourages direct cross-team collaboration to create an integrated product without any indirection between teams. This goal drives focus and exposes the potential for improvement. In addition to just talking to each other and several other coordination practices, many meetings are shared between teams:
- Sprint planning One (teams or team representatives with Product Owner)
- Multi-team Sprint Planning Two (teams discuss and design similar or related features)
- Overall Product Backlog Refinement (team representatives with Product Owner)
- Multi-team Product Backlog Refinement (teams or few representatives, subject matter experts)
- One Sprint Review for all teams
- Overall Retrospective
This supports cross-team learning, standardization, cohesive product delivery, and shared work coordination. Therefore, we don't call this dependencies since it is something welcomed.
You can find more about this in the book Large-Scale Scrum by Bas Vodde and Craig Larman.