HardArchitecture
Users have your app open in several tabs and report being "logged out" in only one of them. How would you design session state across tabs?
Make the cookie or token store the single source of truth and treat every tab as a cache of it, not an owner. Pick one tab to do token refresh — a lock via the Web Locks API, or a leader election over `BroadcastChannel` — so five tabs do not race to refresh with the same refresh token and invalidate each other. Broadcast the outcome: on refresh, every tab adopts the new token; on logout or a 401 that cannot be recovered, every tab clears state and redirects. Re-validate on `visibilitychange`, since a background tab may have missed messages while frozen.
The classic cause of this bug is concurrent refresh with a single-use refresh token: two tabs refresh at once, the server rotates the token, the loser is holding a token that no longer exists and its next call 401s. One writer plus broadcast is what removes the race. The `visibilitychange` re-check matters because a tab in the background can be throttled or frozen entirely — bfcache and tab discarding both mean a tab can wake up with a stale view of the world and no message ever arrived to tell it.
MediumBest practice
A feature you want needs a browser API that part of your audience does not have. How do you proceed?
Detect the feature rather than the browser — `if ("share" in navigator)` — and design the fallback first, so the baseline experience is complete without the API. Then layer the enhanced path on top. Check the actual support numbers for your audience in your analytics rather than in the abstract, and reach for a polyfill only when the API is genuinely replaceable in JavaScript; load it conditionally so the browsers that do not need it pay nothing.
User-agent sniffing dates instantly and is wrong for every browser you did not think of; feature detection asks the only question that matters. Designing the fallback first is what keeps the enhancement optional — if you build the enhanced path first, the fallback tends to become a broken version of it rather than a coherent simpler experience.
MediumWrite code
Implement copy-to-clipboard that works across contexts and fails visibly
The failure path is the whole question: clipboard writes reject for ordinary reasons, and a silent catch produces the worst UX — a button that appears to do nothing.
EasyConcept
When does `localStorage` stop being the right choice, and what actually happens when browser storage fills up?
Leave `localStorage` when any of these apply: the data is more than a few hundred kilobytes (the API is SYNCHRONOUS, so reading and parsing a large blob blocks the main thread on startup); you need structured queries or indexes; you need to store binary data such as Blobs; or you need access from a Worker, where `localStorage` does not exist. IndexedDB covers all four — asynchronous, indexed, structured-clone capable, available in workers — at the cost of a clumsier API, which is why most teams wrap it. On quota: browsers grant an origin a share of available disk, and exceeding it makes writes throw (`QuotaExceededError` for `localStorage`), so writes need a try/catch rather than an assumption. Storage is also EVICTABLE — under pressure a browser may clear a site’s data entirely unless it has been granted persistence — so anything you cannot afford to lose belongs on a server, not in the browser.
The companion question compares the storage APIs. This one is about the boundary and the failure mode, which is what you actually need when deciding where to put an offline draft — and the evictability point is the one most people have never considered.
MediumDebugging
Your `fetch` with `credentials: "include"` fails even though the server returns `Access-Control-Allow-Origin: *`. Why, and what must the server send instead?
// Client
await fetch('https://api.example.com/me', { credentials: 'include' });
// TypeError: Failed to fetch
// Server response headers
Access-Control-Allow-Origin: *
A credentialed request has stricter rules. The browser refuses to expose the response unless the server echoes the EXACT origin — `Access-Control-Allow-Origin: https://app.example.com` — because the wildcard is forbidden once credentials are involved, and it must also send `Access-Control-Allow-Credentials: true`. The same tightening applies to the other wildcards: `Access-Control-Allow-Headers: *` and `Expose-Headers: *` are not honoured for credentialed requests either, so each header has to be named. On a preflighted request the server must return those headers on the `OPTIONS` response as well as the real one, and `Vary: Origin` matters if anything caches the response. The reason is straightforward: the wildcard means "any site may read this", which cannot be safe for a response tied to someone’s session.
The paired questions cover what preflights and what CORS actually protects. This one is the specific failure engineers hit most often, and the answer has a real "why" behind it rather than a list of headers to copy.
ExpertFollow-up
You are adding a service worker to an existing production app. How would you roll it out safely?
Very carefully, because a service worker is the hardest thing to roll back — it can outlive your ability to deploy over it. Ship the registration behind a flag to a small percentage first. Start with the narrowest useful scope and the most conservative strategy: network-first or stale-while-revalidate for HTML, cache-first only for hashed immutable assets, never cache-first for the document. Version the cache name and clean up old caches on activate. Ship the kill switch BEFORE the feature: a path that unregisters the worker and clears caches, so a bad release can be recovered without waiting for every user to close every tab. Monitor for stale-version reports and cache hit rates after rollout. And test the upgrade path explicitly — the second deploy is where service worker bugs appear, not the first.
Expert-level because the rollback story is the whole risk: shipping the kill switch first is the detail that distinguishes someone who has done this from someone who has read about it.
HardMultiple choice
What triggers a CORS preflight request?
A cross-origin request skips preflight only if it is "simple": method GET, HEAD or POST, and every author-set header on the CORS-safelist (`Accept`, `Accept-Language`, `Content-Language`, and `Content-Type` limited to `application/x-www-form-urlencoded`, `multipart/form-data` or `text/plain`). `Authorization` is NOT safelisted, so setting it always triggers an `OPTIONS` preflight — as does `Content-Type: application/json`, which is why almost every JSON API call preflights. The preflight lets the server approve the method and headers before the real request is sent.
HardPredict the output
Which of these reliably fires when a user closes a mobile tab?
window.addEventListener('beforeunload', flush);
window.addEventListener('unload', flush);
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'hidden') flush();
});
Only the `visibilitychange` handler is reliable. `unload` and `beforeunload` are not fired dependably on mobile — a tab can be discarded or backgrounded and killed without them — and registering them also disables the back/forward cache, hurting navigation performance.
`visibilitychange` to `hidden` is the last event you are guaranteed, so it is the correct place to flush analytics or persist a draft — paired with `navigator.sendBeacon`, since a normal fetch may be cancelled as the page goes away.
The remaining 30 Browser & Web APIs questions — plus mock interviews, spaced revision and progress tracking — are in the free interview prep workspace.