← Back to ArticlesBest Practices

Your Test Pyramid Is Upside Down — Here's How That Happened and What to Do About It

June 1, 2026Also on Medium ↗

A few years back, I joined a project that had been running for about three years. The team was proud of their test coverage — 87% line coverage, hundreds of Selenium tests, a full regression suite that ran every night. The problem was that regression suite took four and a half hours to complete and failed randomly on roughly 30% of runs. Nobody trusted it. Developers would re-trigger the pipeline twice before they even looked at a failure.

When I mapped out what they actually had, the shape was immediately obvious: a handful of unit tests at the bottom, maybe a dozen integration tests in the middle, and somewhere north of 600 UI tests at the top. A perfect inverted pyramid. Everything that Mike Cohn warned about, materialized in production CI.

This is not a rare situation. I've seen it in startups, in enterprise shops, in teams that have read all the right books. The pyramid inverts gradually, silently, and always for reasons that made sense at the time.

How the Inversion Happens (It's Never Accidental Negligence)

The most common path I see goes like this: the team starts with manual testing. At some point, someone decides to automate the manual regression scripts. Those scripts are all UI-level flows — they mirror what a human tester would click through. So the first automated tests are all E2E. By the time the team decides to add lower-level tests, there are already 200 UI tests and a pipeline that product management sees as the safety net.

Adding unit tests at that point feels redundant. The E2E tests already cover the behavior, right? Why test the same thing twice? This logic is seductive and wrong, but it's not stupid. Without understanding the cost differential, it's a reasonable position.

The second path is frontend-heavy teams using Cypress or Playwright. These tools are genuinely excellent and very approachable. A developer who has never written a test before can have a meaningful Playwright test running in under an hour. The onboarding friction is low. The onboarding friction for writing good unit tests with proper test doubles and well-structured assertions is considerably higher. So teams optimize for what's easy to write, and the pyramid inverts.

The third path is organizational: QA owns testing, engineering owns code. When those are separate responsibilities, engineers write just enough unit tests to clear coverage gates (if those even exist), and QA builds out automation against the UI because that's the interface they know. Neither group is failing at their job. The structure is failing them.

What the Actual Costs Look Like

Let me be concrete about why the inverted pyramid is expensive, beyond the abstract principle.

A typical unit test in a well-maintained suite runs in under 10 milliseconds. A Playwright test against a real browser, including setup and teardown, rarely finishes in under 3 seconds — often 8 to 15 seconds for anything involving navigation and assertions. That's a 1000x difference in execution time. When you have 600 UI tests versus 600 unit tests, you're looking at the difference between a 2-minute suite and a 90-minute suite, conservatively.

Flakiness compounds this. UI tests are flaky by nature — not because the tools are bad, but because they operate at the intersection of your application, the browser engine, network timing, and test infrastructure. Playwright 1.43 has excellent auto-waiting, but it can't compensate for an application that renders data asynchronously in unpredictable ways. A 2% flake rate across 600 tests means roughly 12 false failures per run. At 600 tests, you're re-running constantly.

Debugging cost is the hidden killer. When a unit test fails, the stack trace points you directly at the problem. When an E2E test fails, you're looking at a screenshot, maybe a video, and a vague assertion error. You have to reverse-engineer what the application state was and why it didn't match expectations. I've watched senior engineers spend 45 minutes debugging an E2E failure that turned out to be a 5-line logic bug that would have been instantly obvious in a unit test.

Feedback loop matters too. A unit test suite that runs in 90 seconds gives developers feedback while the code is still in their head. A 4-hour regression suite gives feedback the next morning, if the pipeline didn't time out.

How to Actually Fix It Without Burning the Team Out

The wrong approach is declaring "we need more unit tests" in a sprint planning meeting and hoping it happens. It doesn't. The existing E2E suite still has to run, still has to be maintained, and still generates incidents that take priority. Without structural intervention, nothing changes.

Here's the approach that has actually worked for me.

First, stop the bleeding. Introduce a rule: any new feature must have unit and integration test coverage before UI test coverage is considered. Don't touch the existing suite yet. Just stop it from getting worse. This is politically easier than a big rewrite and immediately changes team habits.

Second, classify the existing E2E tests into three buckets. Bucket one: tests that cover something with no lower-level test equivalent, where the E2E test is genuinely the right level. These stay. Bucket two: tests that duplicate logic already covered at lower levels, written at the UI layer purely out of habit. These get deleted, not migrated — deleted. Bucket three: tests that cover something important but at the wrong level. These get replaced with unit or integration tests before the E2E version is removed.

That classification exercise alone is usually eye-opening. In my experience, 30 to 40 percent of UI tests in an inverted pyramid fall into bucket two. You can delete them without losing any meaningful coverage. Removing 200 tests from a 600-test suite cuts your run time by a third immediately.

Third, invest in the integration layer, which is typically the most neglected. Integration tests — testing service boundaries, repository behavior against a real (containerized) database, API contract tests — sit in the middle of the pyramid for good reason. They're faster than UI tests and more realistic than pure unit tests. Teams that skip this layer end up compensating with more E2E tests, which accelerates the inversion.

Docker Compose makes spinning up a PostgreSQL instance or a Redis cache trivial in CI. With GitHub Actions or GitLab CI, adding a service container to your pipeline is maybe 10 lines of YAML. There's no good reason to avoid integration tests against real infrastructure anymore — the tooling has made it cheap.

Fourth, make the economics visible to the team. Track test execution time per layer, flake rate per layer, and mean-time-to-debug per layer. When a team can see that their E2E layer costs 40 minutes of CI time and their unit layer costs 3 minutes, the conversation about priorities changes. Metrics that matter to the team tend to get improved by the team.

The Pyramid Is a Heuristic, Not a Law

I want to be honest about one thing: the pyramid shape is a useful mental model, not a rigid prescription. For some systems — a content-heavy web app with complex user journeys, or a product where the UI is genuinely the most risky layer — a slightly top-heavy distribution can make sense. I've worked on a booking flow where three critical E2E tests caught more real regressions per quarter than fifty unit tests would have, because the failure modes were all about integration between services, not internal logic.

The problem isn't having E2E tests. The problem is having E2E tests as your primary safety net when they're the most expensive, slowest, and least reliable tool in the toolbox.

If your CI pipeline takes more than 20 minutes to give feedback on a typical PR, and most of that time is UI tests, you almost certainly have an inversion worth addressing. Start by counting. Count the tests at each layer. Count the minutes each layer consumes. The shape of your pyramid will be visible immediately, and it will tell you exactly where to start.


Abdulkadir Akyurt is a QA Engineer and Test Automation Specialist.

Follow me on Medium for more articles · Connect on LinkedIn · Instagram · X / Twitter · Visit abdulkadirakyurt.com

More like this