Client-Server Architecture Trade-offs
Part of the Full Stack Developer Roadmap
In short
Every feature asks: should this logic run in the browser or on the server? Get it wrong one way and the app feels slow. Get it wrong the other way and users can cheat.
Deciding how much logic lives on the client vs the server — more client logic means a snappier UI but more exposed code; more server logic means better security and control but more round trips.

Think of it like this
Pani puri stall vs DIY chaat tray
At a pani puri stall, the bhaiya fills every puri himself. Slower, but he controls the masala. A DIY tray is instant, but you could fill ten puris with only aloo. Fast and trusting, or slower and in control.
Watch it happen
Where should this logic live?
In the browser
- Instant feedback, no round trip
- Code and data visible to anyone
- Varies with the user's phone
On the server
- Trusted: users can't edit it
- Costs a network round trip
- Same speed on every device
Put UX logic (filters, form hints, animations) in the browser. Put anything about money, permissions or secrets on the server, even if you also show it in the browser.
The big ideas
What you need to know
The browser is public
Anyone can open DevTools, read your JavaScript and change any request. Never trust a price, role or discount sent by the client.
Round trips cost time
Each call to the server can add 100 ms or more on Indian mobile networks. Validate simple things in the browser too.
Do both when it matters
Check a form in the browser for speed, then check it again on the server for safety. Share the rules if you can.
Think about weak phones
Heavy work in the browser drains a budget phone. Big data crunching belongs on the server.
See it in code
Walk through the code
1// Client: a quick estimate so the cart feels instant2export function CartTotal({ items }: { items: { price: number; qty: number }[] }) {3 const estimate = items.reduce((sum, i) => sum + i.price * i.qty, 0);4 return <p>Approx. ₹{estimate}</p>;5}6 7// Server: the real total, using prices from the database8app.post("/api/checkout", async (req, res) => {9 const ids = req.body.items.map((i: { id: string }) => i.id);10 const products = await db.product.findMany({ where: { id: { in: ids } } });11 const total = req.body.items.reduce((sum: number, i: { id: string; qty: number }) => {12 const p = products.find((x) => x.id === i.id);13 return sum + (p ? p.price * i.qty : 0);14 }, 0);15 res.json({ total });16});1/3Client-side maths for a snappy UI. Fine to show, never fine to charge from.
The browser shows a fast estimate. The server ignores any price the browser sends and recalculates from the database, so editing the request gains nothing.
Save yourself the pain
Common mistakes
Oops
Hiding the admin button in React and calling that security.
Do this instead
Hide it for neatness, but check the user's role on the server for every admin API call.
Oops
Sending the whole table to the browser and filtering there, because it's 'easier'.
Do this instead
Filter and paginate on the server. Send only what the screen shows, and nothing private.
Your mission
Draw the trust line
Quick check
A coupon code is checked only in your React code before calling /api/order. What's the risk?
Go deeper
- 1List a few features and decide where each piece of logic should live and why.
- 2Consider what should never be trusted to run only on the client (pricing calculations, permissions).
- 3Recognize this trade-off shifts with frameworks like Next.js that blend the two.
More topics at this stop
Stuck on this topic? Ask an Insider
Get 1:1 guidance from people who've walked this exact path — on the InsideEdge app.