Stop Optimizing Before You Have Data
Cover photo by Chris Ried on Unsplash
Optimizing code before you have a performance problem is a great way to waste time. You’re effectively guessing what the bottleneck will be. I’ve been there, and I’ve seen teams spend weeks architecting for scale that never actually arrives.
The caching trap
I often see developers start with complex caching layers immediately. They think, I’ll just put Redis in front of this API now to save future me the trouble. Here is what that often looks like:
// Premature optimizationconst getUser = async (id) => { const cached = await redis.get(`user:${id}`); if (cached) return JSON.parse(cached); const user = await db.users.findUnique({ where: { id } }); await redis.set(`user:${id}`, JSON.stringify(user), 'EX', 3600); return user;};This adds cache invalidation logic, error handling for the Redis connection, and serialization overhead. If the database query is actually fast, you just added maintenance burden for zero gain. A simpler approach is to write the direct query first. If monitoring reveals a latency spike later, then you cache.
The abstraction tax
Sometimes we try to write generic, high-performance abstractions because we think it’s better engineering. “But what if we need to swap the database driver later?” That’s the most common objection I hear. It’s a fair concern, but usually, that day never comes (or it’s way easier to just refactor later than to maintain a generic interface for five years).
Consider a simple data retrieval:
Approach A (Direct):
const getProducts = () => db.products.findMany();Approach B (Abstractions everywhere):
const getProducts = () => repository.findAll('products');Approach B hides the actual implementation. You lose IDE navigation, type safety, and debugging clarity. I’d avoid the repository pattern unless you actually have multiple data sources. It adds friction without providing any actual value for 99 percent of standard web apps.
When is optimization okay?
I should be clear. If you are building a system that processes millions of events per second, you don’t have the luxury of waiting. You need to think about concurrency and memory allocation from day one. That’s a valid exception to the rule.
However, most of us aren’t building the next Kafka. We’re building CRUD apps. In that world, readability and simplicity win almost every time.
How to actually move fast
Don’t build for hypothetical scale. Measure your endpoints. Use standard monitoring tools. If a function takes 50ms, is that worth optimizing? Probably not. If it takes 5 seconds, fix the query, not the architecture. Keep your code flat, readable, and boring. You’ll thank yourself when you have to fix a bug in three months.
What performance metric do you actually track before you decide to optimize?