Table of contents
Open Table of contents
The tension in one sentence
Vertical slices are the comfortable story: one team ships a thin, end-to-end increment that is testable and demoable. Staggered layers are the common reality: the UI team, the API team, and the platform team each have their own backlog, velocity, and release train. When those timelines do not line up, someone is asked to accept partial validation—and that is where conflict shows up.
Why cadences diverge
Split cadences are rarely mysterious.
- Different owners and backlogs. The mobile or web squad plans sprints around UX milestones; the integration or platform group plans around contract stability, data migrations, and operational risk.
- Different risk profiles. Shipping a UI change is cheap to roll back; changing a canonical enterprise schema is not.
- Dependency chains that do not respect org charts. The product needs a query parameter before the platform exposes the matching filter semantics. The platform shipped an event nobody consumes yet.
None of that requires bad intent. It does require explicit handling of the gaps.
%%{init: {"flowchart": {"curve": "basis", "padding": 8}, "themeVariables": {"fontFamily": "inherit"}}}%%
flowchart TB
subgraph app [Product_app]
UI[UI]
BFF[App_API_or_BFF]
end
subgraph platform [Enterprise_services]
ES[Platform_APIs]
Data[(Data_stores)]
end
UI --> BFF
BFF --> ES
ES --> Data
The diagram is the easy part. The hard part is the calendar: three teams, three burndowns, three definitions of “done.”
%%{init: {"flowchart": {"curve": "basis", "padding": 8}, "themeVariables": {"fontFamily": "inherit"}}}%%
flowchart LR
subgraph t1 [Team_A_UI]
U1[Milestone_U1]
end
subgraph t2 [Team_B_app_API]
U2[Milestone_U2]
end
subgraph t3 [Team_C_platform]
U3[Milestone_U3]
end
U1 -.->|desired_order| U2
U2 -.->|desired_order| U3
Solid sequencing is rare. More often, U1 lands before U3 exists, or U3 lands while U1 is still in design.
Failure modes when things ship out of order
UI before the API (or before the real contract)
The product adds parameters, screens, and empty states against a future contract. Engineering proves behavior with mocks or a stub. Risk: the team confuses “green in CI” with “safe in production.” QA may correctly refuse to sign off because the integrated path does not exist yet.
Staggered delivery often doubles QA’s calendar for the same feature: one pass when only the client path exists (mocks or partial data), and a second pass when the real API or contract finally lands—plus any regression work in the gap. The gantt below is schematic; the point is two validation windows where a vertical slice would have been one.
%%{init: {"gantt": {"useWidth": 900}, "themeVariables": {"fontFamily": "inherit"}}}%%
gantt
title UI ahead of API — two QA passes for one feature
dateFormat YYYY-MM-DD
axisFormat %b %d
section Delivery
UI in staging :ui, 2026-03-02, 5d
Real API in shared env :api, 2026-03-23, 4d
section QA same feature
Pass A partial mock_or_stub :qa1, 2026-03-02, 5d
Pass B full integration :qa2, after api, 5d
API or platform before the UI
Backend capacity ships ahead of the experience. Nothing is wrong with the service in isolation, but value is stranded until something calls it—and integration tests may not cover the real client.
Here the same two-pass pattern appears with the order flipped: QA can exercise the service or contract early (Postman, contract tests, synthetic clients), then must re-validate the full journey once the UI or app wires the behavior—again, roughly twice the touch for one capability.
%%{init: {"gantt": {"useWidth": 900}, "themeVariables": {"fontFamily": "inherit"}}}%%
gantt
title API ahead of UI — two QA passes for one feature
dateFormat YYYY-MM-DD
axisFormat %b %d
section Delivery
Platform or API live :api, 2026-03-02, 5d
Client UI wired :ui, 2026-03-18, 5d
section QA same feature
Pass A service_or_contract_only :qa1, 2026-03-02, 4d
Pass B end_to_end journey :qa2, after ui, 5d
QA in the middle
Test plans assume a stable environment where the full path works. With staggered delivery, QA is asked to test A while B is still a ticket on another board. Without a named policy, every bug becomes a routing argument: app versus platform versus environment.
PO and acceptance pressure
POs are scored on outcomes customers can use. Partial validation feels like shipping debt. If the team never translates “phase A” into explicit acceptance criteria, POs fall back to binary “feature done”—and engineers hide behind “blocked by platform.”
Full-stack, same-team delivery: the smaller gap
When one team owns UI and API for a product slice, you get:
- One backlog and fewer handoffs.
- Shared mental model of the contract as it evolves.
- Faster correction when the wrong abstraction was chosen on day one.
Treat that as the comparative ideal, not a moral lecture. In large enterprises, platform consolidation and compliance often require a separate services org. Your job is not to win an org-design debate in a blog-shaped box; it is to reduce coupling pain where you sit.
Enterprise reality: name the seam
If data flows through a platform you do not own:
- Name the boundary. Who owns incidents when the contract drifts? Where do tickets land when latency or schema breaks a downstream client?
- Prefer written contracts over hallway agreements. OpenAPI specs, async schemas, or explicit versioning policy beat “we will add that field when we can.”
- Clarify who proposes a change. The app team often discovers the need; the platform team owns the blast radius. Consumer-driven expectations (the app team publishes the shape it needs) reduce thrash.
Playbook: avoid, mitigate, handle
Avoid: slice work so something is end-to-end testable, even if narrow
Prefer a thin vertical slice over a broad horizontal layer. A small feature that hits real wire—even with limited scope—beats a large UI that only ever saw mocks.
%%{init: {"flowchart": {"curve": "basis", "padding": 8}, "themeVariables": {"fontFamily": "inherit"}}}%%
flowchart TB
subgraph thin [Thin_vertical_slice]
v1[One_user_path]
v2[Real_API_call]
v3[One_platform_field]
end
subgraph wide [Wide_horizontal_slice]
h1[Many_UI_screens]
h2[Mocks_only]
end
If you cannot get the full filter set, ship one filter against a real endpoint and grow from there.
Mitigate: stubs, contracts, environments, flags
- Stub servers or contract tests so the client team can pin behavior before the platform is ready—without pretending the stub is production.
- Feature flags (see also release and rollout thinking elsewhere on this site) to separate “merged” from “visible,” so partially integrated code does not become a surprise launch.
- Dedicated integration or staging paths where both sides meet on a known build. “Works on my machine” is not a policy; “works in env X with builds A and B” is.
Handle: make partial validation explicit for PO and QA
Write down, per increment:
- What is validated (which path, which environment, which data).
- What is explicitly not validated yet (which calls still hit mocks or return empty).
- What “done for this phase” means versus “done for the customer promise.”
That turns a stressful ambiguity into a schedule and risk conversation. QA can sign what is signable; POs can align stakeholders on phased value. Engineers stop treating “blocked” as a personality trait.
Honesty as a system property
Partial validation is not shameful; undiscussed partial validation is how trust erodes. The fix is not only technical—it is legibility: who owns what, which environment is the contract of record, and what “green” means at each step.
If you take one line into your next planning meeting, try this: “Here is what we can demonstrate end-to-end this sprint; here is what remains on another team’s timeline; here is how we prevent false confidence in between.” That is the kind of sentence that keeps QAs and POs in the loop without asking them to bless fiction.
Further reading (light touch)
- Team and platform dynamics are a whole literature; for a starting mental model, Team Topologies (Team of Teams style) pairs well with hard platform boundaries.
- For incremental replacement of large systems, Martin Fowler’s Strangler Fig pattern describes how to grow capability without a single big bang.
These are pointers, not prerequisites—the concrete moves here are contracts, thin slices, named environments, and explicit validation phases.