System Design 101: Core Concepts Every Engineer Should Know
Every app you use daily — whether it’s Instagram, Google Maps, or your bank’s website — started as a simple idea running on a single…
System Design 101: Core Concepts Every Engineer Should Know
Every app you use daily — whether it’s Instagram, Google Maps, or your bank’s website — started as a simple idea running on a single computer. The gap between that humble beginning and a system that serves millions without breaking a sweata is what system design is all about. This guide walks you through the concepts, decisions, and real-world trade-offs that engineers face when building software that lasts.

Where Everything Starts: The Single Server
Picture a small restaurant with one chef who takes orders, cooks the food, and delivers it to your table. That’s essentially a single-server setup. One machine handles your web logic, your database, and any caching — all bundled together.
When a user types a domain like app.yoursite.com, the DNS (Domain Name System) acts like a phone book, converting that human-readable name into an IP address and pointing the browser to your server. The server responds with either an HTML page (for web browsers) or a JSON payload (for mobile apps). Clean, simple, and perfectly fine — until it isn't.
Practical tip: Even on a single server, separate your concerns in code. Keep your database logic, business rules, and presentation layers distinct from day one. When the time comes to scale, you will not need to untangle a spaghetti codebase.
Choosing Your Database Wisely
As your app grows, the first major architectural decision you’ll face is where to store your data. The wrong choice here costs you months of painful migrations later.
Relational databases (PostgreSQL, MySQL, SQLite) organize data into structured tables with rows and columns. Their superpower is ACID compliance — Atomicity, Consistency, Isolation, and Durability. These four guarantees make relational databases the right tool when correctness matters more than raw speed. Banking transactions, e-commerce order records, healthcare data — these are not places where you want “eventual consistency.” You want to be certain that when money moves from account A to account B, neither record is left in a broken state.
Non-relational databases (MongoDB, Cassandra, Redis, Neo4j) trade some of that strictness for flexibility and speed. They shine when your data has no fixed structure, when your read and write volumes are enormous, or when your data relationships are too complex for flat tables (think social graphs in Neo4j). Key techniques like caching, sharding, and replication help distribute load and reduce bottlenecks in these systems.
Practical tip: Do not choose NoSQL just because it sounds modern. If your data has clear relationships and consistency is non-negotiable (like financial records), a well-indexed PostgreSQL setup will outperform a poorly designed MongoDB collection every time.
Scaling: Up or Out?
Once a single server can no longer keep up, you have two directions to go.
Vertical scaling means beefing up the machine — more RAM, a faster CPU, a bigger disk. It requires zero changes to your application code, which makes it attractive. The problem is that it has a hard ceiling. At some point, there is no bigger machine to buy. And if that single powerful server crashes, your entire application goes down with it.
Horizontal scaling means adding more servers and sharing the traffic. This approach gives you two things that vertical scaling cannot: redundancy (if one server dies, others absorb the traffic) and near-infinite growth potential (you can always add another node).
Over 90% of large enterprises are already using containerization and orchestration tools like Docker and Kubernetes, with organizations adopting this stack reporting up to a 50% reduction in manual deployment overhead and a 30% increase in infrastructure efficiency.
Practical tip: Start vertical, but architect horizontal from the beginning. Do not store session state in local server memory — use a shared store like Redis instead. This makes it trivially easy to add more servers later without breaking sessions.
Load Balancers: The Traffic Directors
Horizontal scaling immediately raises a question: how does incoming traffic know which server to go to? The answer is a load balancer — a gatekeeper that sits in front of your server pool and intelligently distributes requests.
There are several strategies a load balancer can use. Round Robin cycles through servers in sequence — server 1, server 2, server 3, and back to server 1. It works well when all your servers have equal capacity. Least Connections routes each new request to the server currently handling the fewest active connections, which is smarter for workloads where tasks vary in duration. IP Hash always sends a specific user to the same server — useful when session data is stored locally (though, as noted above, you are better off avoiding that). Geographic routing sends users to the server cluster physically closest to them, cutting latency dramatically for a global audience.
Popular software load balancers include NGINX and HAProxy. Cloud providers offer managed solutions — AWS Elastic Load Balancing, Google Cloud Load Balancing, Azure Load Balancer — that handle scaling and health checks automatically.
One important pitfall: a load balancer itself can become a single point of failure. The fix is to run two load balancers in an active-passive or active-active configuration, so traffic continues flowing even if one fails.
Practical tip: Enable health checks on your load balancer. These are periodic pings to each server to confirm it is alive. A server that fails a health check is automatically removed from rotation and traffic is rerouted — no human intervention needed.
Caching: Stop Asking for the Same Answer Twice
You should never ask your database for the same piece of information twice if it hasn’t changed. Accessing RAM is thousands of times faster than hitting a disk.
Caching is the art of storing results you have already computed so you can return them instantly the next time they are requested. There are several layers where caching can live.
Application-level caching uses tools like Redis or Memcached to hold frequently accessed data — product listings, user profiles, configuration values — directly in memory. One real-world implementation of Redis as an application-level cache for a frequently accessed product catalog reduced database query load by over 70%, using a TTL (Time-to-Live) to ensure periodic refresh, and a write-through strategy for critical data like pricing.
CDN caching serves static assets — images, CSS files, JavaScript bundles, videos — from servers physically close to your users. If a user is in London, they should not have to wait for a server in New York to send them a header image.
There are different strategies for keeping your cache consistent with your database. Cache-aside (also called lazy loading) means your application checks the cache first and only fetches from the database on a miss, then stores the result. Write-through updates both the cache and the database on every write, keeping them always in sync. Write-back (or write-behind) updates only the cache immediately and syncs to the database asynchronously — faster writes, but there is a brief window of potential data loss.
Practical tip: Cache invalidation is notoriously hard. The safest mental model: set a sensible TTL on every cache entry so stale data eventually expires on its own. For data that must be fresh immediately (like inventory counts), use write-through caching and avoid relying on TTL alone.
APIs: The Contracts of Your System
An API is the agreed-upon language that different parts of your system — or entirely different systems — use to talk to each other.
REST remains the most widely used style for public APIs. It uses standard HTTP methods: GET to retrieve data, POST to create, PUT to replace, PATCH to partially update, DELETE to remove. Resources are named with plural nouns and grouped logically — /api/v1/orders, /api/v1/users/42/orders. REST is stateless, meaning each request carries all the information needed to process it.
GraphQL solves a real pain point in REST: overfetching and underfetching. With REST, a mobile app might need to call five different endpoints to build a single screen. With GraphQL, the client sends one query describing exactly the fields it needs, and the server returns precisely that. This is especially valuable for complex UIs and mobile clients where bandwidth matters.
gRPC is the choice for internal service-to-service communication where performance is critical. It uses Protocol Buffers for serialization (smaller payloads than JSON) and runs over HTTP/2 (supporting multiplexed streams). Microservices talking to each other dozens of times per second are natural fits for gRPC.
Practical tip: Version your APIs from day one using URL-based versioning (/api/v1/, /api/v2/). Changing an API without versioning forces all your clients to update simultaneously — which is rarely possible and always painful.
Keeping APIs Secure
An API without protection is an open door.
Rate limiting is your first line of defense against both deliberate abuse and accidental overload. Limit clients to a reasonable number of requests per minute per endpoint. Advanced rate limiting now uses machine learning to detect and adapt to abnormal traffic patterns, with different limits applied per endpoint, per user, or per IP address.
Authentication versus Authorization are two distinct concerns that are often confused. Authentication answers “who are you?” — verifying identity using passwords, API keys, or JWT tokens. Authorization answers “what are you allowed to do?” — enforcing permissions through mechanisms like Role-Based Access Control (RBAC) or Attribute-Based Access Control (ABAC).
Input validation cannot be skipped. Every piece of data coming into your system is untrusted. Use parameterized queries to prevent SQL injection. Sanitize HTML inputs to prevent Cross-Site Scripting (XSS). Validate data types and sizes before they ever reach your database.
CORS (Cross-Origin Resource Sharing) controls which external domains are allowed to call your API from a browser. Be explicit — a wildcard (*) is almost never the right answer for a production API.
CSRF tokens protect against cross-site request forgery attacks where a malicious page tricks an authenticated user’s browser into making unintended requests to your server.
Practical tip: Treat security as a checklist, not an afterthought. Before any API endpoint goes to production, confirm it has authentication, authorization checks, input validation, and a rate limit applied. A Web Application Firewall (WAF) like AWS WAF or Cloudflare adds another layer of automated filtering for common attack patterns.
TCP vs. UDP: Picking the Right Transport
At the lowest level of network communication, your data travels over one of two protocols.
TCP establishes a connection through a three-way handshake before sending any data, then guarantees delivery, order, and error correction. This reliability comes at the cost of speed. TCP is the right choice when losing a single packet is unacceptable — financial transactions, login flows, email.
UDP sends packets without establishing a connection or guaranteeing delivery. If a packet is lost, it is gone. In return, UDP is significantly faster and has lower overhead. For live video streaming, online gaming, or VoIP calls, a momentary glitch (a single dropped frame or a brief audio stutter) is far more acceptable than the pause that would result from TCP’s retransmission process.
Practical tip: When you use WebSockets for real-time features like live chat or notifications, those WebSockets run over TCP. If you are building something like a multiplayer game where frame-perfect timing matters more than guaranteeing every packet, explore UDP-based transport protocols.
Putting It All Together: The Production Architecture
A modern production system looks nothing like a single server. A user request might touch a CDN edge node, pass through a load balancer to one of dozens of application servers, pull data from a Redis cache (hitting the database only on a miss), and receive a response in milliseconds. Background tasks are handled asynchronously through message queues like RabbitMQ or Kafka, so a slow job like sending a welcome email never delays the API response.
Asynchronous communication — often managed by message queues — creates a buffer that smooths out traffic spikes and keeps individual services from getting overwhelmed, like a restaurant using a ticket system where the waiter places an order and moves on without waiting at the kitchen pass.
For older systems that have grown unwieldy, legacy modernization can transform outdated software into modern, scalable solutions, ensuring they can adapt and support long-term growth.
The Engineer’s Mindset
Technical knowledge is only half the equation. Every architectural decision has pros and cons — understanding these trade-offs is what separates good developers from great ones. Choosing PostgreSQL over MongoDB is not a statement about which database is better. It is a judgment call about your specific consistency requirements, query patterns, team expertise, and growth projections.
The practical path forward is straightforward: start simple, measure everything, and scale where the data tells you to scale. A caching layer added before you have a database bottleneck is wasted complexity. A caching layer added after you identify that 80% of your database reads are hitting the same ten rows is a well-targeted solution that delivers immediate results.
Build systems that can be understood by someone reading the code a year from now. Design APIs that you would want to use if you were the client. Secure every endpoint as though someone is actively trying to exploit it — because eventually, they will be.
메타데이터
- post_id
- 25ad7e67c265
- slug
- system-design-101-core-concepts-every-engineer-should-know-25ad7e67c265
- url
- https://medium.com/@ahirlog/system-design-101-core-concepts-every-engineer-should-know-25ad7e67c265
- canonical_url
- https://medium.com/@ahirlog/system-design-101-core-concepts-every-engineer-should-know-25ad7e67c265
- author_url
- https://medium.com/@ahirlog
- status
- ok
- fetched_at
- 2026-06-12 18:14:10