Why I Stopped Using Playwright MCP During Development

A small disclaimer before I start. A certain slice of engineers, online, will open a technical post not with the argument but with “you’re using the term wrong” or “your premise is off.” So let me get the definitions out of the way before the actual point.

First: MCP is a protocol, not a product. This post is specifically about Playwright-style browser-automation MCPs, used during active development with state-mutating operations. Other MCPs — filesystem, database, search — are a separate conversation.

I wrote earlier that “I barely use MCPs, and when I do, it’s Playwright for browser stuff.” Today I dropped even that — at least for the active dev phase.

The reason is simple. For anything that mutates state, I can’t trust it.

It touches the UI on its own and corrupts data. It clicks at the wrong moment. It panics when the DOM shifts. For display checks and screenshots, it’s still useful. For state changes, no.

The breaking point: the AI corrupted some data, then reported:

“The data appears to be broken.”

You broke it.

I yelled at my laptop. I’m not proud.

The AI isn’t malicious, of course. It just operates the UI mechanically without understanding the state machine behind it. Which, for state-mutating dev work, is genuinely dangerous.

”Just isolate your environment” — addressing that first

Yes, snapshotting the dev DB or running a dedicated sandbox would prevent this. I tried it.

The problem is that in early development, the schema itself changes daily. Maintaining the seed data and fixtures needed for snapshot restore quickly cost more than just hitting the API with curl.

“Cost of hardening the environment” vs “cost of using curl” — at my current phase, curl won by a wide margin. That’s all this is.

”Maybe you’re just bad at Playwright” — addressing that too

Fair point. With data-testid attributes, role-based selectors, and proper waitForResponse calls, Playwright is plenty stable.

But in early development, the UI changes daily. Sprinkling testids across a moving target is wasted work — far cheaper to do it once the UI settles.

So the real lesson isn’t “Playwright is bad.” It’s that letting an AI drive state changes through a UI that isn’t stabilized yet is the wrong layer to automate.

Why curl is acceptable when Playwright isn’t

“But if you give the AI curl with DELETE, it can wreck data the same way.” Correct. The underlying risk is identical.

What’s different is traceability:

A browser click means different things depending on DOM state. The same “click submit” instruction can fire different actions. The issue isn’t the volume of risk — it’s the auditability of risk.

I separate front and back through an API, so curl covers most of my state-change cases. (Caveat: this only works because of that separation. If you’re on SSR/RSC or SSO redirect flows, curl alone won’t cut it.)

curl -X POST http://localhost:3000/api/user \
  -H "Content-Type: application/json" \
  -d '{"name":"test"}'

What you get:

”Hitting the API directly misses UI-layer bugs” — also true

Form validation, debouncing, optimistic UI, CSRF token embedding — none of these show up at the curl layer. “It works in curl” doesn’t mean it works on screen.

So I split the work:

The thing I stopped doing isn’t “use Playwright.” It’s “let the AI carry state changes end-to-end through the UI.”

Beat the problem with logs instead

Front-end logs ship to a separate log server, so I can trace behavior without letting an AI poke at the browser.

Reading them as separate streams turned out to be much easier than trying to reconstruct the same flow through UI replay.

Conclusion

This isn’t “Playwright MCP is useless.” It’s “letting an AI mutate state through a not-yet-stabilized UI is the wrong layer to automate.”

In the AI era, the urge to automate everything is strong. But what actually matters is deciding what to hold fixed and what to leave variable. In early development, the thing to hold fixed isn’t the UI — it’s the API.