
Web Development — 2026 Week 2
Cross-cutting frontend topics, tooling, and DX Compiled for immediate developer deployment.


Not All Browser APIs Are "Web" APIs
processLocally flag for on-device processing but only for Chrome. WebAuthn passkeys sync via Google Password Manager, iCloud Keychain, or Microsoft Account — entirely vendor-controlled — and cannot be implemented in Electron because there is no hook into a password manager backend. Web Push routes through FCM (Chrome/Edge), APNs (Safari), or Mozilla's infrastructure, each with different rate limits, message-size caps (FCM: 4 KB, APNs: 5 KB), and delivery guarantees. The article's practical conclusion is to treat these APIs as vendor service integrations — document tested browsers, build graceful degradation, disclose privacy implications to users, and plan consciously for vendor lock-in.Why You Should Start Using Projects in Vitest Configuration
projects array in Vitest's defineConfig lets you declare multiple named test runners inside a single config file, each inheriting shared root-level settings via extends: true while overriding only what differs — eliminating the friction of maintaining separate vitest.unit.config.ts and vitest.integration.config.ts files. A typical setup splits tests by file suffix (*.unit.test.{ts,tsx} vs. *.integration.test.{ts,tsx}) and exposes npm scripts like vitest --project unit and vitest --project integration; running both simultaneously with --project proj1 --project proj2 is possible, which is not achievable with separate config files. Vitest Browser Mode fits naturally into the same structure: a browser project adds browser.enabled, a playwright() provider, and its own setupFiles alongside a sibling unit project using jsdom. One practical caveat: coverage and reporters must stay at the root config level; per-project overrides for those require reverting to separate files. The feature is only worth adopting once a second distinct test type actually exists — single-project setups gain nothing from the added structure.
How We Optimized Rendering Performance While Handling Thousands of Annotations in React — Part 2
useSelector selectors returning new arrays from .filter() or .map() triggered rerenders on every store update; the fix was computing derived values inside a useMemo scoped to the relevant state slice, or passing shallowEqual when the selector must return an object. The SDK also used Immutable.js for global state, which creates a new object reference on every property change; narrowing useMemo/useCallback dependencies to specific primitive fields (annotation.id, annotation.type) prevented cascade recalculations. After these changes, hovering over one annotation no longer caused all annotations to rerender, toolbar updates became isolated to real state changes, and CPU usage dropped dramatically.Ads as a Performance Budget Problem

A Better Way to End-to-End Test Your Webapp
e2e-testing-agent, an open-source TypeScript library that replaces hand-written Playwright selectors with a record-and-replay agent loop: on first run, the agent takes screenshots, sends them to OpenAI's computer-use model, executes the returned browser actions via Playwright, and stores the recorded steps — costing Swizec roughly $7 for a full purchase-flow test. Subsequent runs replay the stored steps without burning tokens and run approximately 80% faster, catching regressions without requiring test rewrites when UI changes; the conceptual model is borrowed from Ruby VCR, treating the entire frontend as a third-party API to record. Tests are written as two-argument goal descriptions — a URL and a natural-language intent string such as "Sign up for the mailing list" — and support custom tool calls so the agent can retrieve environment-variable credentials, faker-generated inputs, or database fixtures at runtime. Verification at the end of each run uses a fast, cheap model (gpt-5-nano currently) to visually confirm the goal was achieved from the final screenshot. The author acknowledges that computer-use models are not yet reliable enough for unattended production use and that the first agentic recording sometimes requires babysitting.The Next Two Years of Software Engineering
Addy Osmani frames the next two years around five unresolved questions — junior hiring, skill atrophy, role evolution, specialist vs. generalist, and education — each presented as two contrasting scenarios rather than firm predictions. A Harvard study of 62 million workers found that generative AI adoption cuts junior developer employment by 9–10% within six quarters while senior employment barely moves, compounding a trend of 50% fewer new-grad hires at big tech over the past three years. The skills question is equally contested: 84% of developers now use AI assistance regularly, shifting entry-level work from implementing algorithms to prompting and verifying AI output, with senior engineers warning this risks a deskilling generation unable to catch subtle bugs. Osmani argues the T-shaped engineer — deep in one or two domains, broadly conversant across the stack — is the most durable archetype as AI augments generalists more than specialists, with nearly 45% of engineering roles already requiring multi-domain proficiency. The consistent throughline: human competitive advantage concentrates on architecture, security, system design, mentorship, and the judgment to know when AI output is wrong.

Not All Browser APIs Are "Web" APIs
Polypane's blog exposes a widely misunderstood fracture in the web platform: many standardized-looking browser APIs are actually thin wrappers over proprietary, vendor-operated backend services. The Geolocation API routes through Google Location Services in Chrome and Chromium-based browsers (Firefox also switched to Google's services after retiring Mozilla Location Service in 2024), meaning user location data is silently sent to third-party servers with no browser disclosure. Speech Recognition streams audio to Google, Apple, or Azure Cognitive Services depending on the browser; Chrome 139 introduced an opt-in processLocally flag for on-device processing but only for Chrome. WebAuthn passkeys sync via Google Password Manager, iCloud Keychain, or Microsoft Account — entirely vendor-controlled — and cannot be implemented in Electron because there is no hook into a password manager backend. Web Push routes through FCM (Chrome/Edge), APNs (Safari), or Mozilla's infrastructure, each with different rate limits, message-size caps (FCM: 4 KB, APNs: 5 KB), and delivery guarantees. The article's practical conclusion is to treat these APIs as vendor service integrations — document tested browsers, build graceful degradation, disclose privacy implications to users, and plan consciously for vendor lock-in.
Why You Should Start Using Projects in Vitest Configuration
The projects array in Vitest's defineConfig lets you declare multiple named test runners inside a single config file, each inheriting shared root-level settings via extends: true while overriding only what differs — eliminating the friction of maintaining separate vitest.unit.config.ts and vitest.integration.config.ts files. A typical setup splits tests by file suffix (*.unit.test.{ts,tsx} vs. *.integration.test.{ts,tsx}) and exposes npm scripts like vitest --project unit and vitest --project integration; running both simultaneously with --project proj1 --project proj2 is possible, which is not achievable with separate config files. Vitest Browser Mode fits naturally into the same structure: a browser project adds browser.enabled, a playwright() provider, and its own setupFiles alongside a sibling unit project using jsdom. One practical caveat: coverage and reporters must stay at the root config level; per-project overrides for those require reverting to separate files. The feature is only worth adopting once a second distinct test type actually exists — single-project setups gain nothing from the added structure.
How We Optimized Rendering Performance While Handling Thousands of Annotations in React — Part 2
Igor Perzic of Nutrient details three surgical React optimizations applied to their Web Viewer SDK after Part 1's broad wins (memoization, web workers, deferred tasks) still left sluggishness when thousands of document annotations were rendered simultaneously. The team used React Scan for a high-level rerender heatmap, then wrote a custom hook that diffed previous vs. current props to identify the exact prop triggering each rerender — a technique that exposed callback references, inline style objects, and JSX children recreated on every render as the primary culprits. For Redux, useSelector selectors returning new arrays from .filter() or .map() triggered rerenders on every store update; the fix was computing derived values inside a useMemo scoped to the relevant state slice, or passing shallowEqual when the selector must return an object. The SDK also used Immutable.js for global state, which creates a new object reference on every property change; narrowing useMemo/useCallback dependencies to specific primitive fields (annotation.id, annotation.type) prevented cascade recalculations. After these changes, hovering over one annotation no longer caused all annotations to rerender, toolbar updates became isolated to real state changes, and CPU usage dropped dramatically.
Ads as a Performance Budget Problem
Sarah Gerrard reframes ad integration as an explicit performance budget constraint rather than an afterthought, drawing on her work joining the TanStack ecosystem in September to address years of accumulated technical debt — unused JS paths, dead CSS, and compounding performance regressions that could not be traced to any single commit. From the browser's perspective, third-party ad scripts compete for the same main thread, layout and style recalculation cycles, network bandwidth, and memory budget as first-party UI code; "just load it async" defers the cost but does not eliminate it, as async scripts still parse, execute, trigger layout recalculations, and contend for scheduling time. The hidden performance cost of ads shows up as long tasks delaying first interaction, layout shifts from late DOM insertion, and cascading delays that harm LCP, CLS, and INP — Core Web Vitals increasingly tied to SEO ranking. Gerrard's concrete prescription: allocate a defined budget slice to monetization upfront, then reclaim the equivalent by removing unused JavaScript, eliminating dead CSS, tightening linting, and simplifying component boundaries. On documentation and content sites such as TanStack's, the tolerance for instability is lower because readers notice text jumps acutely and CLS regressions directly degrade discoverability.
READ_FULL_LOGarrow_forwardA Better Way to End-to-End Test Your Webapp
Swizec Teller introduces e2e-testing-agent, an open-source TypeScript library that replaces hand-written Playwright selectors with a record-and-replay agent loop: on first run, the agent takes screenshots, sends them to OpenAI's computer-use model, executes the returned browser actions via Playwright, and stores the recorded steps — costing Swizec roughly $7 for a full purchase-flow test. Subsequent runs replay the stored steps without burning tokens and run approximately 80% faster, catching regressions without requiring test rewrites when UI changes; the conceptual model is borrowed from Ruby VCR, treating the entire frontend as a third-party API to record. Tests are written as two-argument goal descriptions — a URL and a natural-language intent string such as "Sign up for the mailing list" — and support custom tool calls so the agent can retrieve environment-variable credentials, faker-generated inputs, or database fixtures at runtime. Verification at the end of each run uses a fast, cheap model (gpt-5-nano currently) to visually confirm the goal was achieved from the final screenshot. The author acknowledges that computer-use models are not yet reliable enough for unattended production use and that the first agentic recording sometimes requires babysitting.
The web development general story of week 2 is about professional survival and practical craft under the pressure of AI. The featured essay is Addy Osmani's scenario map for the next two years of software engineering — structured not as predictions but as paired contrasts across five unresolved questions: junior hiring, skill atrophy, role evolution, specialist vs. generalist, and education. The Harvard study of 62 million workers that Osmani cites — junior developer employment down 9–10% within six quarters of generative AI adoption, senior employment nearly flat — gives the career anxiety a data frame. His resolution: the T-shaped engineer who is deep in one or two domains and broadly conversant across the stack is the most durable archetype, because AI amplifies generalists more than specialists, and nearly 45% of engineering roles already require multi-domain proficiency. The consistent competitive advantages left to humans are architecture, security, system design, mentorship, and the judgment to catch when AI output is wrong.
Polypane's investigation of browser APIs exposes a structural surprise worth internalizing: many standardized-looking APIs are thin wrappers over vendor-operated backend services. The Geolocation API routes through Google Location Services in Chrome and Chromium (Firefox switched over in 2024). Speech Recognition streams audio to Google, Apple, or Azure Cognitive Services depending on the browser. WebAuthn passkeys sync through entirely vendor-controlled infrastructure and cannot be implemented in Electron. Web Push routes through FCM, APNs, or Mozilla infrastructure — each with different rate limits and message-size caps (FCM: 4 KB; APNs: 5 KB). The practical prescription is to treat these APIs as vendor service integrations, document tested browsers, disclose privacy implications, and plan consciously for lock-in.
The testing section of this week is unusually rich. Vitest's projects array in defineConfig lets a single config file declare multiple named test runners that inherit shared settings via extends: true and can run simultaneously with --project flags — something separate config files cannot achieve. Swizec Teller's e2e-testing-agent replaces hand-written Playwright selectors with a record-and-replay agent loop powered by OpenAI's computer-use model, costing roughly $7 for a full purchase-flow test on first run and replaying 80% faster on subsequent runs. Nutrient's deep dive into React rendering optimization for thousands of simultaneous annotations — using React Scan heatmaps, a custom diff hook, useMemo scoped to specific state slices, and shallowEqual on object-returning selectors — provides the surgical toolkit for the class of performance problem that broad memoization strategies leave behind. And Sarah Gerrard's reframing of ad integration as an explicit performance budget constraint closes the week: third-party ad scripts compete for the same main thread, layout cycles, and memory as first-party code, and "just load it async" defers but does not eliminate the cost — the fix is allocating a defined budget slice to monetization and reclaiming it by removing unused JavaScript and dead CSS.
- Build T-shaped depth deliberately: invest in at least one domain where you can catch AI mistakes that a generalist prompt engineer would miss — architecture, security, and system design are the roles most insulated from AI substitution.
- Audit your use of Geolocation, Speech Recognition, Web Push, and WebAuthn now — document which vendor backends each routes through, disclose the privacy implications to users, and plan fallbacks for the browser environments where your feature degrades.
- When adding third-party ad scripts, allocate a defined performance budget slice to monetization upfront and offset it by removing an equivalent weight of unused JavaScript and dead CSS — loading ads async defers the main-thread cost but does not eliminate it.