Your app works. It's just slow now. Pages that loaded instantly take seconds. The database is the bottleneck — and the reason is almost always one of five predictable problems. I find the actual cause and fix it, rather than guessing at configuration changes.
Database performance doesn't degrade gradually. It falls off a cliff. A query that scans 1,000 rows feels instant. The same query scanning 1,000,000 rows is a different experience entirely. Here's what's actually happening underneath.
Without an index, the database reads every single row to find what you asked for. On a small table that's fine. On a large one, it's the single biggest cause of slowness — and the easiest to fix.
One page render triggers one query for a list, then a separate query for each item's related data. For 100 items, that's 101 queries. Each is fast — the total is not.
Opening a new database connection per request is expensive. Under load, connections pile up, the pool runs dry, and requests queue or fail. Often the first thing to fix when traffic grows.
Every page reload queries the database again, even for data that rarely changes. A cache layer between the app and the database absorbs the repetitive reads.
Complex reports, aggregations and calculations run inside the user's request. The user waits for work that could happen in the background.
Sub-queries that run per row, unnecessary joins, fetching columns you don't use, or sorting in code instead of the database. Small choices that multiply at scale.
This is the single most common serious performance bug in data-backed applications. It's invisible in small tests and devastating in production.
A simple list page — say, recent posts with their author names — written two ways.
// 1 query: fetch 100 posts
posts = findRecent(100);
// Then 100 more — one per post
for (post of posts) {
author = findAuthor(post.authorId);
render(post.title, author.name);
}
// 1 query: fetch posts + authors together
posts = findRecent(100)
.include('author');
for (post of posts) {
render(post.title, post.author.name);
}
The N+1 pattern doesn't show up in slow query logs — every individual query is fast. It only reveals itself when you count the total queries per page render. With 1,000 items, the difference is the gap between a page that loads in milliseconds and one that takes several seconds.
Every fix starts with a diagnostic. I don't change configuration and hope. I find the actual bottleneck, then fix it.
Using slow query logs, EXPLAIN ANALYZE and application metrics, I identify exactly which queries are slow, how the database is executing them, and where the time is actually going.
Indexes on the columns you filter, sort and join on most frequently. No over-indexing — that slows down writes. Focused, tested indexes that fix real bottlenecks.
Rewriting loops that trigger queries per iteration to use eager loading. Usually a small code change with a dramatic performance gain.
PgBouncer for PostgreSQL or ProxySQL for MySQL, configured so your app reuses a small set of connections instead of opening one per request. Critical for apps under load.
Redis or Memcached in front of the database for data that doesn't change often. Reduces database load and improves response times for read-heavy pages.
Replacing expensive nested queries, unnecessary joins and inefficient patterns with faster equivalents. Verified with EXPLAIN ANALYZE before and after.
Diagnose first. Fix second. Measure third. No configuration guesswork.
A conversation about when the app got slow, how much data is involved, and what symptoms you see. I need read-only access to your database and app to begin.
Slow query logs, application performance monitoring data, and EXPLAIN ANALYZE on the specific queries users complain about. This is where the real cause is identified — not guessed.
You receive a written plan: which queries or patterns are causing the problem, what the fix is for each, and roughly how much improvement to expect. Broken into milestones so you can approve stage by stage if you prefer.
Indexes created, N+1 patterns rewritten, connection pooling configured, caching added where it matters. Each change is tested against the running app — not deployed blindly.
Every fix is measured. Query execution time, page load time, database load. You see the actual improvement, not an assurance that it's "better now."
Notes on what was changed and why, plus guidance on what to watch as the app grows. So future changes don't accidentally undo what we fixed.
Database performance is diagnostic work. It requires someone who reads query plans and understands what the database is actually doing — not someone who changes settings and hopes.
React.js, Node.js, PostgreSQL, REST APIs. I build and maintain apps in production — so I understand the database problems that show up in real systems.
Real apps with real data. I've watched queries go from fast to slow and traced exactly why. The patterns are consistent and recognisable.
Since 2015. Running a business means understanding that slow apps lose customers, orders and money. Performance is not academic — it's commercial.
Many of my performance fixes are on AI-built apps. AI tools write queries that work correctly but ignore efficiency — missing indexes, N+1 patterns, unoptimised joins. I know where to look.
"Our dashboard was taking 12 seconds to load. Emmanuel added three indexes and fixed an N+1 pattern. It's now under a second."
"He showed us the EXPLAIN ANALYZE output and explained exactly what was happening. That alone gave us confidence the fix was real, not guesswork."
"Connection pooling was the fix. We'd been throwing more RAM at the problem for months."
Because test data is small. A query that scans 100 rows is instant. The same query scanning 100,000 rows is not. This is the most common reason apps feel fast during development and crawl in production — the database is doing the same work, but on exponentially more data. The fix is usually proper indexing, query optimisation and eliminating N+1 query patterns.
An N+1 query problem happens when your code fetches a list of items (1 query), then loops through that list and makes a separate query for each item's related data (N queries). What looks like one page render becomes 101 database round trips for 100 items. Each individual query is fast, so slow query logs never catch it — but the cumulative effect is severe. The fix is eager loading: fetching the related data in the original query instead of in a loop.
In PostgreSQL, the EXPLAIN ANALYZE command shows exactly how the database executes a query — whether it's scanning the whole table or using an index, how many rows it processes, and how long each step takes. Combined with slow query logs and application performance monitoring, this tells you precisely which queries to fix. I start every performance fix with this diagnostic step, not with guessing.
No — and doing so will actually slow your app down. Indexes speed up reads but slow down writes, because every insert, update and delete has to update the index too. The right approach is to index the columns you filter, sort or join on most frequently. Indexing every column wastes storage, slows writes and confuses the query planner. A focused set of indexes on the right columns makes a huge difference; indexing everything makes things worse.
Every database connection consumes memory and setup time. If your app opens a new connection for every request, you exhaust the database's connection limit quickly — and performance collapses under load. Connection pooling (PgBouncer for PostgreSQL, ProxySQL for MySQL) reuses a small set of connections across many requests, dramatically reducing overhead. This is often the first thing I fix when an app that works with 10 users struggles with 100.
It depends on the cause. Adding missing indexes takes hours. Fixing N+1 queries is often a matter of changing a few lines of code in an ORM. Connection pooling setup is quick. Larger architectural issues — like moving heavy queries to background jobs or adding a cache layer — take longer. I start with a diagnostic to identify the actual bottleneck, then quote the fix as a clear project with milestones.
Tell me what you're seeing: which pages are slow, how much data is involved, and when the slowdown started. I'll tell you honestly what I'd check and what the fix likely involves.
PostgreSQL · MySQL · Diagnostic first · Nigeria-based, worldwide service