Performance analysis: the checklist I use before suggesting a rewrite
Before approving an expensive rewrite, go through this checklist. Six checks that, in most cases, find the bottleneck without changing a line of language.
When someone says "the system is slow, let's rewrite it," the rewrite is rarely the right answer — it is the most expensive and the riskiest. Before approving months of work and the risk of a cutover, I go through a checklist. In the vast majority of cases, it finds three to five concrete causes that explain the bulk of the problem, and none of them require switching languages.
1. Are the indexes being used on the slowest queries?
First stop, always, because it is where most bottlenecks live. Take the slowest queries (the database slow query log already hands them to you) and run EXPLAIN ANALYZE. Look for Seq Scan on large tables — it means the database is reading the whole table for lack of an index. An index in the right place can turn a query from seconds into milliseconds. Watch out too for indexes that exist but are not used because the query does not match the indexed column.
2. Is there an N+1 in the ORM?
The second most common suspect, and the most treacherous because the ORM hides it. Listing 100 items and, for each one, fetching a relationship fires 101 queries instead of two. Log the query count per request. If a simple page fires dozens or hundreds of queries, you found the problem. The fix — eager loading, a join, or a batch — is usually a few lines and returns a huge gain.
3. Is session and auth caching working?
Every authenticated request that hits the database to validate the session and permissions on each call is paying an unnecessary cost. Check whether the session, auth data, and permissions are cached (Redis, for example) with correct invalidation. In high-traffic systems, this is one of the fastest wins: relieving the database of repeated auth load.
4. Are the API payloads bloated?
APIs that return huge objects — fields nobody uses, deeply nested relationships, lists with no pagination — spend time serializing, bandwidth transmitting, and time on the client deserializing. Check the response size of the most-used routes. Pagination, field selection, and compression (gzip/brotli) frequently cut response time without touching the logic.
5. Is the connection pool well configured?
A pool that is too small makes requests wait for a free connection — latency rises without CPU rising, because the process is just waiting. A pool that is too large overloads the database. Check the pool size against real concurrency and monitor connections in use versus available. This is one of those one-line tweaks that, in the right context, changes the behavior under load.
6. Are the assets compressed and behind a CDN?
For the web part, before blaming the backend: are the static assets (JS, CSS, images) compressed, with correct cache headers, and served by a CDN? A high LCP is often heavy, poorly cached assets, not a slow API. WebP/AVIF images, a split bundle, and a CDN solve much of the Core Web Vitals problem without rewriting anything.
How to use the checklist
The rule is to measure, not guess. Each item above should produce a number — before and after. Fix the ones that show up, measure again, and repeat. Almost always, three to five of these six points explain 80% of the slowness, and fixing them returns most of the performance for a fraction of the cost of a rewrite.
It is worth recalling the classic case: a client wanted to rewrite the sales-reporting module of a retail SaaS in Go because of slowness that grew with the report range and bled into inserts; the problem was index fragmentation from random UUIDs, fixed with a migration to UUID v7 across distributed databases with billions of records — +260% throughput, zero lines of language changed. The rewrite would have cost months and would not have touched the cause.
When, then, do you rewrite?
There are legitimate cases: an obsolete stack with no security support, an architecture so coupled it prevents incremental optimization, or a pure CPU-bound bottleneck the language really limits. But even in those, the checklist comes first — because it turns "I think we need to rewrite" into "we know exactly where the problem is and why the rewrite is (or is not) the answer." Diagnosis before surgery, always.