Interactive roadmap

    Backend Performance Best Practices roadmap

    Designed for backend developers who want to speed up their apps and handle more users smoothly without crashes.

    Your progress0% · 0 of 15 topics
    Done Learning Skipped OptionalClick any topic to learn more and mark your progress.
    1. 1

      Measure First

      0 of 3 done

      Always find the real bottlenecks before changing any code.

      Always gather hard data before changing code, or you risk wasting time solving the wrong problems.
    2. 2

      Database Queries and Indexes

      0 of 3 done

      Make data fetching fast and efficient.

      Databases are usually the first bottleneck in web apps, so keep your queries lean and indexed.
    3. 3

      Smart Caching Strategies

      0 of 3 done

      Store frequent results to skip repeated heavy work.

      The fastest request is the one your database never has to process, so cache stable data sensibly.
    4. 4

      Async Work and Queues

      0 of 3 done

      Move slow tasks out of the main request loop.

      Free up your web servers by handling heavy, non-critical computations in the background.
    5. 5

      Pools and Payloads

      0 of 3 done

      Manage connections well and send less data over the wire.

      Save precious milliseconds by keeping network connections warm and responses strictly trimmed.

    Learn it all, with a tutor that never sleeps

    Every topic links to LevelUp lessons, practice exercises and the 24/7 AI Tutor. Your progress updates as you master lessons.

    Every topic explained

    Step 1: Measure First

    Profilers show you exactly which lines of code take the most time to run.
    Read more

    A profiler records your program as it runs, measuring exactly how much CPU time and memory each function uses.

    Think of it like an itemised receipt from a garage repair. Instead of guessing why the bill is high, you see that one specific part took three hours of labour. In backend code, a profiler highlights the exact loop or serialiser slowing down your API response.

    Without profiling, developers often rewrite perfectly fine code based on hunches. Measuring first ensures you fix the genuine bottlenecks.

    • Profilers track CPU usage, memory allocation, and execution time per function.
    • They reveal non-obvious bottlenecks in your backend logic.
    • Flame graphs visually represent where your application spends time.
    • Profile in an environment similar to production for accurate results.
    Application performance monitoring tracks live server health and slow requests in real time.
    Read more

    Application Performance Monitoring, or APM, runs inside your live application to trace user requests from start to finish.

    Imagine a parcel tracking system that logs when a package leaves the depot, enters a sorting hub, and arrives at your door. An APM tool shows the journey of a web request, recording how long it spent in your route handler, your database queries, and third-party APIs.

    This matters because issues often appear only under real production conditions with varied data.

    • APMs capture traces across web frameworks, databases, and external calls.
    • They alert your team automatically when response times spike.
    • Distributed tracing helps track requests across multiple microservices.
    • They surface error rates alongside raw response latency.
    (optional)
    Simulate heavy user traffic to see how your server behaves under pressure.
    Read more

    Load testing means using automated scripts to send hundreds or thousands of simultaneous requests to your backend.

    It is like testing a bridge by parking heavy lorries on it before opening it to public traffic. You learn where the bridge bends and at what point it might collapse. A load test shows whether your server runs out of memory, drops connections, or slows to a crawl when traffic spikes.

    Running these tests prevents embarrassing outages during product launches or peak shopping events.

    • Simulates real user behaviour at scale before real traffic arrives.
    • Helps establish baseline throughput and failure thresholds.
    • Reveals memory leaks that only trigger under prolonged heavy load.
    • Must be run against staging or isolated test environments.

    Step 2: Database Queries and Indexes

    Indexes let the database find rows instantly instead of scanning every single record.
    Read more

    A database index is a specialised data structure, usually a balanced tree, that lets the database find matching rows rapidly.

    Consider the index at the back of a thick textbook. Instead of reading all five hundred pages to find a term, you look up the word and jump straight to page seventy-two. Without an index, the database must perform a full table scan, checking every single record one by one.

    Adding the right indexes reduces query times from seconds to single-digit milliseconds.

    • Indexes speed up reads but slightly slow down writes and updates.
    • Add indexes on columns used in WHERE, JOIN, and ORDER BY clauses.
    • Compound indexes cover queries that filter on multiple columns together.
    • Avoid over-indexing, as each index consumes extra disk space and memory.
    Stop your code from running separate database queries inside loops by fetching data together.
    Read more

    The N plus one problem happens when your code fetches a list of records and then executes an extra query for every item in that list.

    Imagine ordering groceries and having the delivery driver bring your milk, then make a separate trip for your eggs, and another for your bread. It is vastly more efficient to fetch everything in one van load. In code, loading fifty users and then issuing fifty separate queries for their profiles cripples performance.

    Fixing this reduces database strain instantly.

    • Commonly caused by lazy loading in Object Relational Mapping tools.
    • Turns one simple page view into dozens of separate database calls.
    • Solved by eager loading related records using joins or batch queries.
    • Easy to detect by inspecting the query logs for repeating patterns.
    (optional)
    Use explain commands to see how your database executes your queries.
    Read more

    A query plan is the internal roadmap your database engine creates to retrieve the records you requested.

    By prefixing your SQL statement with EXPLAIN, the engine explains whether it will use an index, perform a table scan, or sort records in memory. It is like asking a route planner why it chose a particular motorway instead of country lanes.

    Reading query plans removes guesswork when optimising slow queries, showing you exactly where the engine struggles.

    • Use EXPLAIN or EXPLAIN ANALYZE to inspect query execution details.
    • Look out for sequential or full table scans on large tables.
    • Check the difference between estimated row counts and actual rows returned.
    • Use the output to decide if new indexes or rewritten queries are required.

    Step 3: Smart Caching Strategies

    Keep frequently accessed data in fast memory stores like Redis for instant retrieval.
    Read more

    In-memory caching stores frequently requested data in fast server RAM rather than reading it from disk or querying the database repeatedly.

    Think of keeping a phone number on a sticky note on your desk instead of looking it up in the phone directory every five minutes. Tools like Redis or Memcached hold this data in memory, serving it in microseconds.

    This protects your primary database from being hammered by identical queries, allowing your backend to handle far more concurrent users.

    • RAM access is several orders of magnitude faster than disk access.
    • Redis and Memcached are popular standalone in-memory key-value stores.
    • Best suited for read-heavy data that does not change every second.
    • Always set sensible expiration times to avoid exhausting memory.
    Clear or update cached items correctly when the underlying data changes.
    Read more

    Cache invalidation is the process of updating or removing cached data when the source of truth changes.

    If a shop changes the price of a jacket on its main system, the displayed price on the website must update immediately rather than showing yesterday's lower rate. If you fail to invalidate the cache, users see stale, incorrect information.

    Designing a clean invalidation strategy ensures high performance without sacrificing data accuracy across your application.

    • Common strategies include time-to-live expiration and explicit cache purges on updates.
    • Write-through caching updates the cache and database at the same time.
    • Stale data can lead to serious business logic errors.
    • Keep keys organised using consistent naming conventions for easy targeted purging.
    (optional)
    Tell browsers and proxies to save responses so they never reach your server.
    Read more

    HTTP caching uses standard response headers to tell browsers, content delivery networks, and reverse proxies how to store responses.

    It works like distributing printed flyers. Once a customer has a copy, they do not need to visit your shop just to read the opening hours again. Headers like Cache-Control and ETag allow clients to reuse previous responses directly or verify if anything changed with a tiny request.

    Leveraging HTTP caching keeps huge amounts of traffic away from your backend entirely.

    • Cache-Control headers define who can cache a response and for how long.
    • ETags let clients make conditional requests to check for modifications.
    • Public caches like CDNs can serve identical content to many users.
    • Never cache sensitive personal responses on shared public intermediaries.

    Step 4: Async Work and Queues

    Process slow tasks like sending emails or generating PDFs after you reply to the user.
    Read more

    Background workers are separate processes that execute long-running tasks independently from the web request-response cycle.

    Imagine ordering food at a counter. The cashier gives you a receipt immediately rather than making you wait twenty minutes while the chef cooks your meal. Similarly, your API should respond to a user right away and hand off slow tasks, like sending welcome emails, to a background worker.

    This keeps your API snappy and prevents requests from timing out.

    • Web servers stay responsive by offloading long operations.
    • Ideal for email delivery, image resizing, and report generation.
    • Workers run in separate processes and can scale independently.
    • Tasks are usually placed into a queue for workers to pick up.
    Use tools like RabbitMQ or Kafka to manage reliable task queues between services.
    Read more

    A message broker is middleware that accepts, stores, and routes messages between different services or background workers.

    Think of a sorting office at the Royal Mail. Senders drop letters into postboxes, the office sorts and buffers them safely, and post workers deliver them when ready. Even if the delivery van breaks down, the letters are safe inside the office until service resumes.

    Brokers like RabbitMQ ensure tasks are never lost, even during sudden traffic spikes or worker crashes.

    • Buffers incoming work so servers do not become overwhelmed.
    • Guarantees message durability and delivery across distributed systems.
    • Supports various patterns, including point-to-point queues and pub-sub topics.
    • Decouples producers of work from the consumers that process it.
    (optional)
    Let different parts of your system talk through asynchronous events rather than direct calls.
    Read more

    Event-driven architecture is a design pattern where services publish events when something happens, and other services react independently.

    Consider an orchestra. When the conductor raises the baton, every musician reacts according to their own sheet music, without the conductor whispering instructions to each player individually. When an order is placed, an OrderCreated event is emitted; payment, inventory, and shipping services handle it on their own terms.

    This prevents tight coupling and stops one slow service from blocking the whole system.

    • Services communicate by emitting and listening to business events.
    • Removes direct dependencies and synchronous HTTP chains between microservices.
    • Increases overall fault tolerance because failures are isolated.
    • Requires careful event schema design to avoid breaking consumers.

    Step 5: Pools and Payloads

    Reuse database connections instead of opening a brand new one for every single request.
    Read more

    A connection pool maintains a set of open, active database connections that application threads can borrow and return.

    Opening a fresh database connection requires multiple network round trips, handshakes, and authentication checks, much like signing in at a security desk every time you walk into a room. A pool acts like an open revolving door: the connection is already authenticated and waiting to be used.

    Connection pooling drastically reduces per-request latency and protects databases from connection limits.

    • Avoids the heavy CPU and network cost of opening new connections.
    • Limits the maximum concurrent connections to prevent database crashes.
    • Connections are returned to the pool once the query finishes.
    • Pool size should be tuned based on database capacity and server cores.
    Send only the fields your frontend actually needs and compress responses using gzip or brotli.
    Read more

    Reducing payload size means trimming the volume of data sent across the network by stripping unnecessary fields and compressing the output.

    Imagine posting a parcel filled mostly with air versus vacuum-packing the contents. Sending twenty unused database columns over mobile data slows down page rendering and drains the user's data allowance. Using compression algorithms like gzip or Brotli shrinks the text response significantly before transmission.

    Smaller payloads transfer faster, leading to a much smoother user experience.

    • Select only the specific database columns required by the user interface.
    • Enable gzip or Brotli compression middleware on your web server.
    • Minimise deeply nested JSON structures where flat objects will do.
    • Smaller responses result in faster mobile downloads and lower bandwidth costs.
    (optional)
    Break large lists into smaller chunks or use cursor pagination for endless scrolling.
    Read more

    Pagination splits large datasets into manageable pages or streams, returning only a small batch of records at a time.

    If you searched a library catalogue with ten thousand books, you would not want the librarian dumping all ten thousand volumes on your desk at once. You want to see the first twenty. Offset-based pagination skips rows, while cursor-based pagination uses a unique identifier to fetch the next set instantly.

    This keeps memory usage predictable and prevents the server from locking up on massive tables.

    • Never return unbounded lists of records from an API endpoint.
    • Offset pagination is simple but degrades in performance on large tables.
    • Cursor pagination scales consistently regardless of how deep you paginate.
    • Always enforce a reasonable default and maximum limit for page sizes.

    Related roadmaps