Most Playwright test suites don’t fail because of bad selectors.
They fail because of bad thinking.
I’ve reviewed dozens of Playwright repositories — startups, enterprises, side projects, even “AI-powered” ones.
And I keep seeing the same architectural mistake repeated again and again.
Let’s talk about it.
The Core Mistake (That Looks Harmless at First)
Treating Playwright tests as scripts instead of a system.
90% of projects look like this 👇
test('user can checkout', async ({ page }) => {
await page.goto('/login');
await page.fill('#email', 'test@test.com');
await page.fill('#password', '123456');
await page.click('#login');await page.click('#product-1');
await page.click('#add-to-cart');
await page.click('#checkout');
await expect(page.locator('.success')).toBeVisible();
});It works.
Until it doesn’t.
Why This Architecture Eventually Explodes
This approach causes silent damage over time:
❌ Symptoms You’ll Recognize
- Tests break when UI changes slightly
- Copy-paste everywhere
- Impossible to scale beyond 50–100 tests
- AI tools can’t reason about your tests
- Flaky behavior appears “random”
- New engineers fear touching the suite
The problem isn’t Playwright.
👉 The problem is that there is no architecture.
How the Top 1% Think Differently
Elite QA engineers don’t think in tests.
They think in:
- Systems
- Capabilities
- Flows
- Contracts
- Boundaries
They ask:
“If this app doubles in complexity, will my tests survive?”
The Missing Layer: Test Architecture
What Most People Build
Tests → Playwright APIs → Browser
What Top Engineers Build
Tests
↓
Test Intent (Business Flow)
↓
Domain Layer (Pages, Actions, State)
↓
Playwright (Implementation Detail)
Playwright becomes a tool, not the center of the universe.
The Blueprint Top 1% Use
1️⃣ Domain Objects (Not Page Objects)
Instead of this 👇
await page.click('#login');They build meaning 👇
await auth.login(user);
class AuthDomain {
constructor(private page: Page) {}async login(user: User) {
await this.page.goto('/login');
await this.page.fill('#email', user.email);
await this.page.fill('#password', user.password);
await this.page.click('#login');
}
}💡 Tests talk in business language, not selectors.
2️⃣ Test Intent Is Explicit
Bad test:
test('test1', async () => { ... });Elite test:
test('Customer can complete checkout with valid card', async () => {
await checkoutFlow.completePurchase(customer, product);
});Now:
- AI can reason about it
- Humans understand it
- Failures tell a story
Psychology Behind This Shift
Junior mindset:
“How do I automate this UI?”
Senior mindset:
“How do I model this system?”
Principal mindset:
“How do I make this evolve safely for 3 years?”
That’s the leap.
Why AI & MCP Fail in Poor Architectures
Everyone wants:
- AI-generated tests
- MCP + Playwright
- Self-healing automation
But AI cannot fix chaos.
If your tests are:
- tightly coupled
- selector-driven
- intent-less
Then AI just generates more mess, faster.
AI thrives when:
- Domain language exists
- Actions are abstracted
- Intent is separated from mechanics
That’s why architecture comes before AI.
A Minimal, Scalable Playwright Architecture
tests/
├─ flows/
│ ├─ checkout.flow.ts
│ └─ login.flow.ts
├─ domains/
│ ├─ auth.domain.ts
│ ├─ cart.domain.ts
│ └─ payment.domain.ts
├─ data/
│ └─ users.ts
└─ specs/
└─ checkout.spec.ts
This structure:
- Scales
- Enables AI reasoning
- Reduces flakiness
- Feels… calm 😌
What the Top 1% Avoid (Deliberately)
- ❌ Direct
page.click()in tests - ❌ Long procedural test scripts
- ❌ “One test = one UI path”
- ❌ Page Objects bloated with logic
- ❌ Treating Playwright as the architecture
What They Optimize For
- 🔁 Reusability
- 🧠 Intent clarity
- 🤖 AI compatibility
- 🧱 Change resilience
- 📈 Long-term velocity
Final Thought
Playwright is powerful.
But power without structure creates fragility.
Tests are not scripts.
They are executable documentation of how your system behaves.
90% automate clicks.
Top 1% design systems.


