Stop scanning every market x product after the answer is known
/leads/available-summary returns three things — hasAcceptable, hasAcceptableCredit and one example product — but visited every market x product pair regardless,...
Index the two shapes geolite_locations is actually queried by
Country alone was the only index on an 80,429-row table, and neither real query used it well. Measured on production: city resolution (country + subdivision)...
Count in the database instead of hydrating every commit
GET /changelog/public/stats loaded all 16,571 commit logs as entities and tallied them in a Java loop to produce two small maps and a total. It takes no authent...
Index member_rights_applied, bound the intake claim, aggregate changelog stats
Three findings from an endpoint-by-endpoint audit, each measured against production rather than inferred. member_rights_applied declared no indexes at all whil...
Fetch one server-filtered page instead of the whole organisation
The grid fetched every lead in the org and narrowed it in a ~300-line useMemo. Measured on production 2026-08-20: 1,427 leads fetched so seven could be shown, 2...
Page the grid endpoint and take the PHI audit write off the request thread
GET /api/security/leads returned every lead in the organisation. It now takes page/size/sort plus the filters the grid actually uses, and returns a page with a ...
Filter and page the grid in the database, batch the PHI audit trail
Loading /leads took 23.3 s at 1,427 leads. 23.3 s of that was the HIPAA audit trail: recordLeadList writes one phi_access_log row per lead returned, and each on...
Compress every response, not just the document
server-nonce.js asks upstream for `identity` so the HTML can be stamped with a CSP nonce without a decompress/recompress round trip, then recompresses on the wa...
Drain deferred work one item per idle period
Four components waited for the main thread to go quiet before doing their work — the toast/consent bundle, the scroll chrome, the touch target sweep and the ref...
Read the scroll position from an observer, not from layout
The nav's scrolled/at-top state came from a scroll handler that read window.scrollY, and the mount effect called that handler synchronously during React's commi...
Serve static assets as build-time brotli
The flagged chunk is react-dom, and I checked it properly before concluding anything: it is the production build (no dev markers, and the profiling hooks `injec...
Drop the unused preconnect, and fix the 565 ms of brotli I added
Two findings, and the second one was mine. THE DOCUMENT TOOK 1,259 ms BECAUSE OF THE NONCE WRAPPER. Measured against the live site: the HTML answered in ~900 ...
Move every looping animation onto the compositor
All 26 elements Lighthouse listed under "avoid non-composited animations" were the same mistake in four places: animating a paint property. Measured with Chrome...
63 HTTP requests down to 22
Implements the request-budget report. Measured the same way it was written — Chrome over the debugging protocol, mobile emulation, sixteen-second window. 6...
The reel was never using hls.js, and phones paid for it
Chasing "make fewer HTTP requests" found two real things, and the second is much larger than the audit that led to it. THE HLS BRANCH NOBODY WAS TAKING. HeroSt...
Stop re-measuring the document on every scroll frame
Two scroll listeners on the homepage read `scrollHeight` per event. That is a layout query, and this page is the worst place to make one repeatedly: its section...
Drop backdrop-filter on phones
The homepage renders 94 elements carrying `backdrop-filter: blur(20px)`. Every one is a separate compositing layer that has to read back everything painted behi...
Take the footer and the scroll chrome off the client
Three changes, all removing work from the window where the page is being judged. `(pages)/layout.tsx` carried `"use client"` and never needed it — no state, no...
Stop three resources from outranking the LCP element
The LCP breakdown is where the remaining time is, and its two loading subparts — 270 ms of resource load delay, 500 ms of load duration — are a priority problem...
Remove gsap, the second animation runtime on the homepage
Framer was the chunk Lighthouse named. GSAP is the one it did not, because it arrives late enough to fall outside "unused JavaScript" — but it is 69 KB in its o...
Remove framer-motion from the site
"Reduce unused JavaScript" has named the same 35.4 KB chunk in every report, and it is Framer Motion. It is not an eager script on the homepage, which is why it...
Resolve the org's icons on the server and preconnect the origin
Lighthouse named theme.kamocrm.com as an unconnected origin worth 300 ms while also reporting no origins preconnected, and last round I read that as a stale com...
Stop three route bundles being prefetched onto the homepage
Lighthouse's 103 KiB of "unused JavaScript" named four chunks, two of them essentially 100% unused. None of them appear in the homepage's HTML — they are other ...
Name the backdrop widths, and read the theme config on the server
Two findings, both of which were doing something other than what the code said. THE BACKDROP WAS FETCHING THE 1920 CANDIDATE. 149 KB, for a decorative picture ...
Compress the images harder, hold the cards still, drop duplicate requests
Images. Every quality setting on the page goes down a step, and the allow-list in next.config.ts goes to [30, 50] so nothing can quietly ask for more: hero...
Split the hero, and take the last two libraries off the page
Step C. Neither GSAP nor Framer Motion is on the homepage any more, and the `home` message namespace is no longer sent to the browser at all. JS 24...
Seven sections stop hydrating
Step B. TrustBar, WhatReplacesWhat, HowItWorks, UseCases, Features, CTAStrip and Pricing are server components now. They render to HTML and ship no JavaScript. ...
Take the animation runtime off the homepage entirely
Step A of the hydration work. Framer Motion is gone from the homepage's eager bundle: 798,723 -> 687,593 bytes raw, 245,367 -> 209,422 gzip. That is 35,945 fewe...
Stop rendering eight off-screen sections before the hero can paint
LCP was 4.4 s, and its breakdown said where: 120 ms to first byte, 290 ms of load delay, 390 ms actually downloading the image — and then 1,810 ms of "element r...
Undo the two changes that cost 380ms of blocking time, and quiet the phone
The last two rounds took the score from 87 to 67. Total Blocking Time went from 80 ms to 460 ms and LCP from 3.9 s to 4.4 s. Two of my own changes did that, and...
Move the headline sweep onto the compositor, and the chat widget off load
Two things left over from the last round, both flagged rather than done because each trades something. Doing them now. THE HEADLINE'S LIGHT PASS. It was a seco...
Inline the stylesheet, and drop polyfills nothing can reach
Two Lighthouse findings, both of which stayed open after the last round because I had concluded they were not addressable. They were; I had not looked hard enou...
Stop shipping 1.8 MB of unresized backgrounds after hydration
ThemedBackground was the most expensive thing left on the homepage, and none of it looked like a bug. It rendered a raw <img> straight at the theme bucket, so t...
Stop the fold waiting on hydration, and clear the load window
The homepage shipped its whole fold invisible. Framer Motion serialises an `initial` prop into the prerendered HTML as an inline style, so the markup arrived wi...
Stop the analytics tracker hijacking every link but Home
Every page except the homepage took seconds to open, and the reason was never in this app's own navigation path: the server prerenders, prefetch payloads are co...
Drop the sitewide loading boundary that only secondary pages paid for
Every page except the homepage felt slow, which points straight at the (pages) subtree — and at something I put there. When 4fae924 added (pages)/loading.tsx ev...
Prerender the changelog count, refresh it on publish instead of on visit
Every visitor was fetching this number themselves. PreviewBanner lives in the shared layout, so on every page it hit /api/changelog/today after hydration, waite...
Stop prefetching a megabyte per page, and stop calling the backend for free
Two costs that were paid on every page, both found by following the reporter's own hunch about i18n and needless backend calls. PREFETCH VOLUME — a regression ...
Stop every page switch waiting on a redirect and a 2s fade-in
The prerendering work in 4fae924 fixed the server and the router, and the site still felt like it took 1-2 seconds to change pages. It was never the network. Tw...
Prerender the routes again and give a navigation something to paint
The locale layout resolved its messages with a bare getMessages(), which reads request headers and so opted the ENTIRE app out of static rendering. With no load...
Resolve each handler's platform annotation once
The interceptor is registered for /** so the annotation lookup ran on every request this service serves, sign-in included, and AnnotatedElementUtils walks the m...
Ask for the unread count, not the whole folder tree
The mail badge needs one integer and was fetching every folder in the mailbox to find it, which on the IMAP providers is the most expensive call the mail backen...
Stop making the inbox wait on two lookups it can see the answer to
Two round trips still sat in front of a rendered inbox, and by this point they were the only thing left there — the folder tree and the first page already arriv...
Ask earlier, ask once, and stop asking for what has not changed
Three more things standing between opening /messages and seeing mail. Nothing was requested until the bundle had landed. /messages pulls several megabytes of J...
Stop paying for a key nothing reads, and stop waiting to ask
Three things were slowing /messages down before a single message was fetched. Every email call made a round trip to /api/generate-otk first and sent the result...
Stop opening every folder, and stop refetching every message
Two round-trip problems on the /messages critical path, both invisible in the code that caused them. listFolders opened each folder read-only to read two integ...
Fetch the summary slices' to-one associations and count instead of scanning
The Overview's attention and recent slices read a title per row off a LAZY @ManyToOne, costing up to 16 extra selects per block under open-session-in-view. All ...
Refresh planner statistics after the weekly GeoLite rebuild
YugabyteDB does not auto-analyze, and the staging+rename swap replaces geolite_blocks wholesale -- so after every Sunday sync the planner had no statistics for ...
Geolite range index must be ASC, not YugabyteDB's default HASH
The IP lookup is a range scan: WHERE network_start <= ? ORDER BY network_start DESC LIMIT 1 YugabyteDB partitions the LEADING index column by HASH unless tol...
Like what you see shipping?
All of it arrives in your workspace on its own. Start on the free plan and read this page again in a month.
