AI Registers a Service Worker and Hopes for the Best
AI generates service workers with: no caching strategy (the service worker intercepts requests but does nothing useful), cache-everything forever (the user sees stale content indefinitely, never gets updates), no update mechanism (a new version is deployed but users are stuck on the old cached version), no offline fallback (the app shows a browser error page when offline), and no selective caching (static assets and API responses cached with the same strategy). A poorly configured service worker is worse than no service worker — it serves stale content with no way for the user to refresh.
Modern service workers are: strategy-driven (cache-first for static assets, network-first for API data), Workbox-managed (Google library that handles caching, precaching, and routing), update-prompted (user sees "New version available" with a refresh button), offline-equipped (custom offline page instead of browser error), and PWA-installable (meets Chrome install criteria: HTTPS, manifest, service worker with fetch handler). AI generates none of these.
These rules cover: caching strategy selection, Workbox precaching and runtime caching, service worker update handling, offline fallback pages, PWA installability, and the web app manifest.
Rule 1: Caching Strategy Selection
The rule: 'Match the caching strategy to the resource type. Cache-first (CacheFirst): serve from cache, fall back to network. Use for: static assets (JS, CSS, images, fonts) that change only on deploy. Network-first (NetworkFirst): try network, fall back to cache. Use for: API responses, HTML pages, and any data that changes frequently. Stale-while-revalidate (StaleWhileRevalidate): serve from cache immediately, update cache from network in background. Use for: content that can be briefly stale (avatars, article lists, non-critical data).'
For the strategy decision tree: 'Is it a static asset with a hash in the filename (app.a3f8c2.js)? Cache-first with no expiration (the hash changes on new versions, old cache entries are never matched). Is it an API response? Network-first with 5-minute cache (fresh data preferred, cache as fallback for offline). Is it a user avatar or content listing? Stale-while-revalidate (show cached immediately, update in background). Is it authentication or checkout? Network-only (never cache sensitive or transactional data).'
AI generates: self.addEventListener('fetch', (e) => { e.respondWith(caches.match(e.request) || fetch(e.request)); }) — cache-first for everything. API responses cached permanently: the user sees stale data forever. Or: no fetch handler at all (the service worker does nothing). With strategy per resource type: static assets load instantly from cache, API data is always fresh, and avatars update in the background. Each resource gets the strategy that matches its update frequency.
- Cache-first: static assets (JS, CSS, images, fonts) with content hashes
- Network-first: API responses, HTML pages — fresh preferred, cache as offline fallback
- Stale-while-revalidate: avatars, listings — instant from cache, background refresh
- Network-only: authentication, checkout, payments — never cache transactional data
- Cache-only: precached app shell — always available, updated on service worker install
Cache-first for everything: API responses stale forever. Network-first for everything: static assets re-downloaded every time. Strategy per type: JS/CSS cache-first (instant), API network-first (fresh), avatars stale-while-revalidate (fast + updated). Each resource gets the strategy matching its update frequency.
Rule 2: Workbox for Precaching and Runtime Caching
The rule: 'Use Workbox (Google library) instead of hand-writing service worker caching logic. Precaching: workbox.precaching.precacheAndRoute(self.__WB_MANIFEST) — Workbox precaches the build output (JS, CSS, HTML) during service worker install. The app shell loads instantly on subsequent visits. Runtime caching: workbox.routing.registerRoute(({url}) => url.pathname.startsWith("/api/"), new workbox.strategies.NetworkFirst({ cacheName: "api-cache", networkTimeoutSeconds: 3 })). Workbox handles: cache versioning, cleanup, quota management, and strategy implementation.'
For Next.js integration: 'Use next-pwa (or @serwist/next): configure in next.config.js, Workbox generates the service worker at build time. Precaches: all static pages, JS chunks, CSS, and images. Runtime caches: API routes, dynamic pages. The integration handles: service worker registration, update detection, and cache cleanup. Zero manual service worker code — configure strategies in next.config.js, Workbox generates the implementation.'
AI generates: 200 lines of hand-written service worker code with manual cache versioning, manual cleanup, manual routing, and manual strategy implementation. Every line is a potential bug (cache never cleaned up, stale entries accumulate, quota exceeded). Workbox: 10 lines of configuration, battle-tested implementation, automatic cache management. The same functionality with zero manual cache code and zero cache bugs.
Rule 3: Service Worker Update Prompts
The rule: 'When a new service worker is available, prompt the user to update — do not silently activate (which can break in-progress work) or wait indefinitely (user stays on old version). Pattern: (1) new service worker installs in the background (waiting state), (2) your app detects the waiting worker: navigator.serviceWorker.addEventListener("controllerchange", () => window.location.reload()), (3) show a toast: "New version available. Refresh to update." (4) on click: tell the waiting worker to skipWaiting, which triggers controllerchange, which triggers reload.'
For the update detection: 'Workbox: workbox.precaching.cleanupOutdatedCaches() handles old cache cleanup. For the UI prompt: register an event listener for the waiting worker. wb.addEventListener("waiting", () => { showUpdateToast(); }). On toast click: wb.messageSkipWaiting(). The page reloads with the new service worker active. Without the prompt: the user is stuck on the old version until they close all tabs and reopen (the default service worker lifecycle). The prompt gives the user control over when to update.'
AI generates: self.skipWaiting() in the install event (forces immediate activation — can break in-progress fetch requests and cause inconsistent cached assets) or no update handling (user stays on old version indefinitely, sees stale content). The prompt pattern: the user controls the timing, the update is explicit, and the transition is clean. No silent breakage, no indefinite staleness.
- Detect waiting worker: wb.addEventListener('waiting', showUpdateToast)
- User-controlled update: toast with 'Refresh to update' button
- On click: wb.messageSkipWaiting() triggers controllerchange and reload
- Never self.skipWaiting() in install: can break in-progress requests
- cleanupOutdatedCaches: Workbox removes old precache entries automatically
No update handling: user stays on the old cached version until they close all tabs and reopen. Could be days or weeks. Update prompt toast: 'New version available. Refresh to update.' User clicks, page reloads with new version. Explicit, controlled, clean transition.
Rule 4: Offline Fallback Pages
The rule: 'Precache a custom offline page and serve it when the network is unavailable and no cached version exists. Workbox: workbox.routing.setCatchHandler(({ event }) => { if (event.request.destination === "document") return caches.match("/offline.html"); }). The offline page: shows a friendly message ("You are offline. Some features are unavailable."), lists cached pages the user can still access, and provides a retry button that checks connectivity and reloads when online.'
For the offline experience: 'Beyond the fallback page: make the core app functional offline. Precache the app shell (layout, navigation, static pages). Cache API responses with stale-while-revalidate (last fetched data available offline). Queue mutations for later (IndexedDB stores pending writes, sync when online via Background Sync API). The offline experience should be: degraded but functional (read cached content, queue writes) — not broken (error page, blank screen).'
AI generates: no offline handling. The user goes into a tunnel: the browser shows its default offline dinosaur page. The user thinks the app is broken. With an offline fallback: the user sees your branded offline page, can access cached content, and queued actions sync when they emerge from the tunnel. The offline experience is part of the product — not an error condition.
Rule 5: PWA Installability and Web App Manifest
The rule: 'Meet Chrome PWA install criteria: (1) HTTPS (required), (2) web app manifest with: name, short_name, icons (192x192 and 512x512), start_url, display: standalone, and theme_color, (3) service worker with a fetch handler (even a minimal one), (4) the app is not already installed. When criteria are met: Chrome shows an install prompt (beforeinstallprompt event). Capture the event: let deferredPrompt; window.addEventListener("beforeinstallprompt", (e) => { e.preventDefault(); deferredPrompt = e; showInstallButton(); }). Show a custom install button; on click: deferredPrompt.prompt().'
For the manifest: '{ "name": "RuleSync", "short_name": "RuleSync", "start_url": "/dashboard", "display": "standalone", "theme_color": "#2563eb", "background_color": "#ffffff", "icons": [{ "src": "/icons/icon-192.png", "sizes": "192x192" }, { "src": "/icons/icon-512.png", "sizes": "512x512" }] }. Link in HTML: <link rel="manifest" href="/manifest.json">. display: standalone makes the installed app look native (no browser chrome). theme_color colors the title bar on Android. The manifest is the PWA identity card.'
AI generates: no manifest, no install criteria met, no install prompt handling. The app is web-only with no install option. Users who want quick access bookmark the URL (small icon, browser chrome visible). With PWA installability: the app installs like a native app (home screen icon, standalone window, splash screen). Zero App Store review, zero native code, instant updates via service worker. The web app gets the distribution advantage of native without the native development cost.
PWA install: home screen icon, standalone window (no browser chrome), splash screen, theme-colored title bar. Zero App Store review, zero native code, instant updates via service worker. The web app gets native distribution without native development cost.
Complete Service Workers and PWA Rules Template
Consolidated rules for service workers and PWA.
- Strategy per resource: cache-first (static), network-first (API), stale-while-revalidate (content)
- Workbox: precaching + runtime routing — 10 lines of config, zero manual cache code
- Update prompt: detect waiting worker, show toast, user clicks to activate new version
- Never skipWaiting() in install: breaks in-progress requests — prompt instead
- Offline fallback: custom branded page, cached content accessible, retry when online
- Background Sync: queue mutations offline, sync when connection returns
- PWA manifest: name, icons (192+512), start_url, display: standalone, theme_color
- Install prompt: capture beforeinstallprompt, show custom button, prompt on click