Skip to content
ALI HAITHAM·TECH·responds in <1h
  • Homestart here
  • Workcase studies, projects
  • Serviceswhat I will build for you
  • Writingessays, series
  • Traincourses, labs, cohorts
  • Aboutthe engineer behind this site
Sign inStart a project
ALI HAITHAM · TECH
  • Home↗
  • Work↗
  • Services↗
  • Writing↗
  • Train↗
  • About↗
Sign inStart a project
Online
ALI HAITHAM·TECH

Engineering studio. Damascus, GMT+3.

aliyosef.online

Studio

  • Work
  • Writing
  • Training
  • About
  • Contact me

Portal

  • Sign in
  • Open a ticket
  • Track project
  • My account

Resources

  • Docs
  • Status
  • Changelog
  • Brand kit
  • Privacy
  • Terms

Newsletter

Field notes and tech news. Weekly. No fluff.

Free. Unsubscribe anytime.

Find me elsewhere
© 2026 Ali Haitham Yosef. All rights reserved.Hand-built in React 19. No frameworks of frameworks.Last deployed · 2026-05-08All systems operational
  1. Home/
  2. Writing/
  3. Performance
Performance

The 200ms target on bad networks

What you can guarantee on 2G, what you cannot, and why most performance budgets are wishful thinking.

January 2, 2026·12 min·by Ali Yosef

Loading essay…

ShareXLinkedInRSS
Get the next essay in your inbox.New posts every Sunday morning. Engineering, in plain language. Free, unsubscribe anytime.Subscribe→
AY
Ali Haitham Yosef

Engineer and writer. Builder of multi-tenant platforms, sync engines, and quietly reliable systems. 600+ shipped projects since 2015.

SubscribeStart a project→

Read next

  • IS
    Dec 22, 2025·Performance

    Indexing strategies for analytical queries

    When the right index makes the query 200× faster, and when adding an index makes everything worse.

    10 min
  • MD
    Dec 14, 2025·Performance

    Why we measure on real devices

    A four-year-old Android phone tells the truth. Your dev machine tells you what you want to hear.

    6 min
  • CS
    Dec 6, 2025·Performance

    The forgotten cost of connection setup

    TLS, DNS, TCP. The hidden 600ms before any of your code runs. Where to claw it back.

    7 min
←Back to all essays

"User actions feel instant when they complete in under 200 milliseconds." This number gets quoted in every performance writeup. What nobody tells you is that on real consumer networks, 200 ms is gone before your code runs. Here is what you can guarantee, what you cannot, and why most performance budgets are wishful thinking.

Where the 200 ms goes before you start#

Boot up a 4G phone with a typical (not best-case) cell signal, issue a request to a server in a different country, measure where the time lives:

TLS handshake (cold)        180 ms
DNS lookup (cold)            70 ms
TCP three-way                40 ms (rural cellular)
HTTP request body            ~5 ms
Server processing            varies
Response body                ~10 ms (small)
Render parse + paint         ~16 ms (one frame)

That is ~315 ms before your code does anything, on a cold connection. Even a warm connection (TLS resumption, DNS cached, TCP held open) costs 60-90 ms of round-trip on real cellular. The 200 ms budget is gone before the request reaches your server.

This is not a tale of woe. It is a precondition. Performance work on networked actions is mostly about avoiding the round-trip, not about speeding it up.

What "feels instant" actually requires#

Three patterns produce instant-feeling actions on bad networks. None of them involve making the network faster.

1. Optimistic UI#

The action visibly takes effect before the round-trip. The user taps "send"; the message appears in the chat with a "sending" indicator; the round-trip resolves it to "sent" or "failed."

This works because the user's mental model is that they sent the message. The truth (the server hasn't acknowledged) is implementation detail. The "sending" indicator is the honest part: we tell them it is in flight; they trust the system to resolve it.

2. Local-first reads#

The data on the screen is read from a local database, not the network. The network is only used to keep the local database fresh, in the background, when convenient. We covered this pattern in the offline-first Flutter article; it is the structural reason apps can feel fast on bad networks.

3. Pre-fetching predictably-needed data#

If you know the user is about to need data X (because they are on the screen that lists Xs and the next obvious tap is "open one"), fetch it ahead of time. Latency hidden behind a tap that has not happened yet is latency that is not in the critical path.

The dangerous form of this pattern is "pre-fetch everything." It costs the user data plan and battery. Be specific: pre-fetch only the items that have a > 30 % click-through rate from the current screen.

What you can promise on bad networks#

Three SLOs we offer:

Metric4G typical2GNotes
Tap-to-acknowledged< 50 ms< 50 msOptimistic local update
Tap-to-server-confirmed< 1.2 s< 5 sRound-trip
Sync-after-reconnect< 8 s< 30 sCatch-up window
First meaningful paint< 800 ms< 1.5 sCold app start
First paint after bg→fg< 200 ms< 200 msLocal data, no round-trip

The "tap-to-acknowledged" row is the only one where 200 ms applies, and that is because it is local. The "tap-to-server-confirmed" row is realistic for a network round-trip and we do not pretend otherwise.

The honest framing for users: actions feel instant; sync is in the background; if something fails, we tell you, with a way to retry.

What you cannot promise#

Some things just are slow on bad networks. Be honest about them instead of papering over.

Anything that requires server-side computation before showing data#

A search across a tenant's full dataset. A report. A "what is the current invoice total." Round-trip cost is unavoidable. Show a skeleton, don't stall on a spinner.

Pages that load resources from third parties#

Maps. External authentication providers. Embedded video. Each one is its own DNS + TLS + request cycle, and they add up. If you can avoid the third party, do; if you can't, lazy-load below the fold and show the rest of the page.

Real-time anything during congestion#

Websockets, SSE, push notifications, all of these degrade when the network is congested. The fallback is not "make it work harder;" the fallback is polling on a longer interval and accepting the latency.

Performance budgets that lie

"All actions complete in under 200ms" is a budget that will fail at the first edge case and erode trust in the rest of the budget. Better budgets have shapes: "tap-to-acknowledged 200ms, tap-to-confirmed 1.2s, with explicit visible state in between." A budget that acknowledges the network is more truthful than one that ignores it.

Where we measure#

Two places:

Real User Monitoring (RUM)#

Every key action emits a single event with start/end timestamps and the network type at the time. We aggregate to p50 / p95 / p99 per network type. The interesting one is p95 on cellular; p50 on wifi flatters the numbers.

Slow-network simulation in CI#

Every PR runs a synthetic test against the staging server with network throttled to "slow 3G" via DevTools profile. Any action that takes longer than its budget on slow 3G blocks the PR. This catches regressions that look fine on a fast home network.

The boring conclusion#

Bad networks make the 200 ms target unreachable for any action that involves the network. The honest path forward is to design actions that do not involve the network on the critical path: optimistic local writes, local-first reads, predictive prefetch. Then state clearly what the actual round-trip budget is, and meet it.

Most apps are dishonest about their performance budgets because they were written for the home wifi of the engineering team. The first time you ship to a customer in a basement on 3G, you find out which budget was real.

The next post is on indexing strategies for analytical queries. Or browse the writing index for other directions.