Аудит data + state integrity
Race conditions, optimistic-update revert, localStorage versioning, browser back/forward, multi-tab sync, retry-storms, stale cache. Самый сложный класс багов — невоспроизводимые «иногда работает».
«Иногда работает» — самые дорогие баги в product. Repro не повторяется, support не может объяснить, dev говорит «у меня нормально». В 90% случаев это state integrity: race condition, stale cache, версия localStorage не сматчилась, multi-tab расходится.
Этот промт — структурированный pass через 10 классов state-багов с конкретными test scenarios.
Stack: {{stack}} Mutations: {{primary_mutations}}
1. 10 классов state-багов
1. Race condition: double-submit
Scenario: user clicks Submit, network slow, clicks again before response. Bug: двойная запись в БД, два confirmation emails, payment charged twice. Test:
- Click Submit
- Immediately click Submit again (or 5x rapidly)
- Verify: только одна запись, button disabled во время request
- Use
AbortControllerдля cancel previous
2. Race condition: optimistic update revert
Scenario: UI shows «saved» immediately, server returns 500. Bug: UI lies — пользователь думает saved, реально нет. Test:
- DevTools → Block API endpoint → user clicks Save
- Verify: UI revert with error message
- State согласован: optimistic value не остаётся при server fail
3. Stale data after mutation
Scenario: user creates item, list не обновляется, refresh показывает реально создан. Bug: cache не invalidated после mutation. Test:
- Create new item
- Verify: appears в list без reload
- Edit item → verify: changes reflected везде где используется
- Delete item → исчезает из всех views
4. localStorage version mismatch
Scenario: user open app — v2 deployed, в localStorage v1 data. Bug: parsing fail, crash, потеря saved state. Test:
- Save value в localStorage в old format
- Deploy new version с different format
- Verify: graceful migration или clean state, не crash
- Pattern:
{version: 2, data: {...}}+ migration handler
5. Browser back/forward state
Scenario: user submit form → success page → back → form empty. Bug: form state lost; или хуже — re-submit happens на back. Test:
- Fill form, submit
- Navigate forward → success
- Browser back → form state как должен быть (depending on UX intent)
- Forward → success page без re-submission
6. Multi-tab synchronisation
Scenario: user open app в two tabs, edit в Tab A, Tab B показывает stale. Bug: untracked divergence, тяжёлый conflict resolution. Test:
- Open app in Tab A and Tab B same item
- Edit item в Tab A, save
- Verify: Tab B либо updates (через BroadcastChannel / storage event) либо показывает conflict warning
- Edit одновременно — last-write-wins acceptable если предсказуемо
7. Retry storm
Scenario: server slow, client retries automatically, retry creates more load, server slower. Bug: thundering herd, downtime amplification. Test:
- Throttle API to 5s timeout
- Verify: client backs off (exponential), не retries every 100ms
- Implement
Retry-Afterheader support - Circuit breaker pattern: после N consecutive failures, stop retrying for X seconds
8. Concurrent mutations: write skew
Scenario: Two users edit same item simultaneously. Last write wins → first user's changes lost silently. Bug: lost updates, no conflict warning. Test:
- Get item в Tab A and Tab B
- Edit different fields в каждом tab
- Save A, then save B
- Verify: либо both changes preserved (merge), либо B gets conflict warning
If-MatchETag header pattern или explicit version field
9. Timer / interval cleanup
Scenario: Component mounts, sets interval, unmounts but timer continues; second mount creates 2nd timer. Bug: memory leak, duplicate API calls, eventual crash. Test:
- Navigate to page with periodic refresh
- Navigate away
- Inspect:
setIntervalcleaned up вuseEffectcleanup - WebSocket / EventSource closed
- Subscriptions unsubscribed
10. Pagination / infinite scroll state
Scenario: user scrolls to page 5, opens detail, back → page 1 (lost position). Bug: scroll position lost, pagination state reset. Test:
- Scroll to deep page
- Click item → detail
- Back → должен restore scroll position И pagination cursor
2. Tools для debugging
Chrome DevTools
- Application → Storage: inspect localStorage / IndexedDB / cookies
- Network → Throttling: Slow 3G to surface race conditions
- Performance → Recording: see when state changes, what triggered re-render
- Sources → Conditional breakpoints: break когда условие на mutation
- Memory → Heap snapshot: ловит uncleaned subscriptions / timers
React-specific
- React DevTools → Profiler: видеть unnecessary re-renders
<StrictMode>в dev — двойной effect call → catches missing cleanup- React Query DevTools — visualise query state, cache, mutations
why-did-you-render— find re-renders без reason
State machines
Для complex state: XState / Zustand с history middleware. Visualise state transitions, catches impossible states.
3. Test methodologies
Stress test
- 100 mutations / second on same resource
- Multiple simulated users on same item
- Network with random failures (10% drop rate via DevTools)
Property-based testing
- fast-check: generate random inputs, verify invariants
- «After any sequence of N mutations, total = expected»
Snapshot integrity tests
- Save snapshot перед mutation
- Apply mutation
- Verify только targeted fields changed
4. Common patterns / solutions
AbortController
useEffect(() => {
const ctrl = new AbortController();
fetch(url, { signal: ctrl.signal })
.then(...)
.catch(e => e.name === 'AbortError' ? null : handleError(e));
return () => ctrl.abort();
}, [url]);
Debounce + last-only
const debouncedSave = useMemo(() => debounce((val) => apiSave(val), 500), []);
Только последняя версия value уходит на сервер.
Optimistic update with rollback
const mutation = useMutation({
onMutate: (newValue) => {
const prev = queryClient.getQueryData(['item']);
queryClient.setQueryData(['item'], newValue);
return { prev };
},
onError: (err, _, ctx) => {
queryClient.setQueryData(['item'], ctx.prev);
showError(err);
},
});
BroadcastChannel для multi-tab
const channel = new BroadcastChannel('app');
channel.postMessage({ type: 'item-updated', id });
channel.onmessage = (e) => { /* refetch */ };
LocalStorage versioning
const STORAGE_VERSION = 2;
function load() {
try {
const raw = localStorage.getItem('key');
if (!raw) return defaultState();
const parsed = JSON.parse(raw);
if (parsed.version === STORAGE_VERSION) return parsed.data;
if (parsed.version === 1) return migrateV1ToV2(parsed.data);
return defaultState(); // unknown version
} catch {
return defaultState();
}
}
5. Anti-patterns
- ❌ Disable retry «у нас retries вызывают баги» — нужен правильный backoff
- ❌
setStateбез проверки mounted в async handler — «Cannot update unmounted component» - ❌ Polling каждую секунду — burns battery, server load
- ❌ Игнор failures — assume optimistic будет ОК всегда
- ❌ Один global cache invalidation после ANY mutation — re-fetches всё, dropеёт perf
- ❌ localStorage без try-catch — parse error → crash
- ❌ localStorage без version → can't migrate gracefully
- ❌ В multi-tab нет sync mechanism — divergence неизбежен
- ❌ Test только happy path mutation — race conditions surface на slow network
- ❌ «Это не повторяется» — повторяется, just не в твоём dev environment
6. Output
- Список 10 классов с pass/fail per primary mutation
- Top-5 race conditions found с repro steps + видео
- localStorage migration plan если нет versioning
- Multi-tab sync strategy decision (BroadcastChannel / storage event / server-side)
- Retry/backoff config для каждого critical API call
- Monitoring: что добавить в Sentry / DataDog для catching race conditions в prod
Smoke-чеклист после деплоя
Что прокликать в первые 5-30 минут после деплоя, чтобы поймать 500-ки, 404, broken JS, сломанные формы — до того как это поймает первый пользователь.
Аудит console-ошибок при проходе
Открыть DevTools-консоль и пройтись по всем ключевым страницам. Категоризировать ошибки и warnings: что блокер, что noise, что отложить. Без этого «всё работает» — наугад.
Поиск битых ссылок и 404
Все ссылки — внутренние, внешние, изображения, OG-картинки, sitemap-URL, redirect-chains. Что чекать руками и чем автоматизировать (linkinator / lychee).