Design Paradigms
REST, GraphQL, gRPC, and WebSocket APIs — understand when and why to use each paradigm, their trade-offs, and how to make the right choice for your system.
Table of Contents
The Big Picture — What Are Design Paradigms?
A design paradigm is the communication style your API uses. Just like humans communicate differently depending on context — a formal letter, a quick text, a phone call, a live conversation — APIs have different paradigms optimized for different situations.
There is no single "best" paradigm. Each one exists because it solves a specific set of problems better than the others. The skill is knowing which one to reach for and why.
The Restaurant Analogy
REST is a fixed menu — you pick item #12, you get exactly item #12. Simple, predictable, everyone understands it. GraphQL is a custom order — you tell the chef exactly what you want: 'I want the steak, but only the sauce from dish #5, and the side from dish #8.' You get precisely what you asked for, nothing more. gRPC is kitchen-to-kitchen communication — two chefs in connected kitchens passing dishes through a high-speed window. They speak a compact, pre-agreed language (protobuf) that's fast but not meant for customers. WebSockets is a live conversation with the chef — the kitchen door stays open. The chef can call out 'Your appetizer is ready!' without you asking. Both sides talk whenever they want.
🔥 Key Insight
The paradigm you choose shapes everything — how clients consume your API, how you handle errors, how you cache, how you scale. It's not just a technical choice; it's an architectural decision that affects every team that touches the API.
High-Level Comparison
Before diving deep into each paradigm, here's the mental model. Think of two axes: flexibility vs performance, and request-response vs streaming.
| Paradigm | Model | Format | Best For | Complexity |
|---|---|---|---|---|
| REST | Request → Response | JSON (text) | CRUD APIs, public APIs, web apps | Low |
| GraphQL | Request → Response | JSON (text) | Complex UIs, multiple data sources | Medium |
| gRPC | Request → Response + Streaming | Protobuf (binary) | Microservices, internal APIs | High |
| WebSockets | Persistent bidirectional | Any (text/binary) | Real-time: chat, live data, gaming | High |
🔄 Request-Response Paradigms
- REST — client asks, server answers. One request, one response.
- GraphQL — client asks with a query, server answers with exactly the requested shape.
- gRPC (unary) — client sends a message, server sends a message back. Binary, fast.
⚡ Streaming / Persistent Paradigms
- gRPC streaming — server streams data to client, or both stream to each other.
- WebSockets — persistent connection, both sides push data anytime.
- SSE (Server-Sent Events) — server pushes to client over HTTP (one-way).
Need a simple CRUD API? → REST Need flexible queries for complex UIs? → GraphQL Need fast internal service communication? → gRPC Need real-time bidirectional data? → WebSockets Still unsure? Start with REST. It's the simplest, most understood, most cacheable option. Only move to another paradigm when REST's limitations hurt you.
REST — Deep Dive
REST (Representational State Transfer) is the most widely used API paradigm on the web. It treats everything as a resource identified by a URL, and uses standard HTTP methods to operate on those resources.
Core Principles
Statelessness
Every request contains all the information the server needs. The server doesn't remember previous requests. No session state on the server — each request is independent.
Resource-Based Design
Everything is a resource: users, products, orders. Each resource has a unique URL. You operate on resources, not actions. GET /users/42 — not GET /getUserById?id=42.
Uniform Interface
Standard HTTP methods with consistent meanings. GET reads, POST creates, PUT replaces, PATCH updates, DELETE removes. Every API speaks the same language.
Cacheability
Responses can be cached at every layer — browser, CDN, proxy. Cache-Control headers tell intermediaries what's cacheable. This is REST's superpower over other paradigms.
URL Structure & HTTP Methods
Products: GET /api/products → List all products GET /api/products/42 → Get product #42 POST /api/products → Create a new product PUT /api/products/42 → Replace product #42 entirely PATCH /api/products/42 → Update specific fields of product #42 DELETE /api/products/42 → Delete product #42 Orders: GET /api/users/7/orders → List orders for user #7 POST /api/orders → Create a new order GET /api/orders/101 → Get order #101 PATCH /api/orders/101 → Update order #101 (e.g., cancel) Nested Resources: GET /api/orders/101/items → List items in order #101
Status Codes — Speak HTTP
| Code | Meaning | When to Use |
|---|---|---|
| 200 OK | Success | GET, PUT, PATCH succeeded |
| 201 Created | Resource created | POST succeeded, new resource exists |
| 204 No Content | Success, no body | DELETE succeeded |
| 400 Bad Request | Client error | Invalid input, missing fields |
| 401 Unauthorized | Not authenticated | Missing or invalid auth token |
| 403 Forbidden | Not authorized | Valid token but insufficient permissions |
| 404 Not Found | Resource doesn't exist | GET /users/999 when user 999 doesn't exist |
| 409 Conflict | State conflict | Creating a duplicate resource |
| 429 Too Many Requests | Rate limited | Client exceeded request quota |
| 500 Internal Server Error | Server broke | Unhandled exception on the server |
Idempotency — Why It Matters
An operation is idempotent if calling it multiple times produces the same result as calling it once. This is critical for reliability — if a network request times out, the client can safely retry without causing duplicates.
| Method | Idempotent? | Why |
|---|---|---|
| GET | ✅ Yes | Reading data doesn't change anything |
| PUT | ✅ Yes | Replacing a resource with the same data = same result |
| DELETE | ✅ Yes | Deleting an already-deleted resource = still deleted |
| PATCH | ⚠️ Depends | Incrementing a counter is not idempotent; setting a value is |
| POST | ❌ No | Creating a resource twice = two resources (duplicates) |
REST Strengths
- ✅Simple and universally understood
- ✅HTTP caching works out of the box (CDN, browser, proxy)
- ✅Stateless — easy to scale horizontally
- ✅Huge ecosystem: tools, libraries, documentation
- ✅Perfect for CRUD operations and public APIs
REST Weaknesses
- ❌Over-fetching: GET /users/42 returns ALL fields even if you need just the name
- ❌Under-fetching: need user + orders + reviews = 3 separate requests
- ❌No real-time: client must poll for updates
- ❌Versioning is painful (v1/users vs v2/users)
- ❌Nested resources get awkward (/users/7/orders/101/items/3)
🎯 Interview Insight
REST is the default choice for most APIs. In an interview, start with REST unless there's a specific reason not to. Then explain: "REST works here because we need simple CRUD with caching. If we later find over-fetching is a problem for our mobile clients, we could consider GraphQL for those endpoints."
GraphQL
GraphQL is a query language for APIs. Instead of the server deciding what data to return (like REST), the client specifies exactly what it needs. One request, one endpoint, precisely the data you asked for.
Custom Order vs Fixed Menu
REST is a fixed menu: you order 'Combo #3' and get a burger, fries, and a drink — even if you only wanted the burger. GraphQL is a custom order: you say 'I want the burger from Combo #3, the sauce from Combo #7, and nothing else.' You get exactly what you asked for, in one trip to the counter.
How GraphQL Works
Single Endpoint
Unlike REST (which has /users, /products, /orders), GraphQL has ONE endpoint: POST /graphql. Every query goes to the same URL. The query body determines what data comes back.
Schema Definition
The server defines a schema — a typed contract of all available data and operations. Types, fields, relationships, queries, and mutations are all declared upfront. The schema IS the documentation.
Resolvers
For each field in the schema, a resolver function fetches the data. The resolver for 'user.orders' might query the Orders database. Resolvers compose — the GraphQL engine calls them as needed based on the query.
REST approach (3 requests): GET /api/users/42 → { id, name, email, avatar, bio, ... } GET /api/users/42/orders → [{ id, total, status, items, ... }, ...] GET /api/orders/101/reviews → [{ id, rating, comment, ... }, ...] GraphQL approach (1 request): POST /graphql { user(id: 42) { name email orders(last: 5) { id total reviews { rating } } } } Response — exactly what was asked: { "user": { "name": "Alice", "email": "alice@example.com", "orders": [ { "id": "101", "total": 59.99, "reviews": [{ "rating": 5 }] } ] } }
Real-World Use Case: Dashboard
Imagine a dashboard that shows a user's profile, their recent orders, top products, and notification count — all on one page. With REST, that's 4+ API calls. With GraphQL, it's one query that fetches exactly the fields each widget needs. This is where GraphQL shines: complex UIs that aggregate data from multiple sources.
GraphQL Strengths
- ✅No over-fetching — client gets exactly what it asks for
- ✅No under-fetching — one query can span multiple resources
- ✅Strongly typed schema — self-documenting, great tooling
- ✅Perfect for mobile (bandwidth-sensitive, varied screen sizes)
- ✅Schema evolution without versioning (deprecate fields, add new ones)
GraphQL Weaknesses
- ❌Caching is hard — every query is a POST to one endpoint
- ❌N+1 query problem — naive resolvers make too many DB calls
- ❌Complexity — schema design, resolvers, query optimization
- ❌Security — malicious clients can craft expensive nested queries
- ❌File uploads are awkward (not natively supported)
⚠️ The N+1 Problem
If a query asks for 50 users and each user's orders, a naive implementation makes 1 query for users + 50 queries for orders = 51 queries. The fix: DataLoader — a batching utility that collects all order requests and makes ONE batched query. This is not optional; it's required for any production GraphQL server.
gRPC & Protobuf
gRPC is a high-performance RPC (Remote Procedure Call) framework built by Google. Instead of sending JSON over HTTP like REST, gRPC sends binary data (Protocol Buffers) over HTTP/2. It's designed for fast, efficient communication between services.
Kitchen-to-Kitchen Communication
REST is like a customer ordering from a menu — human-readable, flexible, but slow. gRPC is like two kitchens in the same restaurant communicating through a high-speed window. They don't need menus or polite language. They have a pre-agreed shorthand (protobuf schema): 'Order 42, table 7, extra sauce' — compact, fast, no ambiguity. The window (HTTP/2) lets them pass multiple dishes simultaneously.
Protocol Buffers (Protobuf)
Protobuf is the serialization format gRPC uses. You define your data structures and service methods in a.protofile, and a code generator creates typed client and server code in your language of choice.
syntax = "proto3"; package user; // The data structure message User { int32 id = 1; string name = 2; string email = 3; repeated Order orders = 4; } message Order { int32 id = 1; double total = 2; string status = 3; } // The service definition service UserService { // Unary — one request, one response rpc GetUser(GetUserRequest) returns (User); // Server streaming — one request, stream of responses rpc ListOrders(ListOrdersRequest) returns (stream Order); // Client streaming — stream of requests, one response rpc UploadLogs(stream LogEntry) returns (UploadResult); // Bidirectional streaming — both sides stream rpc Chat(stream ChatMessage) returns (stream ChatMessage); } message GetUserRequest { int32 id = 1; } message ListOrdersRequest { int32 user_id = 1; } message LogEntry { string message = 1; int64 timestamp = 2; } message UploadResult { int32 count = 1; } message ChatMessage { string text = 1; string sender = 2; }
Communication Types
Unary RPC
Client sends one request, server sends one response. Like a normal REST call but binary and faster. Used for: fetching a user, creating an order.
Server Streaming
Client sends one request, server streams back multiple responses. Used for: real-time price feeds, log tailing, large result sets.
Client Streaming
Client streams multiple messages, server sends one response when done. Used for: file uploads, batch data ingestion, sensor data.
Bidirectional Streaming
Both sides stream messages independently. Used for: chat, collaborative editing, real-time gaming.
Why Binary? JSON vs Protobuf
| Feature | JSON (REST/GraphQL) | Protobuf (gRPC) |
|---|---|---|
| Format | Text (human-readable) | Binary (machine-optimized) |
| Size | Larger (field names repeated in every message) | ~3-10x smaller (field numbers, no names) |
| Parse speed | Slower (string parsing) | ~5-10x faster (binary decoding) |
| Schema | Optional (OpenAPI, JSON Schema) | Required (.proto file) |
| Code generation | Optional | Built-in (generates typed clients/servers) |
| Debugging | Easy (read JSON in browser) | Hard (binary, need tools to decode) |
| Browser support | Native | Limited (needs grpc-web proxy) |
gRPC Strengths
- ✅~10x faster than REST for serialization/deserialization
- ✅Strongly typed contracts — code generation prevents mismatches
- ✅HTTP/2 multiplexing — multiple RPCs on one connection
- ✅Streaming support — server, client, and bidirectional
- ✅Deadline propagation — timeouts cascade across services
gRPC Weaknesses
- ❌Not browser-friendly — needs grpc-web or a REST gateway
- ❌Debugging is harder — binary payloads aren't human-readable
- ❌Steeper learning curve — proto files, code gen, tooling
- ❌Less ecosystem support than REST (fewer tools, libraries)
- ❌Overkill for simple CRUD APIs with low traffic
🎯 Why Companies Use gRPC Internally
Companies like Google, Netflix, and Uber use gRPC for service-to-service communication. When you have 500+ microservices making millions of internal calls per second, the performance difference between JSON and Protobuf is massive. Externally, they still expose REST or GraphQL for public APIs because browsers and third-party developers expect it.
WebSocket APIs
WebSocket APIs provide a persistent, full-duplex communication channel between client and server. Unlike REST (where the client always initiates), WebSockets let the server push data to the client at any time without being asked.
Phone Call vs Letter Exchange
REST is exchanging letters — you write, send, wait for a reply, write again. Each letter has a full address and return address (HTTP headers). WebSockets is a phone call — you dial once (the handshake), and then both sides talk freely. No need to re-introduce yourself on every sentence. The line stays open until someone hangs up.
How WebSocket APIs Differ from HTTP
| Feature | HTTP (REST) | WebSocket |
|---|---|---|
| Connection | New connection per request (or keep-alive) | Single persistent connection |
| Direction | Client → Server only (client initiates) | Bidirectional (both sides push) |
| Overhead | Headers on every request (~800 bytes) | Minimal framing (~2-6 bytes per message) |
| State | Stateless | Stateful (connection is maintained) |
| Caching | Built-in (Cache-Control, ETags) | No caching support |
| Scaling | Easy (stateless, any server) | Harder (sticky sessions, connection state) |
Real-World Use Cases
Chat Applications
Messages must appear instantly. The server pushes new messages to all participants the moment they're sent. Slack, Discord, WhatsApp Web all use WebSockets.
Live Notifications
When someone likes your post or a deployment finishes, the server pushes a notification immediately. No polling delay.
Trading Dashboards
Stock prices change multiple times per second. The server streams price updates continuously. Even a 1-second delay is unacceptable.
WebSockets vs Polling vs Long Polling vs SSE
| Approach | How It Works | Direction | Best For |
|---|---|---|---|
| HTTP Polling | Client asks every N seconds | Client → Server | Low-frequency updates (weather, dashboards) |
| Long Polling | Client asks, server holds until data exists | Client → Server (delayed) | Moderate real-time (notifications) |
| SSE | Server pushes over a persistent HTTP connection | Server → Client only | One-way feeds (news, logs, events) |
| WebSockets | Persistent connection, both sides push | Bidirectional | Chat, gaming, collaboration, trading |
WebSocket Strengths
- ✅True real-time — sub-millisecond latency
- ✅Bidirectional — server pushes without client asking
- ✅Low overhead — no HTTP headers on every message
- ✅Efficient for high-frequency updates
- ✅Native browser support (WebSocket API)
WebSocket Weaknesses
- ❌Stateful connections — harder to scale horizontally
- ❌No built-in caching, retry, or error handling
- ❌Load balancer complexity (sticky sessions needed)
- ❌Reconnection logic must be implemented by the client
- ❌Overkill for request-response patterns
⚠️ Scaling WebSockets
With REST, any server can handle any request (stateless). With WebSockets, a user's connection is tied to a specific server. To broadcast a message to all users, you need a pub/sub system (Redis Pub/Sub, Kafka) so all servers receive the message and push it to their connected clients. This is the primary scaling challenge.
End-to-End Decision Scenarios
The real skill isn't knowing what each paradigm does — it's knowing which one to pick for a given system. Let's walk through real scenarios.
💬 Scenario: Chat Application
Choose: WebSockets for real-time messaging + REST for everything else
Why WebSockets: Messages must appear instantly for all participants. The server needs to push messages without the client asking. Bidirectional — both sides send messages.
Why REST alongside: User registration, login, fetching chat history, uploading files — these are all standard request-response operations. No need for WebSockets here.
Architecture: REST API for CRUD + WebSocket server for real-time messaging + Redis Pub/Sub for broadcasting across server instances.
🛒 Scenario: E-Commerce Backend
Choose: REST for public API + GraphQL for the storefront
Why REST: Product catalog, order management, payment processing — these are classic CRUD operations. REST is simple, cacheable (product pages via CDN), and well-understood by third-party integrators.
Why GraphQL for storefront: The product page needs product details + reviews + related products + inventory status. With REST, that's 4 requests. With GraphQL, it's one query. Mobile app gets a lighter payload by requesting fewer fields.
Architecture: REST API for backend services + GraphQL BFF (Backend for Frontend) that aggregates REST calls for the storefront.
📈 Scenario: Real-Time Stock Trading App
Choose: WebSockets for price feeds + gRPC for internal services + REST for account management
Why WebSockets: Stock prices change multiple times per second. The server must push updates instantly. Even 100ms of delay can mean a different price.
Why gRPC internally: The pricing engine, order matching engine, and risk service communicate millions of times per second. Protobuf's binary format and gRPC's streaming make this feasible. JSON would be too slow.
Why REST for accounts: Login, account settings, transaction history — low-frequency CRUD. REST is the simplest choice.
🔧 Scenario: Microservices Internal Communication
Choose: gRPC
Why: Internal services don't need human-readable JSON. They need speed, strong typing, and streaming. gRPC's code generation ensures type safety across services in different languages. Protobuf is 3-10x smaller than JSON. HTTP/2 multiplexing handles high concurrency.
Why not REST: JSON serialization overhead adds up at millions of requests/second. No streaming support. No built-in code generation.
Architecture: gRPC between all internal services + REST or GraphQL gateway for external clients.
💡 The Pattern
Most real systems use multiple paradigms. REST for public APIs and simple CRUD. GraphQL for complex client-facing queries. gRPC for internal service-to-service calls. WebSockets for real-time features. The skill is knowing where each one fits.
Trade-offs & Decision Making
Every paradigm choice is a trade-off. Here are the key comparisons you need for interviews.
REST vs GraphQL
| Dimension | REST | GraphQL |
|---|---|---|
| Data fetching | Fixed response shape (server decides) | Client specifies exact fields needed |
| Endpoints | Multiple (/users, /orders, /products) | Single (/graphql) |
| Caching | Easy (HTTP caching, CDN, ETags) | Hard (POST requests, unique queries) |
| Over-fetching | Common (returns all fields) | Eliminated (client picks fields) |
| Under-fetching | Common (multiple round trips) | Eliminated (one query, nested data) |
| Learning curve | Low (HTTP methods, URLs) | Medium (schema, queries, resolvers) |
| Tooling | Massive ecosystem | Growing but smaller |
| Best for | Simple CRUD, public APIs, cacheable data | Complex UIs, mobile apps, aggregation |
GraphQL vs gRPC
| Dimension | GraphQL | gRPC |
|---|---|---|
| Primary use | Client-facing APIs | Service-to-service communication |
| Format | JSON (text) | Protobuf (binary) |
| Performance | Good | Excellent (3-10x faster serialization) |
| Flexibility | High (client-driven queries) | Low (pre-defined RPCs) |
| Streaming | Subscriptions (limited) | Full streaming (server, client, bidirectional) |
| Browser support | Native (HTTP POST) | Limited (needs grpc-web) |
| Schema | GraphQL SDL | .proto files |
| Best for | Frontend-to-backend | Backend-to-backend |
WebSockets vs HTTP
| Dimension | HTTP (REST/GraphQL) | WebSockets |
|---|---|---|
| Connection | Short-lived (per request) | Long-lived (persistent) |
| Direction | Client initiates | Bidirectional |
| Overhead | High (headers every request) | Low (minimal framing) |
| Scaling | Easy (stateless) | Hard (stateful connections) |
| Caching | Built-in | None |
| Use case | CRUD, queries, mutations | Real-time: chat, live data, gaming |
| Complexity | Low | High (reconnection, state, pub/sub) |
🎯 Decision Framework
Ask these questions in order: (1) Is this real-time bidirectional? → WebSockets. (2) Is this internal service-to-service at high volume? → gRPC. (3) Does the client need flexible, nested queries? → GraphQL. (4) Everything else? → REST. Start simple, add complexity only when the simpler option's limitations hurt.
Interview Questions
Conceptual, scenario-based, and comparison questions you're likely to encounter.
Q:When would you choose GraphQL over REST?
A: When the client needs flexible queries across multiple resources — like a dashboard that shows user profile, recent orders, and notifications in one view. With REST, that's 3+ round trips and over-fetching. GraphQL solves both in one request. Also when you have multiple clients (web, mobile, TV) that need different data shapes from the same backend — GraphQL lets each client request exactly what it needs without building separate endpoints.
Q:Why is gRPC faster than REST?
A: Three reasons: (1) Protobuf binary serialization is 3-10x smaller and 5-10x faster to parse than JSON. (2) gRPC uses HTTP/2 with multiplexing — multiple RPCs share one connection without head-of-line blocking. (3) Code generation creates optimized client/server stubs — no runtime reflection or manual parsing. The trade-off: you lose human readability and browser compatibility.
Q:Why not use WebSockets everywhere?
A: WebSockets are stateful — each connection is tied to a specific server. This makes horizontal scaling much harder (you need sticky sessions or a pub/sub layer). There's no built-in caching, retry logic, or error handling — you implement all of it yourself. For standard request-response patterns (fetching a product, submitting a form), REST is simpler, cacheable, and supported by every tool in the ecosystem. WebSockets add complexity that's only justified when you need persistent, bidirectional, real-time communication.
You're building a mobile app for a social media platform
Which API paradigm would you use and why?
Answer: GraphQL for the main feed and profile pages — mobile clients are bandwidth-sensitive and need different data than web. GraphQL lets the mobile app request only the fields it needs (no over-fetching). REST for auth, file uploads, and simple CRUD. WebSockets for real-time notifications and chat. The GraphQL layer acts as a BFF (Backend for Frontend) that aggregates data from internal REST/gRPC services.
Your company has 200 microservices communicating via REST
The CTO wants to improve internal API performance. What do you recommend?
Answer: Migrate internal service-to-service communication to gRPC. At 200 services making millions of internal calls, JSON serialization overhead is significant. gRPC's Protobuf is 3-10x smaller, code generation ensures type safety across services in different languages, and HTTP/2 multiplexing handles high concurrency. Keep REST for external-facing APIs. Migrate incrementally — start with the highest-traffic internal routes.
A junior developer proposes using GraphQL for a simple CRUD admin panel
Is this a good idea?
Answer: Probably not. A simple admin panel with basic CRUD operations is REST's sweet spot. GraphQL adds complexity (schema design, resolvers, N+1 prevention, caching challenges) that isn't justified when the data access patterns are simple and predictable. REST gives you HTTP caching for free, simpler error handling, and a much larger ecosystem of admin panel tools. GraphQL shines when data access patterns are complex and varied — not for straightforward CRUD.
Common Mistakes
These misconceptions trip up engineers in interviews and in real architectural decisions.
Thinking GraphQL replaces REST completely
GraphQL solves specific problems (over-fetching, under-fetching, multiple client types). But it introduces new ones: caching is harder, security is more complex (query depth attacks), and the N+1 problem requires DataLoader. For simple CRUD APIs, public APIs, and cacheable content, REST is still the better choice.
✅Use GraphQL where its strengths matter (complex UIs, mobile apps, data aggregation). Use REST everywhere else. Many production systems use both — GraphQL as a BFF layer on top of REST services.
Using WebSockets when polling would suffice
If your data updates every 30 seconds, HTTP polling is simpler and works fine. WebSockets add connection management, reconnection logic, sticky sessions, and pub/sub infrastructure. That complexity is only justified for sub-second, bidirectional communication.
✅Ask: 'Does the server need to push data to the client in real-time?' and 'Is the update frequency higher than once per second?' If both answers are yes → WebSockets. Otherwise → polling or SSE.
Ignoring REST best practices
Using POST for everything, returning 200 for errors, putting verbs in URLs (/getUser, /deleteOrder), not using proper status codes. This makes APIs unpredictable and hard to consume.
✅Resources as nouns (/users, /orders), HTTP methods as verbs (GET, POST, PUT, DELETE), proper status codes (201 for created, 404 for not found, 409 for conflict), and consistent error response format.
Misunderstanding gRPC's use case
Trying to use gRPC for browser-facing APIs or thinking it replaces REST for public APIs. gRPC is designed for internal service-to-service communication where performance matters and both sides are controlled by the same team.
✅gRPC for internal, high-performance service communication. REST or GraphQL for external/browser-facing APIs. If you need gRPC in the browser, use grpc-web with a proxy — but consider whether REST/GraphQL would be simpler.
Choosing a paradigm based on hype, not requirements
Picking GraphQL because it's trendy, or gRPC because it's 'faster', without analyzing whether the system actually needs those capabilities. Every paradigm adds complexity — that complexity must be justified by real requirements.
✅Start with REST (simplest, most understood). Move to GraphQL when over-fetching/under-fetching becomes a real problem. Move to gRPC when internal service performance is a measured bottleneck. Move to WebSockets when you need real-time bidirectional communication. Let requirements drive the decision, not trends.