Most Playwright problems at senior level are not API problems. They are consequences of the execution model that nobody explained. So start here.
Playwright drives the browser from outside it. Your test code runs in Node.js. The browser runs as a separate process. Commands travel between them over a WebSocket connection using the Chrome DevTools Protocol or an equivalent.
That distance is the opposite of Cypress's choice, and it buys specific things.
No same-origin restriction. Your test is not page code, so the browser's page-level security rules do not apply to it. Cross-origin navigation is unremarkable. SSO redirects just work.
Multiple tabs, multiple contexts, multiple browsers. Your test is outside, so it can hold references to many things at once. This is what makes multi-user testing possible.
Real browser diversity. The protocol is implemented for Chromium, Firefox and WebKit, so the same code drives all three.
It costs latency per command. Each action is a round trip. Playwright hides this well by batching and by doing the waiting inside the browser rather than polling from outside, which is why it still feels fast.
Now the piece that matters more at senior level: the worker model.
Playwright runs tests in worker processes. Each worker is a separate Node.js process with its own browser instance. The default is roughly half your CPU cores.
Three consequences that explain most confusing senior-level failures.
Workers share nothing in memory. A module-level variable set in one test is invisible to a test in another worker. Code that "works locally" with one worker and breaks in CI with four is almost always this. The variable was never shared; you just never ran two tests at once.
Each test gets a fresh browser context. Not a fresh browser, a fresh context, which is the isolation boundary: separate cookies, storage and cache, created in milliseconds. This is why tests start logged out and why they cannot leak into each other through the browser.
Workers are recycled, and sometimes restarted. A worker runs many tests in sequence. If a test crashes the browser, Playwright starts a fresh worker. Worker-scoped state does not survive that, which is worth knowing before you cache something expensive at worker scope and wonder why it occasionally re-computes.
The layered model, from slowest to fastest to create:
Worker process one per parallel slot, holds a browser
Browser expensive, reused across tests in the worker
Context cheap, one per test, the isolation boundary
Page a tab inside a context
Reading that top to bottom explains the whole design: keep the expensive thing (the browser), throw away the cheap thing (the context). Full isolation without paying for a browser launch per test.
What this predicts. You can now answer architecture questions without experimenting.
Can two tests share a database record safely? No, and worker isolation will not save you, because isolation is about the browser and both workers hit the same server.
Can I log in once in beforeAll and have tests be logged in? No. Different context. Use storageState, which is a file, and files cross process boundaries.
Why did adding workers make the suite flakier? Because parallelism exposed a shared-resource dependency that was always there.
The comparison, for interviews. Cypress runs inside the browser and trades freedom for directness. Playwright runs outside and trades directness for freedom. Selenium also runs outside but over a standardised protocol with a driver process in between, which buys enormous browser and language reach at the cost of speed and of any built-in waiting.
Being able to say that cleanly, without advocacy, is a senior signal in itself.
Remember this: out-of-process control buys freedom, the worker model buys speed, and both explain the failures that only appear in parallel.
Reference: Browser contexts.