Why This Is Hard
When your QA environment is backed by a production database you cannot:- Spin up an isolated database per test run
- Create unlimited disposable users
- Drop tables or truncate data as cleanup
The Core Contract
Every test must follow four rules:Create its own data
Tests create the entities they need, prefixed with
e2e-<uuid> for identification.Mutate only what it created
Never edit or delete baseline entities like shared workspaces, users, or billing settings.
Schedule a safety net
Use Stably Scheduler to sweep stale
e2e-* records nightly.Recommended Architecture
Shared Accounts = Identity Only
Reuse pre-created accounts for sign-in. Never modify account settings in parallel tests.
Data Is Test-Owned
Every entity a test creates is namespaced with
e2e-<uuid>, making it safe to delete.Cleanup Is Deterministic
Delete by stored IDs in
afterEach, not broad filters that could match real data.Scheduler Sweeps Stragglers
A nightly Stably schedule runs a cleanup project that removes stale
e2e-* data older than 24 hours.Playwright Implementation
1. Reuse auth state for shared accounts
Create a setup project that logs in once and saves auth state. This avoids repeated login flows and keeps shared accounts as read-only identity providers.auth/setup-auth.ts
2. Namespace + cleanup tracker fixture
This fixture gives every test a collision-resistant namespace and tracks cleanup functions that run automatically after each test.tests/fixtures/test-data.ts
UUID in the namespace prevents collisions even when Stably Cloud scales your tests to many parallel tests. ULID is also a good option if you want sortable IDs.
3. Use shared workspace, mutate only child data
The shared workspace acts as a read-only container. Tests create child resources inside it with thee2e- prefix and only ever touch those.
tests/e2e/projects.spec.ts
4. Serialize shared-state mutation tests
If a test must change workspace-level settings (billing, members, global toggles), isolate it in a serial suite so it never runs in parallel with other tests.Running with Stably
Organize with Playwright projects
Separate your E2E tests from your cleanup project so they can be triggered independently.playwright.config.ts
Run on Stably Cloud
With the namespace fixture preventing collisions, you can safely scale parallelism. Start conservative and increase workers as you confirm tests stay collision-free.Schedule cleanup as a safety net
Per-test cleanup should be your primary defense. But tests can crash, workers can be killed, and cleanup code can fail. Add acleanup Playwright project that sweeps stale e2e-* records, and schedule it with Stably.
Cleanup project:
tests/cleanup/stale-e2e-data.spec.ts
stably.yaml:
stably.yaml
autofix: true means if the cleanup test itself breaks (for example, due to an API change), Stably’s fix agent will automatically diagnose and repair it.
You can also create and edit schedules visually from the Stably Scheduler UI.
Monitor with alerts
Configure Slack or email notifications so your team knows immediately if cleanup fails or E2E tests start flaking:stably.yaml
Guardrails
What to always do
What to always do
- Prefix all test-created names with
e2e-<uuid> - Delete by exact IDs stored during the test, never by broad queries
- Keep one manual emergency cleanup command (
npx stably test --project cleanup) - Increase workers gradually after confirming no collisions
- Use Stably environments to manage credentials per environment
What to avoid
What to avoid
- Parallel write tests against the same shared entity
- Reusing mutable fixture records across test files
- Relying only on nightly cleanup (always clean per test first)
- Depending on test execution order
- Hardcoding customer IDs in tests
Rollout Plan
Add the namespace + cleanup tracker fixture
Start with the fixtures above. No existing tests break since the fixture is opt-in.
Convert your flakiest mutable tests
Pick 3-5 tests that create or modify shared data and convert them to the create-and-own pattern.
Add the cleanup project and schedule it
Wire up the
cleanup project in playwright.config.ts and add a nightly schedule in stably.yaml.Serialize shared-state mutation tests
Move tests that must change workspace settings into
test.describe.configure({ mode: 'serial' }).Related Docs
Defining Test Groups
Organize tests into projects for flexible execution
Test Schedulers
Create and manage scheduled test runs
Run Tests on Cloud
Scale test execution with Stably Cloud
Alerts & Notifications
Set up Slack and email alerts for test results