These anti-patterns affect how work moves through the team. They create bottlenecks, hide problems, and prevent the steady flow of small changes that continuous delivery requires.
This is the multi-page printable view of this section. Click here to print.
Team Workflow
- 1: Pull Request Review Bottlenecks
- 2: Work Items Too Large
- 3: No Vertical Slicing
- 4: Too Much Work in Progress
- 5: Push-Based Work Assignment
1 - Pull Request Review Bottlenecks
Category: Team Workflow | Quality Impact: High
What This Looks Like
A developer opens a pull request and waits. Hours pass. A day passes. They ping someone in chat. The reviewer is busy with their own work. Eventually, late in the afternoon or the next morning, comments arrive. The author has moved on to something else and has to reload context to respond. Another round of comments. Another wait. The PR finally merges two or three days after it was opened.
Common variations:
- The aging PR queue. The team has five or more open PRs at any given time. Some are days old. Developers start new work while they wait, which creates more PRs, which creates more review load, which slows reviews further.
- The designated reviewer. One or two senior developers review everything. They are overwhelmed. Their review queue is a bottleneck that the rest of the team works around by starting more work while they wait.
- The drive-by approval. Reviews are so slow that the team starts rubber-stamping PRs to unblock each other. The review step exists in name only. Quality drops, but at least things merge.
- The nitpick spiral. Reviewers leave dozens of style comments on formatting, naming, and conventions that could be caught by a linter. Each round triggers another round. A 50-line change accumulates 30 comments across three review cycles.
- The “I’ll get to it” pattern. When asked about a pending review, the answer is always “I’ll look at it after I finish this.” But they never finish “this” because they have their own work, and reviewing someone else’s code is never the priority.
The telltale sign: the team tracks PR age and the average is measured in days, not hours.
Why This Is a Problem
Slow code review is not just an inconvenience. It is a systemic bottleneck that undermines continuous integration, inflates cycle time, and degrades the quality it is supposed to protect.
It blocks continuous integration
Trunk-based development requires integrating to trunk at least once per day. A PR that sits for two days makes daily integration impossible. The branch diverges from trunk while it waits. Other developers make changes to the same files. By the time the review is done, the PR has merge conflicts that require additional work to resolve.
This is a compounding problem. Slow reviews cause longer-lived branches. Longer-lived branches cause larger merge conflicts. Larger merge conflicts make integration painful. Painful integration makes the team dread merging, which makes them delay opening PRs until the work is “complete,” which makes PRs larger, which makes reviews take longer.
In teams where reviews complete within hours, branches rarely live longer than a day. Merge conflicts are rare because changes are small and trunk has not moved far since the branch was created.
It inflates cycle time
Every hour a PR waits for review is an hour added to cycle time. For a story that takes four hours to code, a two-day review wait means the review step dominates the total cycle time. The coding was fast. The pipeline is fast. But the work sits idle for days because a human has not looked at it yet.
This wait time is pure waste. Nothing is happening to the code while it waits. No value is being delivered. The change is done but not integrated, tested in the full pipeline, or deployed. It is inventory sitting on the shelf.
When reviews happen within two hours, the review step nearly disappears from the cycle time measurement. Code flows from development to trunk to production with minimal idle time.
It degrades the review quality it is supposed to protect
Slow reviews produce worse reviews, not better ones. When a reviewer sits down to review a PR that was opened two days ago, they have no context on the author’s thinking. They review the code cold, missing the intent behind decisions. They leave comments that the author already considered and rejected, triggering unnecessary back-and-forth.
Large PRs make this worse. When a review has been delayed, the author often keeps working on the same branch, adding more changes to avoid opening a second PR while the first one waits. What started as a 50-line change becomes a 300-line change. Research consistently shows that reviewer effectiveness drops sharply after 200 lines. Large PRs get superficial reviews - the reviewer skims the diff, leaves a few surface-level comments, and approves because they do not have time to review it thoroughly.
Fast reviews are better reviews. A reviewer who looks at a 50-line change within an hour of it being opened has full context on what the team is working on, can ask the author questions in real time, and can give focused attention to a small, digestible change.
It creates hidden WIP
Every open PR is work in progress. The code is written but not integrated. The developer who authored it has moved on to something new, but their previous work is still “in progress” from the team’s perspective. A team of five with eight open PRs has eight items of hidden WIP that do not appear on the sprint board as “in progress” but consume the same attention.
This hidden WIP interacts badly with explicit WIP. A developer who has one story “in progress” on the board but three PRs waiting for review is actually juggling four streams of work. Each PR that gets comments requires a context switch back to code they wrote days ago. The cognitive overhead is real even if the board does not show it.
Impact on continuous delivery
Continuous delivery requires that every change move from commit to production quickly and predictably. Review bottlenecks create an unpredictable queue between “code complete” and “integrated.” The queue length varies based on reviewer availability, competing priorities, and team habits. Some PRs merge in hours, others take days. This variability makes delivery timelines unpredictable and prevents the steady flow of small changes that CD depends on.
The bottleneck also discourages the small, frequent changes that make CD safe. Developers learn that every PR costs a multi-day wait, so they batch changes into larger PRs to reduce the number of times they pay that cost. Larger PRs are riskier, harder to review, and more likely to cause problems - exactly the opposite of what CD requires.
How to Fix It
Step 1: Measure review turnaround time (Week 1)
You cannot fix what you do not measure. Start tracking two numbers:
- Time to first review: elapsed time from PR opened to first reviewer comment or approval.
- PR age at merge: elapsed time from PR opened to PR merged.
Most teams discover their average is far worse than they assumed. Developers think reviews happen in a few hours. The data shows days.
Step 2: Set a team review SLA (Week 1)
Agree as a team on a review turnaround target. A reasonable starting point:
- Reviews within 2 hours during working hours.
- PR age at merge under 24 hours.
Write this down as a working agreement. Post it on the board. This is not a suggestion - it is a team commitment.
Step 3: Make reviews a first-class activity (Week 2)
The core behavior change: reviewing code is not something you do when you have spare time. It is the highest-priority activity after your current task reaches a natural stopping point.
Concrete practices:
- Check for open PRs before starting new work. When a developer finishes a task or hits a natural pause, their first action is to check for pending reviews, not to pull a new story.
- Auto-assign reviewers. Do not wait for someone to volunteer. Configure your tools to assign a reviewer automatically when a PR is opened.
- Rotate reviewers. Do not let one or two people carry all the review load. Any team member should be able to review any PR. This spreads knowledge and distributes the work.
- Keep PRs small. Target under 200 lines of changed code. Small PRs get reviewed faster and more thoroughly. If a developer says their PR is “too large to split,” that is a work decomposition problem.
Step 4: Consider synchronous review (Week 3+)
The fastest review is one that happens in real time. If async review consistently exceeds the team’s SLA, move toward synchronous alternatives:
| Method | How it works | Review wait time |
|---|---|---|
| Pair programming | Two developers write the code together. Review is continuous. | Zero |
| Over-the-shoulder | Author walks reviewer through the change on a call. | Minutes |
| Rapid async | PR opened, reviewer notified, review within 2 hours. | Under 2 hours |
| Traditional async | PR opened, reviewer gets to it when they can. | Hours to days |
Pair programming eliminates the review bottleneck entirely. The code is reviewed as it is written. There is no PR, no queue, and no wait. For teams that struggle with review latency, pairing is often the most effective solution.
Step 5: Address the objections
| Objection | Response |
|---|---|
| “I can’t drop what I’m doing to review” | You are not dropping everything. You are checking for reviews at natural stopping points: after a commit, after a test passes, after a meeting. Reviews that take 10 minutes should not require “dropping” anything. |
| “Reviews take too long because the PRs are too big” | Then the PRs need to be smaller. A 50-line change takes 5-10 minutes to review. The review is not the bottleneck - the PR size is. |
| “Only senior developers can review this code” | That is a knowledge silo. Rotate reviewers so that everyone builds familiarity with every part of the codebase. Junior developers reviewing senior code is learning. Senior developers reviewing junior code is mentoring. Both are valuable. |
| “We need two reviewers for compliance” | Check whether your compliance framework actually requires two human reviewers, or whether it requires two sets of eyes on the code. Pair programming satisfies most separation-of-duties requirements while eliminating review latency. |
| “We tried faster reviews and quality dropped” | Fast does not mean careless. Automate style checks so reviewers focus on logic, correctness, and design. Small PRs get better reviews than large ones regardless of speed. |
Measuring Progress
| Metric | What to look for |
|---|---|
| Time to first review | Should drop below 2 hours |
| PR age at merge | Should drop below 24 hours |
| Open PR count | Should stay low - ideally fewer than the number of team members |
| PR size (lines changed) | Should trend below 200 lines |
| Review rework cycles | Should stay under 2 rounds per PR |
| Development cycle time | Should decrease as review wait time drops |
Related Content
- Code Review - The practice guide for CD-compatible review techniques
- Trunk-Based Development - Daily integration requires fast reviews
- Push-Based Work Assignment - Push assignment makes reviews feel like a distraction from “my work”
- Too Much Work in Progress - Slow reviews create hidden WIP as PRs queue up
- Work Decomposition - Small PRs start with small stories
2 - Work Items Too Large
Category: Team Workflow | Quality Impact: High
What This Looks Like
A developer picks up a work item on Monday. By Wednesday, they are still working on it. By Friday, it is “almost done.” The following Monday, they are fixing edge cases. The item finally moves to review mid-week - a 300-line pull request that the reviewer does not have time to look at carefully.
Common variations:
- The week-long item. Work items routinely take five or more days. Developers work on a single item for an entire sprint without integrating to trunk. The branch diverges further every day.
- The “it’s really just one thing” item. A ticket titled “Add user profile page” hides a login form, avatar upload, email verification, notification preferences, and password reset. It looks like one feature to the product owner. It is six features to the developer.
- The point-inflated item. The team estimates work at 8 or 13 points. Nobody questions whether an 8-point item should be decomposed. High estimates are treated as a property of the work rather than a signal that the work is too big.
- The “spike that became a feature.” A time-boxed investigation turns into an implementation. The developer keeps going because they have momentum, and the result is a large, unreviewed change that was never planned or decomposed.
- The horizontal slice. Work is split by technical layer: “build the database schema,” “build the API,” “build the UI.” Each item takes days because it spans the entire layer. Nothing is deployable until all three are done.
The telltale sign: look at the team’s cycle time distribution. If work items regularly take five or more days from start to done, the items are too large.
Why This Is a Problem
Large work items are not just slow. They are a compounding force that makes every other part of the delivery process worse.
They prevent daily integration
Trunk-based development requires integrating to trunk at least once per day. A work item that takes a week to complete cannot be integrated daily unless it is decomposed into smaller pieces that are each independently integrable. Most teams with large work items do not decompose them - they work on a branch for the full duration and merge at the end.
This means a week of work is invisible to the rest of the team until it lands as a single large merge. A week of assumptions go untested against the real state of trunk. A week of potential merge conflicts accumulate silently.
When work items are small enough to complete in one to two days, each item is a natural integration point. The developer finishes the item, integrates to trunk, and the change is tested, reviewed, and deployed before the next item begins.
They make estimation meaningless
Large work items hide unknowns. An item estimated at 8 points might take three days or three weeks depending on what the developer discovers along the way. The estimate is a guess wrapped in false precision.
This makes planning unreliable. The team commits to a set of large items, discovers mid-sprint that one of them is twice as big as expected, and scrambles at the end. The retrospective identifies “estimation accuracy” as the problem, but the real problem is that the items were too big to estimate accurately in the first place.
Small work items are inherently more predictable. An item that takes one to two days has a narrow range of uncertainty. Even if the estimate is off, it is off by hours, not weeks. Plans built from small items are more reliable because the variance of each item is small.
They increase rework
A developer working on a large item makes dozens of decisions over several days: architectural choices, naming conventions, error handling approaches, API contracts. These decisions are made in isolation. Nobody sees them until the code review, which happens after all the work is done.
When the reviewer disagrees with a fundamental decision made on day one, the developer has built five days of work on top of it. The rework cost is enormous. They either rewrite large portions of the code or the team accepts a suboptimal decision because the cost of changing it is too high.
With small items, decisions surface quickly. A one-day item produces a small pull request that is reviewed within hours. If the reviewer disagrees with an approach, the cost of changing it is a few hours of work, not a week. Fundamental design problems are caught early, before layers of code are built on top of them.
They hide risk until the end
A large work item carries risk that is invisible until late in its lifecycle. The developer might discover on day four that the chosen approach does not work, that an API they depend on behaves differently than documented, or that the database cannot handle the query pattern they assumed.
When this discovery happens on day four of a five-day item, the options are bad: rush a fix, cut scope, or miss the sprint commitment. The team had no visibility into the risk because the work was a single opaque block on the board.
Small items surface risk early. If the approach does not work, the team discovers it on day one of a one-day item. The cost of changing direction is minimal. The risk is contained to a small unit of work rather than spreading across an entire feature.
Impact on continuous delivery
Continuous delivery is built on small, frequent, low-risk changes flowing through the pipeline. Large work items produce the opposite: infrequent, high-risk changes that batch up in branches and land as large merges.
A team with five developers working on five large items has zero deployable changes for days at a time. Then several large changes land at once, the pipeline is busy for hours, and conflicts between the changes create unexpected failures. This is batch-and-queue delivery wearing agile clothing.
The feedback loop is broken too. A small change deployed to production gives immediate signal: does the change work? Does it affect performance? Do users behave as expected? A large change deployed after a week gives noisy signal: something changed, but which of the fifty modifications caused the issue?
How to Fix It
Step 1: Establish the 2-day rule (Week 1)
Agree as a team: no work item should take longer than two days from start to integrated on trunk.
This is not a velocity target. It is a constraint on item size. If an item cannot be completed in two days, it must be decomposed before it is pulled into the sprint.
Write this as a working agreement and enforce it during planning. When someone estimates an item at more than two days, the response is “how do we split this?” - not “who can do it faster?”
Step 2: Learn vertical slicing (Week 2)
The most common decomposition mistake is horizontal slicing - splitting by technical layer instead of by user-visible behavior. Train the team on vertical slicing:
Horizontal (avoid):
| Work item | Deployable? | Testable end-to-end? |
|---|---|---|
| Build the database schema for orders | No | No |
| Build the API for orders | No | No |
| Build the UI for orders | Only after all three are done | Only after all three are done |
Vertical (prefer):
| Work item | Deployable? | Testable end-to-end? |
|---|---|---|
| User can create a basic order (DB + API + UI) | Yes | Yes |
| User can add a discount to an order | Yes | Yes |
| User can view order history | Yes | Yes |
Each vertical slice cuts through all layers to deliver a thin piece of complete functionality. Each is independently deployable and testable. Each gives feedback before the next slice begins.
Step 3: Use acceptance criteria as a splitting signal (Week 2+)
Count the acceptance criteria on each work item. If an item has more than three to five acceptance criteria, it is probably too big. Each criterion or small group of criteria can become its own item.
Write acceptance criteria in concrete Given-When-Then format. Each scenario is a natural decomposition boundary:
Each scenario can be implemented, integrated, and deployed independently.
Step 4: Decompose during refinement, not during the sprint (Week 3+)
Work items should arrive at planning already decomposed. If the team is splitting items mid-sprint, refinement is not doing its job.
During backlog refinement:
- Product owner presents the feature or outcome.
- Team discusses the scope and writes acceptance criteria.
- If the item has more than three to five criteria, split it immediately.
- Each resulting item is estimated. Any item over two days is split again.
- Items enter the sprint already small enough to flow.
Step 5: Address the objections
| Objection | Response |
|---|---|
| “Splitting creates too many items to manage” | Small items are easier to manage, not harder. They have clear scope, predictable timelines, and simple reviews. The overhead per item should be near zero. If it is not, simplify your process. |
| “Some things can’t be done in two days” | Almost anything can be decomposed further. Database migrations can be done in backward-compatible steps. UI changes can be hidden behind feature flags. The skill is finding the decomposition, not deciding whether one exists. |
| “We’ll lose the big picture if we split too much” | The epic or feature still exists as an organizing concept. Small items are not independent fragments - they are ordered steps toward a defined outcome. Use an epic to track the overall feature and individual items to track the increments. |
| “Product doesn’t want partial features” | Feature flags let you deploy incomplete features without exposing them to users. The code is integrated and tested continuously, but the user-facing feature is toggled on only when all slices are done. |
| “Our estimates are fine, items just take longer than expected” | That is the definition of items being too big. Small items have narrow estimation variance. If a one-day item takes two days, you are off by a day. If a five-day item takes ten, you have lost a sprint. |
Measuring Progress
| Metric | What to look for |
|---|---|
| Item cycle time | Should be two days or less from start to trunk |
| Development cycle time | Should decrease as items get smaller |
| Items completed per week | Should increase even if total output stays the same |
| Integration frequency | Should increase as developers integrate completed items daily |
| Items that exceed the 2-day rule | Track violations and discuss in retrospectives |
| Work in progress | Should decrease as smaller items flow through faster |
Related Content
- Work Decomposition - The practice guide for breaking work into small increments
- Small Batches - Batch size reduction at every level, from stories to commits to deploys
- Too Much Work in Progress - Large items inflate WIP because they occupy a slot for days
- PR Review Bottlenecks - Large items produce large PRs that reviewers avoid
- Trunk-Based Development - Daily integration requires items small enough to finish in a day or two
3 - No Vertical Slicing
Category: Team Workflow | Quality Impact: Medium
What This Looks Like
The team breaks a feature into work items by architectural layer. One item for the database schema. One for the API. One for the frontend. Maybe one for “integration testing” at the end. Each item lives in a different lane or is assigned to a different specialist. Nothing reaches production until the last layer is finished and all the pieces are stitched together.
Common variations:
- Layer-based assignment. “The backend team builds the API, the frontend team builds the UI.” Each team delivers their layer independently. Integration is a separate phase that happens after both teams are “done.”
- The database-first approach. Every feature starts with “build the schema.” Weeks of database work happen before any API or UI exists. The schema is designed for the complete feature rather than for the first thin slice.
- The API-then-UI pattern. The API is built and “tested” in isolation with Postman or curl. The UI is built weeks later against the API. Mismatches between what the API provides and what the UI needs are discovered at the end.
- The “integration sprint.” After the layers are built separately, the team dedicates a sprint to wiring everything together. This sprint always takes longer than planned because the layers were built on different assumptions.
- Technical stories on the board. The backlog contains items like “create database indexes,” “add caching layer,” or “refactor service class.” None of these deliver user-visible value. They are infrastructure work that has been separated from the feature it supports.
The telltale sign: ask “can we deploy this work item to production and have a user see something different?” If the answer is no, the work is sliced horizontally.
Why This Is a Problem
Horizontal slicing feels natural to developers because it matches how they think about the system’s architecture. But it optimizes for how the code is organized, not for how value is delivered. The consequences compound across every dimension of delivery.
Nothing is deployable until everything is done
A horizontal slice delivers no user-visible value on its own. The database schema alone does nothing. The API alone does nothing a user can see. The UI alone has no data to display. Value only emerges when all layers are assembled - and that assembly happens at the end.
This means the team has zero deployable output for the entire duration of the feature build. A feature that takes three sprints to build across layers produces three sprints of work in progress and zero deliverables. The team is busy the entire time, but nothing reaches production.
With vertical slicing, every item is deployable. The first slice might be “user can create a basic order” - thin, but it touches the database, API, and UI. It can be deployed to production behind a feature flag on day two. Feedback starts immediately. The remaining slices build on a working foundation rather than converging on an untested one.
Integration risk accumulates invisibly
When layers are built separately, each team or developer makes assumptions about how their layer will connect to the others. The backend developer assumes the API contract looks a certain way. The frontend developer assumes the response format matches their component design. The database developer assumes the query patterns align with how the API will call them.
These assumptions are untested until integration. The longer the layers are built in isolation, the more assumptions accumulate and the more likely they are to conflict. Integration becomes the riskiest phase of the project - the phase where all the hidden mismatches surface at once.
With vertical slicing, integration happens with every item. The first slice forces the developer to connect all the layers immediately. Assumptions are tested on day one, not month three. Subsequent slices extend a working, integrated system rather than building isolated components that have never talked to each other.
Feedback is delayed until it is expensive to act on
A horizontal approach delays user feedback until the full feature is assembled. If the team builds the wrong thing - misunderstands a requirement, makes a poor UX decision, or solves the wrong problem - they discover it after weeks of work across multiple layers.
At that point, the cost of changing direction is enormous. The database schema, API contracts, and UI components all need to be reworked. The team has already invested heavily in an approach that turns out to be wrong.
Vertical slicing delivers feedback with every increment. The first slice ships a thin version of the feature that real users can see. If the approach is wrong, the team discovers it after a day or two of work, not after a month. The cost of changing direction is the cost of one small item, not the cost of an entire feature.
It creates specialist dependencies and handoff delays
Horizontal slicing naturally leads to specialist assignment: the database expert takes the database item, the API expert takes the API item, the frontend expert takes the frontend item. Each person works in isolation on their layer, and the work items have dependencies between them - the API cannot be built until the schema exists, the UI cannot be built until the API exists.
These dependencies create sequential handoffs. The database work finishes, but the API developer is busy with something else. The API work finishes, but the frontend developer is mid-sprint on a different feature. Each handoff introduces wait time that has nothing to do with the complexity of the work.
Vertical slicing eliminates these dependencies. A single developer (or pair) implements the full slice across all layers. There are no handoffs between layers because one person owns the entire thin slice from database to UI. This also spreads knowledge - developers who work across all layers understand the full system, not just their specialty.
Impact on continuous delivery
Continuous delivery requires a continuous flow of small, independently deployable changes. Horizontal slicing produces the opposite: a batch of interdependent layer changes that can only be deployed together after a separate integration phase.
A team that slices horizontally cannot deploy continuously because there is nothing to deploy until all layers converge. They cannot get production feedback because nothing user-visible exists until the end. They cannot limit risk because the first real test of the integrated system happens after all the work is done.
The pipeline itself becomes less useful. When changes are horizontal slices, the pipeline can only verify that one layer works in isolation - it cannot run meaningful end-to-end tests until all layers exist. The pipeline gives a false green signal (“the API tests pass”) that hides the real question (“does the feature work?”).
How to Fix It
Step 1: Learn to recognize horizontal slices (Week 1)
Before changing how the team slices, build awareness. Review the current sprint board and backlog. For each work item, ask:
- Can a user (or another system) observe the change after this item is deployed?
- Can I write an end-to-end test for this item alone?
- Does this item deliver value without waiting for other items to be completed?
If the answer to any of these is no, the item is likely a horizontal slice. Tag these items and count them. Most teams discover that a majority of their backlog is horizontally sliced.
Step 2: Reslice one feature vertically (Week 2)
Pick one upcoming feature and practice reslicing it. Start with the current horizontal breakdown and convert it:
Before (horizontal):
- Create the database tables for notifications
- Build the notification API endpoints
- Build the notification preferences UI
- Integration testing for notifications
After (vertical):
- User receives an email notification when their order ships (DB + API + email + minimal UI)
- User can view notification history on their profile page
- User can disable email notifications for order updates
- User can choose between email and SMS for shipping notifications
Each vertical slice is independently deployable and testable end-to-end. Each delivers something a user can see. The team gets feedback after item 1 instead of after item 4.
Step 3: Use the deployability test in refinement (Week 3+)
Make the deployability test a standard part of backlog refinement. For every proposed work item, ask: “If this were the only thing we shipped this sprint, would a user notice?”
If not, the item needs reslicing. This single question catches most horizontal slices before they enter the sprint.
Complement this with concrete acceptance criteria in Given-When-Then format. Each scenario should describe observable behavior, not technical implementation:
- Good: “Given a registered user, when they update their email, then a verification link is sent to the new address”
- Bad: “Build the email verification API endpoint”
Step 4: Break the specialist habit (Week 4+)
Horizontal slicing and specialist assignment reinforce each other. As long as “the backend developer does the backend work,” slicing by layer feels natural.
Break this cycle:
- Have developers work full-stack on vertical slices. A developer who implements the entire slice - database, API, and UI - will naturally slice vertically because they own the full delivery.
- Pair a specialist with a generalist. If a developer is uncomfortable with a particular layer, pair them with someone who knows it. This builds cross-layer skills while delivering vertical slices.
- Rotate who works on what. Do not let the same person always take the database items. When anyone can work on any layer, the team stops organizing work by layer.
Step 5: Address the objections
| Objection | Response |
|---|---|
| “Our developers are specialists - they can’t work across layers” | That is a skill gap, not a constraint. Pairing a frontend developer with a backend developer on a vertical slice builds the missing skills while delivering the work. The short-term slowdown produces long-term flexibility. |
| “The database schema needs to be designed holistically” | Design the schema incrementally. Add the columns and tables needed for the first slice. Extend them for the second. This is how trunk-based database evolution works - backward-compatible, incremental changes. Designing the “complete” schema upfront leads to speculative design that changes anyway. |
| “Vertical slices create duplicate work across layers” | They create less total work because integration problems are caught immediately instead of accumulating. The “duplicate” concern usually means the team is building more infrastructure than the current slice requires. Build only what the current slice needs. |
| “Some work is genuinely infrastructure” | True infrastructure work (setting up a new database, provisioning a service) still needs to be connected to a user outcome. “Provision the notification service and send one test notification” is a vertical slice that includes the infrastructure. |
| “Our architecture makes vertical slicing hard” | That is a signal about the architecture. Tightly coupled layers that cannot be changed independently are a deployment risk. Vertical slicing exposes this coupling early, which is better than discovering it during a high-stakes integration phase. |
Measuring Progress
| Metric | What to look for |
|---|---|
| Percentage of work items that are independently deployable | Should increase toward 100% |
| Time from feature start to first production deploy | Should decrease as the first vertical slice ships early |
| Development cycle time | Should decrease as items no longer wait for other layers |
| Integration issues discovered late | Should decrease as integration happens with every slice |
| Integration frequency | Should increase as deployable slices are completed and merged daily |
Related Content
- Work Decomposition - The practice guide for vertical slicing techniques
- Small Batches - Vertical slicing is how you achieve small batch size at the story level
- Work Items Too Large - Horizontal slices are often large because they span an entire layer
- Trunk-Based Development - Vertical slices enable daily integration because each is independently complete
4 - Too Much Work in Progress
Category: Team Workflow | Quality Impact: High
What This Looks Like
Open the team’s board on any given day. Count the items in progress. Now count the team members. If the first number is significantly higher than the second, the team has a WIP problem.
Common variations:
- Everyone on a different story. A team of five has eight or more stories in progress. Nobody is working on the same thing. The board is a wide river of half-finished work.
- Sprint-start explosion. On the first day of the sprint, every developer pulls a story. By mid-sprint, all stories are “in progress” and none are “done.” The last day is a scramble to close anything.
- Individual WIP hoarding. A single developer has three stories assigned: one they’re actively coding, one waiting for review, and one blocked on a question. They count all three as “in progress” and start nothing new - but they also don’t help anyone else finish.
- Hidden WIP. The board shows five items in progress, but each developer is also investigating a production bug, answering questions about a previous story, and prototyping something for next sprint. Unofficial work doesn’t appear on the board but consumes the same attention.
- Expedite as default. Urgent requests arrive mid-sprint. Instead of replacing existing work, they stack on top. WIP grows because nothing is removed when something is added.
The telltale sign: the team is busy all the time but finishes very little. Stories take longer and longer to complete. The sprint ends with a pile of items at 80% done.
Why This Is a Problem
High WIP is not a sign of a productive team. It is a sign of a team that has optimized for starting work instead of finishing it. The consequences compound over time.
It destroys focus and increases context switching
Every item in progress competes for a developer’s attention. A developer working on one story can focus deeply. A developer juggling three stories - one active, one waiting for review, one they need to answer questions about - is constantly switching context. Research consistently shows that each additional concurrent task reduces productive time by 20-40%.
The switching cost is not just time. It is cognitive load. Developers lose their mental model of the code when they switch away, and it takes 15-30 minutes to rebuild it when they switch back. Multiply this across five context switches per day and the team is spending more time reloading context than writing code.
In a low-WIP environment, developers finish one thing before starting the next. Deep focus is the default. Context switching is the exception, not the rule.
It inflates cycle time
Little’s Law is not a suggestion. It is a mathematical relationship: cycle time equals work in progress divided by throughput. If a team’s throughput is roughly constant (and over weeks, it is), the only way to reduce cycle time is to reduce WIP.
A team of five with a throughput of ten stories per sprint and five stories in progress has an average cycle time of half a sprint. The same team with fifteen stories in progress has an average cycle time of 1.5 sprints. The work is not getting done faster because more of it was started. It is getting done slower because all of it is competing for the same capacity.
Long cycle times create their own problems. Feedback is delayed. Requirements go stale. Integration conflicts accumulate. The longer a story sits in progress, the more likely it is to need rework when it finally reaches review or testing.
It hides bottlenecks
When WIP is high, bottlenecks are invisible. If code reviews are slow, a developer just starts another story while they wait. If the test environment is broken, they work on something else. The constraint is never confronted because there is always more work to absorb the slack.
This is comfortable but destructive. The bottleneck does not go away because the team is working around it. It quietly degrades the system. Reviews pile up. Test environments stay broken. The team’s real throughput is constrained by the bottleneck, but nobody feels the pain because they are always busy.
When WIP is limited, bottlenecks become immediately visible. A developer who cannot start new work because the WIP limit is reached has to swarm on something blocked. “I’m idle because my PR has been waiting for review for two hours” is a problem the team can solve. “I just started another story while I wait” hides the same problem indefinitely.
It prevents swarming and collaboration
When every developer has their own work in progress, there is no incentive to help anyone else. Reviewing a teammate’s pull request, pairing on a stuck story, or helping debug a failing test all feel like distractions from “my work.” The result is that every item moves through the pipeline alone, at the pace of a single developer.
Swarming - multiple team members working together to finish the highest-priority item - is impossible when everyone has their own stories to protect. If you ask a developer to drop their current story and help finish someone else’s, you are asking them to fall behind on their own work. The incentive structure is broken.
In a low-WIP environment, finishing the team’s most important item is everyone’s job. When only three items are in progress for a team of five, two people are available to pair, review, or unblock. Collaboration is the natural state, not a special request.
Impact on continuous delivery
Continuous delivery requires a steady flow of small, finished changes moving through the pipeline. High WIP produces the opposite: a large batch of unfinished changes sitting in various stages of completion, blocking each other, accumulating merge conflicts, and stalling in review queues.
A team with fifteen items in progress does not deploy fifteen times as often as a team with one item in progress. They deploy less frequently because nothing is fully done. Each “almost done” story is a small batch that has not yet reached the pipeline. The batch keeps growing until something forces a reckoning - usually the end of the sprint.
The feedback loop breaks too. When changes sit in progress for days, the developer who wrote the code has moved on by the time the review comes back or the test fails. They have to reload context to address feedback, which takes more time, which delays the next change, which increases WIP further. The cycle reinforces itself.
How to Fix It
Step 1: Make WIP visible (Week 1)
Before setting any limits, make the current state impossible to ignore.
- Count every item currently in progress for the team. Include stories, bugs, spikes, and any unofficial work that is consuming attention.
- Write this number on the board. Update it daily.
- Most teams are shocked. A team of five typically discovers 12-20 items in progress once hidden work is included.
Do not try to fix anything yet. The goal is awareness.
Step 2: Set an initial WIP limit (Week 2)
Use the N+2 formula as a starting point, where N is the number of team members actively working on delivery.
| Team size | Starting WIP limit | Why |
|---|---|---|
| 3 developers | 5 items | One per person plus a buffer for blocked items |
| 5 developers | 7 items | Same ratio |
| 8 developers | 10 items | Buffer shrinks proportionally |
Add the limit to the board as a column header or policy: “In Progress (limit: 7).” Agree as a team that when the limit is reached, nobody starts new work.
Step 3: Enforce the limit with swarming (Week 3+)
When the WIP limit is reached and a developer finishes something, they have two options:
- Pull the next highest-priority item if the WIP count is below the limit.
- Swarm on an existing item if the WIP count is at the limit.
Swarming means pairing on a stuck story, reviewing a pull request, writing a test someone needs help with, or resolving a blocker. The key behavior change: “I have nothing to do” is never the right response. “What can I help finish?” is.
Step 4: Lower the limit over time (Monthly)
The initial limit is a starting point. Each month, consider reducing it by one.
| Limit | What it exposes |
|---|---|
| N+2 | Gross overcommitment. Most teams find this is already a significant reduction. |
| N+1 | Slow reviews, environment contention, unclear requirements. Team starts swarming. |
| N | Every person on one thing. Blocked items get immediate attention. |
| Below N | Team is pairing by default. Cycle time drops sharply. |
Each reduction will feel uncomfortable. That discomfort is the point - it exposes constraints in the workflow that were hidden by excess WIP.
Step 5: Address the objections
Expect resistance and prepare for it:
| Objection | Response |
|---|---|
| “I’ll be idle if I can’t start new work” | Idle hands are not the problem. Idle work is. Help finish something instead of starting something new. |
| “Management will see people not typing and think we’re wasting time” | Track cycle time and throughput. When both improve, the data speaks for itself. |
| “We have too many priorities to limit WIP” | Having many priorities is exactly why you need a WIP limit. Without one, nothing gets the focus needed to finish. Everything is “in progress,” nothing is done. |
| “What about urgent production issues?” | Keep one expedite slot. If a production issue arrives, it takes the slot. If the slot is full, the new issue replaces the current one. Expedite is not a way to bypass the limit - it is part of the limit. |
| “Our stories are too big to pair on” | That is a separate problem. See Work Decomposition. Stories should be small enough that anyone can pick them up. |
Measuring Progress
| Metric | What to look for |
|---|---|
| Work in progress | Should stay at or below the team’s limit |
| Development cycle time | Should decrease as WIP drops |
| Stories completed per week | Should stabilize or increase despite starting fewer items |
| Time items spend blocked | Should decrease as the team swarms on blockers |
| Sprint-end scramble | Should disappear as work finishes continuously through the sprint |
Related Content
- Limiting WIP - The practice guide for implementing WIP limits
- Small Batches - Reducing batch size at every level reinforces low WIP
- Work Decomposition - Stories must be small enough to flow through a WIP-limited system
- Push-Based Work Assignment - Push assignment and high WIP are mutually reinforcing anti-patterns
5 - Push-Based Work Assignment
Category: Team Workflow | Quality Impact: High
What This Looks Like
A manager, tech lead, or project manager decides who works on what. Assignments happen during sprint planning, in one-on-ones, or through tickets pre-assigned before the sprint starts. Each team member has “their” stories for the sprint. The assignment is rarely questioned.
Common variations:
- Assignment by specialty. “You’re the database person, so you take the database stories.” Work is routed by perceived expertise rather than team priority.
- Assignment by availability. A manager looks at who is “free” and assigns the next item from the backlog, regardless of what the team needs finished.
- Assignment by seniority. Senior developers get the interesting or high-priority work. Junior developers get what’s left.
- Pre-loaded sprints. Every team member enters the sprint with their work already assigned. The sprint board is fully allocated on day one.
The telltale sign: if you ask a developer “what should you work on next?” and the answer is “I don’t know, I need to ask my manager,” work is being pushed.
Why This Is a Problem
Push-based assignment is one of the most quietly destructive practices a team can have. It undermines nearly every CD practice by breaking the connection between the team and the flow of work. Each of its effects compounds the others.
It reduces quality
Push assignment makes code review feel like a distraction from “my stories.” When every developer has their own assigned work, reviewing someone else’s pull request is time spent not making progress on your own assignment. Reviews sit for hours or days because the reviewer is busy with their own work. The same dynamic discourages pairing: spending an hour helping a colleague means falling behind on your own assignments, so developers don’t offer and don’t ask.
This means fewer eyes on every change. Defects that a second person would catch in minutes survive into production. Knowledge stays siloed because there is no reason to look at code outside your assignment. The team’s collective understanding of the codebase narrows over time.
In a pull system, reviewing code and unblocking teammates are the highest-priority activities because finishing the team’s work is everyone’s work. Reviews happen quickly because they are not competing with “my stories” - they are the work. Pairing happens naturally because anyone might pick up any story, and asking for help is how the team moves its highest-priority item forward.
It increases rework
Push assignment routes work by specialty: “You’re the database person, so you take the database stories.” This creates knowledge silos where only one person understands a part of the system. When the same person always works on the same area, mistakes go unreviewed by anyone with a fresh perspective. Assumptions go unchallenged because the reviewer lacks context to question them.
Misinterpretation of requirements also increases. The assigned developer may not have context on why a story is high priority or what business outcome it serves - they received it as an assignment, not as a problem to solve. When the result doesn’t match what was needed, the story comes back for rework.
In a pull system, anyone might pick up any story, so knowledge spreads across the team. Fresh eyes catch assumptions that a domain expert would miss. Developers who pull a story engage with its priority and purpose because they chose it from the top of the backlog. Rework drops because more perspectives are involved earlier.
It makes delivery timelines unpredictable
Push assignment optimizes for utilization - keeping everyone busy - not for flow - getting things done. Every developer has their own assigned work, so team WIP is the sum of all individual assignments. There is no mechanism to say “we have too much in progress, let’s finish something first.” WIP limits become meaningless when the person assigning work doesn’t see the full picture.
Bottlenecks are invisible because the manager assigns around them instead of surfacing them. If one area of the system is a constraint, the assigner may not notice because they are looking at people, not flow. In a pull system, the bottleneck becomes obvious: work piles up in one column and nobody pulls it because the downstream step is full.
Workloads are uneven because managers cannot perfectly predict how long work will take. Some people finish early and sit idle or start low-priority work, while others are overloaded. Feedback loops are slow because the order of work is decided at sprint planning; if priorities change mid-sprint, the manager must reassign. Throughput becomes erratic - some sprints deliver a lot, others very little, with no clear pattern.
In a pull system, workloads self-balance: whoever finishes first pulls the next item. Bottlenecks are visible. WIP limits actually work because the team collectively decides what to start. The team automatically adapts to priority changes because the next person who finishes simply pulls whatever is now most important.
It removes team ownership
Pull systems create shared ownership of the backlog. The team collectively cares about the priority order because they are collectively responsible for finishing work. Push systems create individual ownership: “that’s not my story.” When a developer finishes their assigned work, they wait for more assignments instead of looking at what the team needs.
This extends beyond task selection. In a push system, developers stop thinking about the team’s goals and start thinking about their own assignments. Swarming - multiple people collaborating to finish the highest-priority item - is impossible when everyone “has their own stuff.” If a story is stuck, the assigned developer struggles alone while teammates work on their own assignments.
The unavailability problem makes this worse. When each person works in isolation on “their” stories, the rest of the team has no context on what that person is doing, how the work is structured, or what decisions have been made. If the assigned person is out sick, on vacation, or leaves the company, nobody can pick up where they left off. The work either stalls until that person returns or another developer starts over - rereading requirements, reverse-engineering half-finished code, and rediscovering decisions that were never shared. In a pull system, the team maintains context on in-progress work because anyone might have pulled it, standups focus on the work rather than individual status, and pairing spreads knowledge continuously. When someone is unavailable, the next person simply picks up the item with enough shared context to continue.
Impact on continuous delivery
Continuous delivery depends on a steady, predictable flow of small changes through the pipeline. Push-based assignment produces the opposite: batch-based assignment at sprint planning, uneven bursts of activity as different developers finish at different times, blocked work sitting idle because the assigned person is busy with something else, and no team-level mechanism for optimizing throughput. You cannot build a continuous flow of work when the assignment model is batch-based and individually scoped.
How to Fix It
Step 1: Order the backlog by priority (Week 1)
Before switching to a pull model, the backlog must have a clear priority order. Without it, developers will not know what to pull next.
- Work with the product owner to stack-rank the backlog. Every item has a unique position - no tied priorities.
- Make the priority visible. The top of the board or backlog is the most important item. There is no ambiguity.
- Agree as a team: when you need work, you pull from the top.
Step 2: Stop pre-assigning work in sprint planning (Week 2)
Change the sprint planning conversation. Instead of “who takes this story,” the team:
- Pulls items from the top of the prioritized backlog into the sprint.
- Discusses each item enough for anyone on the team to start it.
- Leaves all items unassigned.
The sprint begins with a list of prioritized work and no assignments. This will feel uncomfortable for the first sprint.
Step 3: Pull work daily (Week 2+)
At the daily standup (or anytime during the day), a developer who needs work:
- Looks at the sprint board.
- Checks if any in-progress item needs help (swarm first, pull second).
- If nothing needs help and the WIP limit allows, pulls the top unassigned item and assigns themselves.
The developer picks up the highest-priority available item, not the item that matches their specialty. This is intentional - it spreads knowledge, reduces bus factor, and keeps the team focused on priority rather than comfort.
Step 4: Address the discomfort (Weeks 3-4)
Expect these objections and plan for them:
| Objection | Response |
|---|---|
| “But only Sarah knows the payment system” | That is a knowledge silo and a risk. Pairing Sarah with someone else on payment stories fixes the silo while delivering the work. |
| “I assigned work because nobody was pulling it” | If nobody pulls high-priority work, that is a signal: either the team doesn’t understand the priority, the item is poorly defined, or there is a skill gap. Assignment hides the signal instead of addressing it. |
| “Some developers are faster - I need to assign strategically” | Pull systems self-balance. Faster developers pull more items. Slower developers finish fewer but are never overloaded. The team throughput optimizes naturally. |
| “Management expects me to know who’s working on what” | The board shows who is working on what in real time. Pull systems provide more visibility than pre-assignment because assignments are always current, not a stale plan from sprint planning. |
Step 5: Combine with WIP limits (Week 4+)
Pull-based work and WIP limits reinforce each other:
- WIP limits prevent the team from pulling too much work at once.
- Pull-based assignment ensures that when someone finishes, they pull the next priority - not whatever the manager thinks of next.
- Together, they create a system where work flows continuously from backlog to done.
See Limiting WIP for how to set and enforce WIP limits.
What managers do instead
Moving to a pull model does not eliminate the need for leadership. It changes the focus:
| Push model (before) | Pull model (after) |
|---|---|
| Decide who works on what | Ensure the backlog is prioritized and refined |
| Balance workloads manually | Coach the team on swarming and collaboration |
| Track individual assignments | Track flow metrics (cycle time, WIP, throughput) |
| Reassign work when priorities change | Update backlog priority and let the team adapt |
| Manage individual utilization | Remove systemic blockers the team cannot resolve |
Measuring Progress
| Metric | What to look for |
|---|---|
| Percentage of stories pre-assigned at sprint start | Should drop to near zero |
| Work in progress | Should decrease as team focuses on finishing |
| Development cycle time | Should decrease as swarming increases |
| Stories completed per sprint | Should stabilize or increase despite less “busyness” |
| Rework rate | Stories returned for rework or reopened after completion - should decrease |
| Knowledge distribution | Track who works on which parts of the system - should broaden over time |
Related Content
- Limiting WIP - Pull-based work and WIP limits are complementary practices
- Work Decomposition - Pull works best when items are small and well-defined
- Working Agreements - The team’s agreement to pull, not push, should be explicit
- Common Blockers - Push-based assignment contributes to several listed blockers