A follow-up to “The Test Pyramid — Reimagined.” Start with the opener if you missed it.
Every testing model in popular circulation — the pyramid, the trophy, the honeycomb, the diamond, the four-layer version I just spent six posts on — has one load-bearing assumption that almost nobody states. Developers and testers actually talk to each other. Not “report bugs to each other.” Talk. Read each other’s tests. Sit together for thirty minutes on a tricky feature. Disagree in PR review and resolve it on the merits.
Take that assumption away and every model in the list collapses into the same shape: an inverted pyramid, a bloated top, a thin and untrusted bottom, two teams pointing at each other every time a release wobbles. The diagram on the whiteboard doesn’t change. The thing the diagram is supposed to describe stops existing.
So this post is about the conversation. And because the conversation almost always stalls in the same place, I’m going to address most of it to the tester. If you’re a developer reading along, there’s a section near the end for you, but the larger move I’m asking for here belongs to the testing side of the room.
Here’s why.
Why the tester has to go first
There’s a structural asymmetry in the dev-tester relationship that nobody likes to name. The developer writes code. The tester finds what’s wrong with it. The tester’s job output is, at the surface level, the things the developer missed. I wrote a whole post about this awkwardness and I won’t rehash it here, but the consequence for communication is the part that matters now.
When a bug ships, the natural reaction on the testing side is the one I described in the opener: I clearly can’t trust the unit tests, so I’d better cover this myself in Selenium. That move is understandable. It is also the engine that builds the inverted pyramid. Every time a tester reaches for a system test instead of going downstairs and asking “could a unit test have caught this?”, a piece of the trust seam between the two teams gets cut. After enough cuts, the seam is gone. The tester has their suite, the developer has theirs, and neither side is reading the other’s.
The asymmetry is this: the developer can ignore the tester’s tests and still ship a feature. The tester cannot do their job without engaging with the developer’s tests. So if trust has to be rebuilt, the tester is the one who has to move first. Not because it’s fair. Because it’s the only direction the move actually exists in.
That’s the bad news. The good news is that the move isn’t hard. It’s a handful of habits, none of them heroic, all of them learnable inside a sprint.
Read the unit tests
This is the single biggest one and it’s the one most testers skip.
If your developers write unit tests and you have never opened the file they live in, you are testing in the dark. You don’t know what they cover. You don’t know what they assume. You don’t know which boundaries they stubbed and which they exercised honestly. You are, in practice, treating the unit-test layer as a black box that either passes or fails, and behaving as if a green build tells you nothing about the actual coverage underneath.
Open the file. Read the tests. You don’t have to understand every assertion the first time — that comes with reps. What you’re looking for is the shape of the coverage. What’s there? What isn’t? Which assertions feel load-bearing and which feel like they were written to make the CI green?
The first time you sit down with a developer and say “I read your unit tests for the payment service and I think the refund path isn’t covered — am I reading that right?”, the entire relationship changes. You stop being the person at the end of the pipe who finds the bugs that escape and become the person who understands the whole pipe. That’s the role the model needs you in.
Two practical notes:
- You don’t need to be fluent in the language. A senior SDET reading a Java unit test for the first time will be slower than the dev who wrote it, but they will still catch missing branches, missing edge cases, weird stubs, and over-broad mocks. The pattern-matching skill transfers. Use it.
- Read in PR review, not after the fact. Once the PR ships, your feedback costs a follow-up ticket. In PR review, it costs ten minutes and a comment. Same input, different cost.
Pair on integration tests, not just system tests
Integration tests are the layer most likely to get short-changed in a real codebase, and they’re the layer where dev-tester collaboration has the highest leverage. The dev knows the internals, the tester knows the failure modes — both perspectives are needed and neither is sufficient.
The habit: when a new feature lands at the integration layer, sit down with the dev for thirty minutes. Walk the boundaries together. What’s stubbed? What’s real? What’s the failure mode we’re worried about? Is there a test for the auth-fails case? The downstream-returns-500 case? The race condition you remember from two quarters ago that bit a different team?
This conversation is the most efficient testing investment you will ever make. You will catch more bugs in those thirty minutes than in a week of writing system tests against the deployed version of the same feature. And you will have demonstrated, in a way that nothing else demonstrates, that you are engaged with the dev’s work — not just the dev’s bugs.
If your team’s culture currently makes this conversation feel weird, that’s the culture you have to push on. It shouldn’t feel weird. It should feel like the most ordinary part of the week.
When a bug ships, ask the layer question first
The bug ships. The instinct, the trained-in reflex, is to ask: what system test would have caught this?
Stop. Ask the layer question first: what is the lowest layer that could have caught this bug?
The honest answer is almost never “system test.” Most of the time it’s “a unit test that doesn’t exist yet” or “an integration test that stubbed the boundary too aggressively.” Sometimes it’s “this bug wasn’t catchable at any layer; the only thing that would have caught it is a different design.”
When the answer is “a lower-layer test,” go find the dev who owns that code and say so. “This bug shipped. I think the lowest layer that could have caught it is a unit test on the validator. Want to pair on writing it?” Then close the bug with that test, at that layer.
The thing this habit does, over time, is reverse the inverted-pyramid gravity. Every time you push a test down instead of up, the suite gets faster, the signal gets earlier, and — critically — the developer learns that their layer is the one you trust to do the catching. You can’t tell them that. You can only show them by where you keep choosing to put the tests.
Push back in code review, but bring receipts
You’re going to see bad unit tests. Unit tests that mock out the thing they were supposed to be testing. Unit tests that assert on incidental implementation details instead of behavior. Unit tests that pass because they were written to pass, not to fail when something breaks.
You should push back on those in code review. That’s not optional and it’s not rude. It’s the same act a senior dev performs when they review a junior dev’s code. The difference, and the part you have to manage carefully, is that you are not the author of the code under test. Your pushback has to be specific, concrete, and useful — not vague suspicion.
Bad: “I don’t think this test really covers the case.”
Better: “This test stubs the repository, so it’ll pass even if findByEmail returns the wrong record. What if we test against an in-memory repo instead, so the lookup is real?”
The first version is a vibe. The second is a code review comment a developer can act on. Same critique, different cost to the relationship. The skill of giving the second kind of feedback is learnable, and it is the single highest-leverage communication skill an SDET can develop.
A side benefit: once you’ve left a few of those comments and the dev has acted on them, your future comments carry more weight. Trust accrues. The seam closes a little.
Delete tests publicly
The other half of the pruning-rewards-pruner rule from the system-tests post. When you delete a system test that didn’t need to exist, tell the team you did it. Not as a victory lap — as a piece of information they need.
The Slack message looks like this: “I just deleted the OrderHistoryE2E.checkoutFlowWithCoupon system test. The same path is covered by an integration test in orders-service and a unit test in coupon-validator, both of which I verified pass. We were running the system version on every deploy and it was adding three minutes for no additional signal.”
What that message does, beyond the deletion itself, is teach the team a pattern. The pattern is: tests below the line are the canonical version, tests above the line are the fallback, and the SDET is the one auditing the relationship. The next time a dev is writing an integration test, they will write it slightly more carefully, because they know the system test that would otherwise have to exist is being held to a standard.
You don’t get that effect from silent deletions. Make the deletions visible.
Translate when a bug crosses layers
A weird thing happens when a tester has been at a company long enough: they accumulate a mental map of which bugs live at which layers in this codebase. Auth failures are usually a config issue, not a code issue. Pagination bugs are usually in the cursor encoding. The flake in the orders system test is actually upstream in the address service.
This map is one of the most valuable artifacts in the engineering organization, and it lives entirely inside the tester’s head until somebody asks. Make a habit of writing it down — in tickets, in PR comments, in incident reviews. When a bug crosses layers — when something looks like a UI bug but is actually a data bug, or vice versa — you are the one most likely to spot it, and the one whose narration of the trace will save the most time.
The phrase to internalize: “I’ve seen this shape before.” Then explain where, when, and what it actually was. Every time you do that publicly, you make the dev-tester seam thinner.
Now let me talk to the devs
This post is mostly to the tester, but the conversation is two-sided, and there are a few specific asks worth making explicit to the developer side of the room.
- Treat the unit tests as load-bearing, not decorative. A unit test that exists to make the CI green is worse than no test at all — it’s a lie dressed up as a safety net. If you wouldn’t trust the test to catch a real regression, don’t write it. (And if you did write it once, you can delete it. That’s allowed.)
- Take SDET code-review comments like you’d take a senior dev’s. The SDET reviewing your test code is not requesting clarification. They are giving you a code review. Treat the comments accordingly. If you disagree, disagree — but disagree on the merits, not on the role of the commenter.
- Pair with the SDET on the integration layer. This is the thirty-minute conversation from earlier in this post. If your SDET hasn’t asked for it yet, ask them. They will be surprised, and then thrilled, and then you will both ship better code together.
- Don’t say “QA will catch it.” This phrase, more than any other, is what built the inverted pyramid in your org. The first time you hear yourself about to say it, swap in “let’s catch this here.”
That’s the ask. Five lines. It’s not a lot. Most of the work is on the other side of the seam.
The handoff death-spiral and how to refuse it
There’s a failure mode every dev-tester relationship eventually hits if neither side resists it. Think of it as a handoff death-spiral — it goes like this:
- The dev writes the feature and the unit tests and considers themselves done.
- The SDET writes the system tests and finds bugs.
- The dev fixes the bugs and writes no new unit tests, because the SDET’s system tests are now “covering” that case.
- The SDET, noticing this, writes more system tests to be safe.
- The dev, noticing this, writes fewer unit tests, because the SDET is so thorough.
- The system suite balloons. The unit suite atrophies. The release cadence collapses. The team blames each other.
The thing nobody on either side notices is that the spiral has a direction. Each side is responding to the other’s behavior in a way that makes the next iteration worse. It is structurally inevitable unless someone refuses to play.
The refusal, on the tester side, looks like this: “I’m not going to write this as a system test. I think it belongs as a unit test on the validator. Can we pair on it?” And then you actually pair on it. The system test never gets written. The unit test does. The spiral breaks for one iteration. Do that twenty times and the spiral has reversed.
The refusal, on the dev side, looks like this: “The bug the SDET found should have been caught by my unit tests and wasn’t. I’m going to add a unit test for it, not wait for the SDET to add a system test.”
Either refusal works. Both refusals together work fast. But somebody has to go first, and given the structural asymmetry I started this post with, that somebody is almost always the tester.
The model is a relationship, not a diagram
The four-layer pyramid is a diagram. It is also, more importantly, a relationship. The diagram is what you draw on the whiteboard. The relationship is what determines whether the diagram describes your team or describes someone else’s.
The relationship is built in the small moments. The PR review comment that didn’t have to be left but was. The thirty-minute pairing session that didn’t have to happen but did. The system test that didn’t have to be deleted but was, with a Slack message explaining why. The bug ticket that doesn’t say “add a system test for this” but says “the unit test for this case is missing — owner?”
None of these are heroic. All of them are habits. And habits compound — the team that builds these habits over a year ends up with a fundamentally different pyramid than the team that doesn’t, even if both teams started from the same diagram and the same tools.
If you’re the SDET, the larger move belongs to you. I know that’s annoying. I’ve been on that side of the asymmetry. But it’s the only direction the move exists in, and the payoff — the pyramid finally working the way it’s drawn, the on-call rotations getting quieter, the dev who used to roll their eyes at your reviews now asking for them — is worth the unfairness of going first.
Go forth – as a team!
Discover more from Go Forth And Test
Subscribe to get the latest posts sent to your email.
Pingback: The Pyramid Isn't Dead, You're Just Not Using It Right