Playwright 1.63 adds named test locks for shared resources. They solve the scheduling half of the shared-state problem — the state itself is still yours to isolate.
Every parallel suite has the same handful of tests: the ones that pass alone and fail together. They touch the shared staging account, the global feature flag, the one tenant in the third-party sandbox that cannot be cloned. For years the answers were all homemade — a fixture that hands out accounts, a separate serial project at the end of the run, a mutex file on the CI runner, or the blunt instrument of turning workers down to one.
Playwright 1.63, released on September 4, ships a native answer: test locks. A test declares a named lock, and tests holding the same lock name never run concurrently — across files, workers, and projects — while everything else stays parallel.
settings.spec.tstypescript
import { test, expect } from '@playwright/test';// Shares the one global settings object in staging.test('admin can rotate the marketing banner', { lock: 'global-settings' }, async ({ page }) => {// never runs at the same time as another 'global-settings' test});test.describe('account-wide limits', { lock: 'global-settings' }, () => {// the whole group holds the same lock});
This is a good feature, and it will be overused within the quarter. The release solves a real coordination problem so cleanly that it will be mistaken for a solution to the isolation problem underneath it. So here is the position, early: a lock serializes access to a shared resource; it does not isolate the test. Serialization is the floor, not the fix.
Why locks look like the whole answer
The appeal is obvious. The failure mode everyone knows — two workers mutating the same account at the same moment, assertions reading state the other worker just wrote — disappears the instant both tests hold the same lock. No fixture refactor, no test-data redesign, no negotiating with the team that owns the sandbox. One annotation, and the intermittent failures stop. Compared to the cost of real isolation, which is measured in engineering weeks, a lock is nearly free.
And for a specific class of resource, nearly free is correct. A global account setting, a legacy tenant that cannot be provisioned per worker, a third-party sandbox with one API key — these are genuinely un-clonable, and serializing access to them is the honest engineering answer. That is what the feature is for.
What the lock leaves on the table
Two tests that never run at the same time can still poison each other. The first test sets the banner to variant B and passes. The second test acquires the lock, reads the banner expecting the default, and fails — deterministically, reproducibly, and now with a green-history-shaped hole in the failure data because last week the execution order happened to be kinder. The lock removed the race. It did nothing about the leak.
This is the distinction the series article on parallel execution spends its length on: shared state has two failure modes, contention and leakage, and they need different fixes. Locks address contention — simultaneous access. Leakage — one test’s writes becoming another test’s preconditions — survives serialization perfectly. A dirty resource handed over one holder at a time is still a dirty resource.
Failure mode
Symptom
Lock helps?
Contention
Two workers write the same record simultaneously; intermittent, order-dependent failures
Yes — this is exactly what serialization removes
Leakage
Test depends on state a previous test left behind; deterministic once ordering changes
No — the state still carries over, one holder at a time
Capacity
Shared sandbox rate-limits or falls over under load
Partially — serial holders still consume the same quota
There is also a scope boundary worth knowing before you rely on it: a lock coordinates workers inside a single Playwright run. Two shards executing on two machines are two runs. If your pipeline shards across CI jobs, the lock name is invisible to the other shard, and the shared staging account is back to being contended — silently, because the annotation looks like protection.
The decision rule
The release notes themselves say it plainly: if you can create a user, inbox, tenant, or namespace per test, do that instead. The decision rule that follows, in order of preference:
Isolate if you can. A resource that can be created per test or per worker — an account, a
project, a namespace — should be. Isolated tests run in parallel, carry no ordering
assumptions, and fail in ways that reproduce. This is the endpoint, not the luxury option.
Lock what you cannot isolate. Global settings, legacy tenants, third-party sandboxes with
one credential. Name the lock after the resource, not the feature — ‘global-settings’, never
‘admin-tests’ — so the serialization boundary matches the actual contention boundary.
Reset what you lock. Because the lock does not clean state, every locked test either
restores the resource on exit or starts by asserting nothing about what it inherited. A lock
without a reset strategy is contention solved and leakage scheduled.
Count the serialization. Each locked group is a serial island in a parallel suite. Track
the wall-clock share of locked tests; when it climbs past a few percent of the pipeline, the
locks have become the bottleneck, and the real fix — making the resource isolable — is back on
the table.
Adopting test locks for a shared staging resource
Advantages
One annotation removes contention failures across files, workers, and projects
Serializes only the resource that needs it; the rest of the suite stays parallel
Replaces homemade mutexes and serial end-of-run projects with a supported primitive
Costs
Does nothing about state leakage between successive holders
Invisible across sharded CI jobs — protection ends at the machine boundary
Broad or careless lock names quietly convert a parallel suite into a serial one
Cheap enough to postpone the real isolation work indefinitely
That last con is the one to watch. Locks are cheap enough that the fixture refactor — the one that would let forty tests run truly isolated — now has a permanently acceptable alternative. Six months later the suite has eleven locks, the pipeline has crept from nine minutes to twenty, and nobody can name when the tradeoff was made.
Where this lands
Used as designed, test locks remove the last good excuse for the serial cleanup project at the end of the pipeline, and they retire a generation of hand-rolled coordination hacks. That is real progress, and the implementation — serialize the named resource, parallelize everything else — is the right shape.
Used as a substitute for isolation, they become the next retry flag: a configuration line that makes the suite look stable while the underlying dependency on shared state keeps compounding. The tests pass. The suite slows. The leak waits for the order to change. The resource was the problem, and it still is.
Key takeaways
Key takeaways
Playwright 1.63 test locks serialize access to a named resource across files, workers, and projects — the rest of the suite stays parallel.
Locks fix contention, not leakage: a dirty shared resource handed over one holder at a time is still dirty.
Isolate first, lock what cannot be isolated, and pair every lock with a reset strategy — serialization is not cleanup.
Name locks after the resource, keep them narrow, and track the serial share of your pipeline as a metric someone owns.
Locks coordinate within one run; sharded CI jobs are separate runs and share nothing.
A suite that is stable sequentially but fails under parallelism has not become less reliable — parallelism has simply exposed isolation defects that were always there.
Shared fixtures and mutated global data are the quietest source of flaky suites. Build data per test, make uniqueness a rule, and isolate what each test touches.
Non-deterministic failures are defect reports with bad formatting. A field workflow for reproducing, triaging, and resolving flaky tests before the team stops believing the suite.