Skip to main content

Web application performance

Is your web app slow? Don’t buy a bigger server yet

By Kashif Abbas Kazmi · Code Huddle · Product engineering guides

A slow web application is not necessarily short of processing power. Delays can come from database queries, external services, network round trips, or work in the browser. Measure where the time goes before upgrading hosting. A larger server can still spend much of each request waiting.

1. Measure one slow journey before changing anything

"The app is slow" is not something a team can fix. "The orders page takes six seconds to load for a sales manager with two years of data" is. Choose the journey people complain about most, such as search results, a dashboard, checkout or an admin report. Record how long it takes, for which user, and with how much data.

Split that time into its parts. The Network panel in browser developer tools shows how long the browser waited for the server separately from download and rendering time. If the server responds quickly but the page still feels slow, inspect downloads, JavaScript execution, rendering, and third-party scripts. More server CPU may not help with those delays.

Look at the slowest requests, not only the average. An average of 400 milliseconds can hide a significant share of requests that take several seconds. For public pages, Google's Core Web Vitals give useful reference points: a Largest Contentful Paint of 2.5 seconds or less, an Interaction to Next Paint of 200 milliseconds or less, and a Cumulative Layout Shift of 0.1 or less, measured at the 75th percentile of real visits and assessed separately for mobile and desktop. These measure user experience; API response times still need separate monitoring.

  • Which page or action is slow?
  • Which user, role or account sees the delay?
  • How much data does that account have?
  • How long does it take today, and how long should it take?

2. Count the round trips inside each request

When the database is accessed over a network, each separate query can add a round trip. When the application and database run in different regions or with different providers, each trip can take tens of milliseconds before any work begins. For illustration, 40 sequential queries with 25 milliseconds of network delay per query add about one second before query execution time is included. This is an example calculation, not a measured result from a Code Huddle project. A bigger server does not shorten that distance.

A common cause is the "N+1" pattern. An admin list loads 50 orders, then runs a separate query for each order's customer, and another for its payment status. One page becomes 101 queries. A suitable join or a small number of batched queries can reduce those round trips. Check the resulting query plan and response size; a large join is not automatically faster.

Ask the team to log the number of queries per request alongside its duration. Independent queries can run in parallel, related data can be loaded together, and the application and database should normally sit in the same region. These changes often matter more than the hosting plan.

3. Check the database before the server

A selective filter without a suitable index can make the database scan far more rows than it returns. A sequential scan can still be the right plan for a small table or a query that needs much of the data. That may be invisible with a thousand records and painful with a million. Tools such as EXPLAIN ANALYZE in PostgreSQL or EXPLAIN in MySQL show how a query is executed, and a slow-query log identifies which queries deserve attention first. Indexes also add work to every write, so add them for real query patterns rather than for every column.

EXPLAIN ANALYZE actually executes the statement. Start with read-only plans and controlled measurements; commands that change data require a safe environment and appropriate transaction handling. Investigate expensive queries with realistic data and without exposing customer information.

Connection limits are a less obvious constraint. A database accepts a limited number of simultaneous connections. Check the configured limit and any reserved connections; managed providers may impose additional plan or proxy limits. Each application instance usually holds its own pool of connections, so adding more instances or serverless functions can exhaust the limit. Requests then queue or fail, and scaling out has made the problem worse.

Size connection pools against the database's limit, and consider a connection pooler such as PgBouncer or the provider's own proxy when many processes share one database. Check this before approving an increase in instance count.

4. Send less data and do less while the user waits

Large responses are slow on any server. Common examples include an API that returns every field and nested relation when the screen shows four columns, an admin table that downloads 10,000 rows so the browser can filter them, and full-size images displayed as thumbnails. Paginate and filter on the server, select only the fields the screen needs, compress responses, and resize images before delivery.

Some work does not need to finish before the user sees a response. Emails, PDF reports, exports, and upload processing are often candidates for background jobs. Payment and AI workflows need an explicit decision about which steps can be deferred. Confirm only that a job has been accepted until the operation succeeds; do not tell a user a payment or booking is complete before required checks finish. Use durable queues, safe retries, duplicate handling, and a visible failure state. Set timeouts on external calls and define what happens on failure. A timeout does not prove that a provider stopped processing the operation, so check its status before retrying a payment or another action with side effects.

  • Unpaginated lists and tables.
  • Responses that include unused fields or relations.
  • Exports and reports generated during the request.
  • External API calls without timeouts or fallbacks.
  • Images and files served at their original size.

5. Add caching deliberately, not as the first fix

A cache can hide a slow query, but it introduces new questions: how long data may stay out of date, what clears it, and what happens when it is empty. Decide which information can safely be stale. Prices, permissions and stock levels usually have stricter requirements than a list of blog categories.

Cached responses must respect access rules. In a multi-tenant product, a cache key that ignores the account or user can show one customer's data to another. Avoid storing personalized responses in a shared CDN cache unless the configuration clearly separates them. Understand and fix the underlying query first, then cache where measurement shows a clear benefit.

6. Know when a bigger server is the right answer

Hosting upgrades are sometimes the correct decision. Once queries, round trips and payloads are reasonable, look for sustained processor usage near its limit during normal traffic, memory pressure that causes swapping or restarts, a database whose frequently used data no longer fits in memory, or traffic that has grown. In those cases, more capacity is justified.

Treat the upgrade like any other change. State the expected improvement for a specific journey, measure the same journey with the same data afterwards, and record the additional monthly cost. If the numbers do not move, the bottleneck was somewhere else.

Common mistakes to avoid

Many performance problems survive because the testing conditions hide them. A developer laptop with the database on the same machine has almost no network delay and a small dataset, so a page that takes 200 milliseconds locally can take several seconds in production.

  • Testing only on a local machine with sample data.
  • Judging performance by the average response time.
  • Adding application instances without checking database connection limits.
  • Adding a cache before measuring the slow query.
  • Upgrading hosting without measuring the result.
  • Treating every slow page as the same problem.

What to share when requesting a performance review

Describe the slow journeys, when they happen, current timings if you have them, where the application and database are hosted, approximate traffic and data volumes, and any recent changes. Provide access to monitoring, logs and the database through an agreed process with named owners.

Ask the reviewing team for findings ranked by expected impact, with a before and after measurement for each change. Code Huddle's ParkingHub case study describes a multi-tenant platform where many customers share one product. It illustrates the kind of environment in which query design and connection use matter; it is not presented here as a performance benchmark. A targeted diagnosis should come before decisions about a hosting upgrade, a migration or a rewrite.

Sources and further reading