Scaling pain: client-side vs server-side filtering (déjà vu from 40 years of building apps)

Over the past few weeks adding new cities to LocalMusicX.com, I ran into a scaling issue that felt very familiar — one of those “I’ve seen this movie before” moments. It took a few hours of back and forth with the Agent, but we got to the root cause.

At small scale, everything worked great. We were loading event data and filtering it client-side by city. Fast, simple, no issues.

Then we started adding more cities and events.

And things… slowed down. Then degraded. Then basically hit a wall.

The root cause was classic:
:backhand_index_pointing_right: the backend was returning all events for all cities
:backhand_index_pointing_right: the browser was doing the filtering

So even if a user just wanted to return 20 events for “Bellingham tonight,” we were sending the entire dataset of thousands of events over the wire.

It worked beautifully… until it didn’t.

What’s interesting is how subtle this failure mode is:

  • performance degrades gradually

  • nothing obviously “breaks” at first

  • easy to miss until you hit a tipping point

Then suddenly you’re debugging under pressure with a live site struggling.

The fix, of course, was straightforward once identified:
:backhand_index_pointing_right: move filtering to the server
:backhand_index_pointing_right: return only relevant data per request

Now the architecture scales properly — performance stays flat as we add more cities.

What struck me most is how timeless this pattern is. I’ve hit this same issue multiple times over decades of development, just with different tech stacks.

Curious how others here approach this tradeoff:

  • Do you default to server-side filtering early?

  • Any heuristics for knowing when client-side filtering is “safe”?

  • Have you run into similar slow-burn scaling failures?

Would love to hear war stories.

1 Like

Great writeup, this is one of those patterns that bites everyone eventually. Moving filtering server-side is the right first move, but I’d start thinking about caching now before you hit the next wall.

Think about the access patterns for a local music events site. People in Bellingham are probably searching for the same handful of queries.. “tonight,” “this weekend,” maybe filtered by genre. That’s highly cacheable data. Events don’t change every second, new ones get added maybe a few times a day, so even a short TTL of 5-10 minutes would dramatically cut your database load while keeping things fresh enough.

I run a lab-grown diamond marketplace with 600k+ live listings and hit the exact same progression you’re describing. Client-side worked great early on, then we scaled up and everything slowed down when real traffic started. Server-side filtering fixed the immediate pain, but the real unlock was multi-level caching. People search by shape, carat, price range and a relatively predictable set of combos get hit over and over. Caching those results means most queries never touch the database at all.

For your setup I’d look at a few things:

  1. Response caching for your most common city + filter combos. If “Austin tonight” gets hit 500 times a day, that result doesn’t need to be computed fresh every time.

  2. A short TTL so new events still show up in a reasonable window. 5-10 minutes is usually the sweet spot for event data. Short enough that nothing feels stale, long enough to absorb the repeated traffic.

  3. Cache invalidation when new events are added. If you’re ingesting events on a schedule you can clear the relevant city caches at the same time so fresh data shows up immediately instead of waiting for TTL expiry.

  4. Consider precomputing your highest traffic pages. If you know which cities get the most hits, build those responses on a schedule and serve them cold.

Server-side filtering reduces the payload but caching reduces the work entirely. You’ll be glad you have it in place before the next set of cities goes live.

To answer your questions directly, yes I default to server-side filtering early now, learned that one the hard way more than once. My rule of thumb is if the dataset can grow independent of the user’s request (more cities, more listings, more events over time) it needs to be filtered server-side from day one. Client-side filtering is only safe when the full dataset is bounded and small, like filtering a user’s own saved items or a short dropdown list.

1 Like

Great idea! The event scraping for each city is scheduled so it would be super easy to cache today’s events for a given established city when someone searches it. Then we can clear the cache the next time the city is scraped. That would also mean that the just-in-time scraping for unestablished cities could be pumping new events into the events table without competing with continuous city event queries triggered by users checking where they can go hear some music tonight.

2 Likes

Yeah I would try that out, let me know how it goes!

1 Like