Overview
JavaScript is the layer where sites most often become slow, and where the cost is least visible during development. On the developer's laptop, a 400 KB bundle parses instantly. On a mid-range Android phone on a congested network, the same bundle is seconds of unresponsive page.
Our position is straightforward: ship the least JavaScript that does the job. Modern browsers and modern CSS handle a great deal that used to require scripting, and every kilobyte not shipped is a kilobyte that cannot block the main thread.
Progressive enhancement
Core functionality should work without JavaScript, then be improved by it. A form that posts normally and is enhanced with client-side validation and an async submit still works when a script fails to load, when a request times out, or when an extension interferes. This is not nostalgia — it is the difference between a degraded experience and a broken one.
Modern JavaScript
We write standards-based ES2023+ and let the build handle browser targets:
- Modules with proper code splitting, so a page loads the code it uses and nothing more.
async/awaitwith real error handling, cancellation viaAbortController, and no unhandled promise rejections.- Native APIs where they exist —
IntersectionObserverfor lazy behaviour and scroll effects instead of scroll handlers,ResizeObserverinstead of resize polling,fetchinstead of a library. - Event delegation rather than hundreds of individual listeners.
- Debouncing and throttling on anything triggered by input, scroll or resize.
Working with jQuery honestly
A great deal of production code still runs on jQuery, and there is nothing wrong with that. What is wrong is loading a library to do things the platform now does natively, or maintaining a codebase where nobody knows what a selector touches.
- Existing jQuery codebases: we maintain and extend them. Rewrites are expensive and risky, and "it uses jQuery" is not by itself a reason for one.
- Incremental migration where the code is genuinely holding the project back: replace jQuery module by module behind stable interfaces, so the site is deployable at every step.
- New work: no jQuery.
querySelectorAll,classList,fetchandclosestcover what jQuery was mostly used for, without the dependency. - Duplicate copies: WordPress and plugin ecosystems routinely end up loading two or three versions of jQuery. Finding and removing that is often a quick, visible win.
AJAX and data loading
Asynchronous requests are where subtle bugs live — race conditions, double submissions, stale responses arriving after newer ones.
- Requests cancelled on navigation and superseded by newer ones via
AbortController. - Loading, empty, error and success states all designed and implemented. The error state is the one that gets skipped and the one users actually hit.
- Submit buttons disabled during flight so a double click does not create two orders.
- Retry with backoff for transient failures, and a clear message when retrying will not help.
- CSRF tokens on state-changing requests, and responses treated as untrusted input rather than injected into the DOM as HTML.
Performance and INP
Interaction to Next Paint is now a Core Web Vital, and it measures precisely what users feel: the delay between tapping something and seeing a response.
- Break long tasks so the main thread is never blocked for more than 50ms at a stretch.
- Defer and lazy-load anything not needed for the first interaction; load third-party widgets on interaction rather than on page load.
- Audit third-party scripts ruthlessly. Chat widgets, analytics and tag managers are frequently the largest JavaScript cost on a site and nobody owns the list.
- Budget the bundle and enforce it in the build, so regressions are caught before release rather than in a support ticket.
Security
Front-end code is public. We build accordingly: no secrets or API keys in client bundles, all user-supplied content escaped rather than assigned to innerHTML, a Content-Security-Policy that limits what can execute, and validation repeated on the server because client-side validation is a convenience, never a control.
How we work
- Establish what genuinely needs JavaScript and what CSS or HTML can do instead.
- Build with a performance budget agreed up front.
- Test on real mid-range devices and throttled networks, not just the development machine.
- Measure INP and total blocking time before and after, and report the numbers.
What you get
- Functionality that degrades gracefully instead of breaking.
- A JavaScript budget that is measured and enforced.
- Async behaviour with real error and loading states.
- An honest assessment of legacy code — maintain, migrate incrementally, or rewrite — with the reasoning behind it.
The fastest JavaScript is the JavaScript you did not ship. The second fastest is the JavaScript that loads when the user actually needs it.
