It is easy to leave a frontend conference with a long list of things to learn and no clear reason to use them. AI assistants, new runtimes, signals, edge rendering, WebAssembly: each can solve a real problem. They can also add work to an application that was doing fine.
I find a more useful starting point is the constraint. Is the page slow to become usable? Are state changes hard to follow? Does a small UI change take too long to verify? A concrete problem gives you a way to evaluate a new tool and a reason to stop if it does not help.
The changes below are worth understanding. They do not all belong in the same application.
Use AI where you can check the work
A component request usually hides several decisions. “Build a search box” leaves loading behavior, keyboard navigation, empty results, errors, and accessible naming unspecified. A generated component can look finished while making poor choices about all of them.
Give the assistant an existing component to follow, the relevant design tokens, and the behavior you expect. Ask for a small change that you can run and inspect. This is particularly useful for repetitive markup, test setup, and exploring an unfamiliar API with its documentation available.
Generated tests need review too. A test can encode the same wrong assumption as the implementation. Define the important behavior first: for example, pressing Escape closes the suggestions without clearing the input, and an empty result is announced without moving focus.
Accessibility also needs human evaluation. The W3C accessibility evaluation guidance (opens in a new tab) explains that tools alone cannot determine whether a site is accessible. Use automated checks to find problems, then inspect the keyboard flow, names, states, and announcements in the actual interface.
Choose a runtime for the workload
A JavaScript runtime change affects the development and server environment. It does not replace the JavaScript engine in your user’s browser. Keep those decisions separate when evaluating performance claims.
Bun (opens in a new tab) combines a runtime with tools such as a package manager, bundler, and test runner. Deno (opens in a new tab) provides explicit permissions for sensitive operations such as filesystem and network access. Those are concrete differences to evaluate; a benchmark headline cannot tell you whether your application will be easier to operate on either runtime.
Try the build, tests, native dependencies, deployment environment, and debugging tools with the application you maintain. Measure the slow operation you intend to improve. Keep the migration small enough that compatibility failures are easy to locate.
Use WebAssembly for a specific computation
WebAssembly’s documented use cases (opens in a new tab) include tasks such as image processing and media workloads. It can be useful when you need an existing library written in another language or a measured improvement in a compute-heavy operation.
For an image editor, that might mean keeping controls and UI state in JavaScript while a worker runs a WebAssembly filter. Account for module download, initialization, and data transfer as well as the filter’s execution time. Moving work off the main thread and making that work faster are separate improvements.
WebGPU (opens in a new tab) provides another option for graphics and GPU computation. It is worth considering for a workload with enough parallel work to benefit from it. Check the devices and browsers you support, and provide a fallback where the capability is unavailable.
Make state easier to follow
Signals make dependencies between state and derived values explicit. Angular’s signals guide (opens in a new tab) describes writable signals and memoized computed values. A filter is a small example:
import { computed, signal } from '@angular/core';
type Item = { id: string; name: string; active: boolean };
const items = signal<Item[]>([]);
const activeOnly = signal(false);
const visibleItems = computed(() =>
activeOnly() ? items().filter(item => item.active) : items()
);
items.set([
{ id: 'a', name: 'Alpha', active: true },
{ id: 'b', name: 'Beta', active: false },
]);
activeOnly.set(true);
// visibleItems() now contains Alpha.The filtered list is derived rather than maintained as a second copy of state. That removes a synchronization problem: every update to the items or filter has one place to go.
Signals do not decide who owns the state or when to fetch data. Those boundaries still need design. Keep local interaction state close to the component and share state when multiple parts of the application need the same source of truth.
Split applications when ownership requires it
Micro-frontends can let teams release independently, but they also create contracts for routing, authentication, shared UI, and dependency versions. If teams release together and change the same features, separate deployments may add coordination without much independence. Start by making module boundaries clear inside the application.
Place rendering near the right data
Rendering closer to a user can reduce part of a request’s travel time. It can also add a long round trip if the data still lives in a distant region. Sketch the full request path before moving server code to an edge location.
Static content, personalized data, and interactive controls have different needs. Cache public content where appropriate, render data near its source, and send the browser the code it needs for interaction. Then measure a representative user journey, including slow devices and cold caches.
Prefetch with a budget
Loading a likely next page can make navigation feel faster. It can also spend bandwidth on a page the user never visits. Start with an obvious signal, such as a likely next step in a sequence, before adding a prediction model.
Chrome’s prerendering documentation (opens in a new tab) covers the distinction between fetching resources and preparing an entire page. Prerendering can execute page code before a visible navigation, so account for analytics and other side effects. Check browser support and measure wasted work alongside navigation speed.
Offline support needs equally explicit decisions. If a user edits a record offline, decide how the application represents pending work, detects a conflicting edit, and recovers after a failed sync. A cache alone cannot define those product behaviors.
Shorten the feedback loop
The most useful tooling improvement may be a faster answer to an ordinary question: did this change break the form? A focused test, a component preview, and a readable error often help more than another layer of automation.
Make the common path easy to run locally and in CI. Keep commands explicit enough that another engineer can reproduce a failure. If a tool chooses configuration automatically, there should still be a way to inspect the resolved settings and override them.
For a form component, I would want quick access to populated, empty, loading, disabled, and error states. That helps an engineer or designer spot a missing behavior before opening a pull request.
Give new interfaces a useful job
A natural-language interface can help someone express a complex filter. For example: “Show campaigns with a click-through rate above three percent in the last thirty days.” The interface should show the interpreted dates, metric definition, and filters so the user can correct them. Keep direct controls available for small adjustments.
Voice adds another input method, but it also brings privacy, recognition, and correction concerns. A user in a shared office may prefer typing. A person correcting one date should not have to repeat the entire request.
WebXR (opens in a new tab) provides APIs for immersive experiences. A spatial view may be useful for inspecting a physical object or a three-dimensional model. An ordinary reporting table needs a stronger reason to move into that environment. Design around the task and provide alternatives for users without the required device or input method.
Choose tools the team can maintain
Integrated tools can reduce setup work, but the useful test is what happens when one part fails. Can you run a test outside the editor? Inspect a generated build configuration? Export the result of a review? Reproduce a preview without the person who created it?
Make design and engineering feedback concrete. A preview with the exact changed state is easier to review than a description of it. A short note about the intended behavior helps reviewers judge whether a generated implementation meets the requirement.
Separate framework features from promises
Follow the documentation for the version you use. Angular’s incremental hydration (opens in a new tab) lets an application defer hydration of parts of a server-rendered page until specified triggers. That is a specific behavior to evaluate against your page’s interaction needs. It does not mean every component becomes interactive without client-side work.
Treat broader claims about automatic optimization or generated, production-ready interfaces with the same care. Ask what the feature does, what work it moves elsewhere, and how you will detect a regression. These are my engineering preferences, not a statement of my employer’s product plans.
Run a small experiment
Pick one recurring problem and define the result you want before trying a new tool. For a slow image operation, compare total completion time and UI responsiveness. For AI assistance, compare the time to a reviewed change, including corrections. For a rendering change, compare the actual loading and interaction experience.
Keep the existing implementation as a baseline and limit the experiment to one route, component, or task. Write down what improved, what became harder, and what you still do not know. That gives you a better reason to adopt a technology than its place on a trends list.
Continue reading
More from the journal.
JavaScript
7 min read
JavaScript from ES6 to ES2024: What Actually Helps
A practical look at modern JavaScript: clearer async code, safer defaults, and data transformations with fewer surprises.
Angular Architecture
6 min read
Building Angular Apps That Stay Easy to Change
Feature boundaries, state ownership, and rendering choices that keep a growing Angular application understandable.
Performance Engineering
7 min read
Web Performance: Find the Wait, Then Fix It
A practical method for finding slow user journeys, tracing the cause, and checking that a performance change actually helps.