
Web Development — 2026 Week 2
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… 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
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 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.