DEV Community

Cover image for A Website Can Be Fast and Still Be Bad
Suresh
Suresh

Posted on

A Website Can Be Fast and Still Be Bad

I have a personal rule: never ship anything I haven't checked against my own list first.

It didn't start as a list. It started as a bunch of sticky notes and half-forgotten Slack messages to myself, every time something went wrong in production — a query that crawled once real traffic hit, a token that got stolen because I stored it in the wrong place, a "fast" page that still lost users because nobody bothered asking who it was for. Over the years, working across React, Node, MongoDB, MySQL, SQL Server, and AWS, those notes turned into an actual set of do's and don'ts I run through on every project, no matter how small.

Lately AI tools have made a lot of the "fast website" checklist feel automatic — compress the image, cache the query, lazy-load the asset, done. And that part genuinely works. But a fast site and a good site aren't the same thing, and most of these AI-speed guides quietly skip the parts that actually decide whether people stick around.

So here's my list, properly written out for once — frontend, backend, security, and the one step before all of it that most people forget to do at all.

Let's start where the user actually meets your work — the frontend.

Part 1: Making the Frontend Actually Fast

Speed here comes down to three things: how much you're sending down the wire, how long the server takes to respond, and how smart the browser is about loading what it receives.

Do:

  • Compress images and serve modern formats. Drop the PNGs and JPEGs where you can and switch to WebP or AVIF — same visual quality, a fraction of the file size.
  • Lazy-load anything below the fold. Images, iframes, heavy components — don't make the browser fetch what the user hasn't scrolled to yet.
  • Put a CDN in front of your static assets. Cloudflare (or CloudFront if you're already on AWS) serves content from a node physically closer to the visitor, which shaves off real, measurable latency.
  • Ship less JavaScript on first load. Code-split your React bundles by route so people aren't downloading your entire app just to read a landing page.

Don't:

  • Don't lazy-load your hero image or anything visible on first paint — that just delays the thing people see first.
  • Don't rely on a single CDN region if your audience is global. Check where your traffic is actually coming from before you assume Cloudflare's defaults have you covered.

Making People Actually Like Using the Site

This is the part that gets skipped once the Lighthouse score looks good. A fast site that's confusing to use just lets people leave faster.

Do:

  • Keep navigation dead simple. If someone has to think about where to click, you've already lost a few seconds of their patience.
  • Make search actually work. Not a search bar that exists for decoration — one that returns relevant results fast.
  • Design mobile-first. More of your traffic is on a phone than you think, and retrofitting desktop layouts down to mobile always shows.
  • Get the technical basics flawless — no layout shift, no broken states, no spinner that never resolves.

Don't:

  • Don't bury the primary action three menus deep because it looked cleaner in Figma.
  • Don't assume "visually delightful" means "add more animation." Delight usually means the thing worked the way the user expected, instantly.

The Part Everyone Skips: Who Are You Even Building This For?

Here's the uncomfortable truth. You can nail every performance metric above and still lose, because none of it matters if you're fast for the wrong audience.

Before you touch another optimization, do this:

  1. Identify your top competitors and actually read how they talk to people — their blog tone, their ad copy, their social captions. Then go read their customer reviews. The complaints in those reviews are basically a free list of what your audience wants that isn't being delivered.

  2. Segment your audience properly, not just by age and location:

    • Demographics — age, gender identity, income, profession
    • Geographics — location, language, climate if it's relevant to what you sell
    • Psychographics — values, hobbies, lifestyle, daily habits
    • Behaviors — how they discover brands, how they use tech, what actually triggers them to buy
  3. Build 2–3 real buyer personas out of that research. Give them names, a goal, and a specific frustration. Then run every new feature or article through one question: would this specific person find value in this?

This is the piece an AI performance tool can't do for you. A model can compress your images. It can't tell you that your 45-year-old B2B buyer persona doesn't care about your Instagram-style micro-animations — they care about finding pricing information in under 10 seconds.

Speed, UX, and audience targeting aren't three separate projects. They're the same project, approached from three angles: your architecture, your user's psychology, and how you deliver resources to match both.

Part 2: Backend Performance — Without Touching a Single Line of App Code

I’ve seen apps feel slow because of one missing database index. Before rewriting a component, I check the query plan first.

Cache the right things, the right way

Put an in-memory store like Redis in front of your primary database for frequent read queries, and cache entire JSON responses on your read-heavy REST or GraphQL endpoints.

But — do NOT use Redis for highly dynamic, personalized pricing. If a price changes every few seconds based on location, demand, or account tier, caching every price variation will chew through your server RAM fast. Calculate that kind of price live, straight from an indexed DB lookup.

If you still want caching on something like a product catalog page, use the cache-aside pattern with eviction:

  1. Admin updates the price in MySQL.
  2. Backend deletes that product's key from Redis.
  3. Next customer request hits Redis, gets a miss, fetches from MySQL, and repopulates Redis.

Fix your indexes — this is the #1 speed win, full stop

SQL (MySQL / SQL Server):
Run EXPLAIN on your slow queries. Anywhere you see type: ALL, that's a full table scan. Add B-Tree indexes on your WHERE, JOIN, and ORDER BY columns. If you're filtering on multiple columns at once, use a composite index and order the fields most-specific first.

MongoDB:
Run .explain("executionStats") and look for COLLSCAN — that's your collection scan warning sign. Create single or compound indexes with db.collection.createIndex().

Part 3: The Security Rules Nobody Reads Until Something Breaks

Everyone's rushing to ship faster with AI-generated boilerplate, and a lot of that boilerplate quietly skips security defaults that used to be common sense. Here's what to actually avoid, stack by stack.

OAuth 2.0 / OIDC (Authentication)

  • Don't use the Implicit Flow for your React SPA. It returns access tokens directly in the browser's URL hash, which leaves them exposed in browser history and to malicious extensions. Use Authorization Code Flow with PKCE instead.
  • Don't store long-lived JWTs or refresh tokens in localStorage. They're wide open to XSS attacks. Store them in HttpOnly, Secure, SameSite=Strict cookies instead.

WebSockets (Real-time features)

  • Don't skip authentication on the handshake. If anyone can open a socket connection without proving who they are during the initial HTTP upgrade, you've handed attackers an easy way to DoS your event loop.
  • Don't trust the payload just because the user is authenticated. Authenticated doesn't mean safe — validate the schema and check permissions before you broadcast any incoming socket message to other users.

Node.js Event Loop (Performance under load)

  • Don't run heavy CPU work on the main thread. Blocking crypto operations, huge array loops, JSON.parse() on a 50MB file — any of these freeze your single-threaded event loop and every other incoming request stalls with it. Offload to Worker Threads.
  • Don't mix async/await with sync file methods. fs.readFileSync() or fs.writeFileSync() inside a route handler blocks execution for everyone. Use the promises API — fs.promises.readFile() — instead.

Amazon S3 (Storage)

  • Don't disable "Block Public Access" unless you're deliberately hosting a public static site. Turning it off is how sensitive files end up getting picked up by automated scanners.
  • Don't use S3 as your query database. Repeatedly fetching and parsing large JSON/CSV files from S3 on every request will hit you with API limits and latency. Ingest that data into MySQL or MongoDB instead, and query it properly.

The Actual Point

None of this is complicated in isolation — cache the hot paths, index the slow queries, don't leave tokens in localStorage, don't skip your buyer personas. What's easy to miss is that speed and security fixes get all the attention right now because AI tools make them fast to apply, while the audience research step — the one that decides whether any of it even matters — still takes real, slow, human thinking.

Ship the fast site. Just make sure you know who it's fast for first.


This is the list I wish someone had handed me years ago instead of letting me learn each rule the hard way. If you've got your own "don't do this" rule from experience, drop it in the comments — always curious what other people's war stories add to the list.

Top comments (0)