So You Can Write Code But Can't Design a System β We Need to Talk ποΈπ
"It works on my machine." β Every dev who has never thought about scale.

Let me set the scene.
It's a Monday morning. Sprint planning. The product manager slides over a new requirement: "We need real-time notifications for all users."
Someone in the back confidently says: "Easy. I'll just add a cron job that polls the database every second."
The room goes quiet.
A senior dev somewhere closes their laptop, stares at the ceiling, and begins questioning their life choices.
π€¦ The Epidemic Nobody Talks About
We spend so much time arguing about tabs vs. spaces, async/await vs. .then(), and whether var is a war crime β but somehow, system design is the one skill that gets completely skipped in a lot of devs' growth journey.
You'll find brilliant engineers who can write beautifully clean code, follow SOLID principles to the letter, and still design a system that collapses the moment it gets 500 concurrent users.
I've lived this. I've been this. And I've definitely suffered alongside people like this.
Let me introduce you to the archetypes.
π The Cast of Characters
1. The "Just Add More Servers" Guy
This person's solution to every performance problem is horizontal scaling β except they've designed the system with a single-threaded, stateful singleton that can never actually scale horizontally.
"Why is it slow? Just spin up another instance!"
"β¦the instances are fighting over the same database row lock."
"β¦spin up more instances?"
I once saw a .NET Web API where every request was locking a static List<T> in memory. The solution proposed? Load balancer. Multiple instances. Each with their own in-memory list. Each blissfully unaware the others existed.
The bug reports were Shakespearean in their tragedy.
2. The "One Database to Rule Them All" Architect
Every domain. Every entity. Every audit log. Every blob. One. SQL. Database.
User profiles? SQL. Product catalog with 2 million SKUs and dynamic attributes? SQL with a JSON column. Real-time chat messages? Definitely SQL. File uploads? Oh, those are stored as VARBINARY(MAX).
You think I'm joking. I am not joking.
The database becomes a 400-table monstrosity with foreign keys that form a graph so complex it would make a topologist cry. And then one day you need to add an index and the migration takes 6 hours on production because nobody thought about table locks.
3. The Synchronous Everything Devotee
This dev has heard of message queues. They've nodded along during the architecture discussion. Then they go home and write:
// "Sending" a notification
foreach (var user in allUsers)
{
emailService.Send(user.Email, subject, body); // blocking HTTP call
}
return Ok("Done!"); // 45 seconds later
The HTTP request just becomes the world's most patient person, sitting there, waiting, while your app crawls through 10,000 email API calls in sequence.
Meanwhile, the load balancer timeout fires at 30 seconds. The client retries. You're now sending emails twice. Some users get four emails. The CEO gets twelve.
RabbitMQ, Azure Service Bus, literally anything β but no. We stay synchronous. We stay pure.
4. The "We'll Optimize Later" Prophet
Ah, the classic. The one who knows the design is bad but ships it anyway with the sacred promise:
"This is just an MVP. We'll refactor when we need to."
Narrator: They never refactored.
That "temporary" caching solution that stores everything in a single Redis key as a serialized JSON blob of the entire application state? That's in production. Has been for two years. The key is 4MB. It gets rewritten on every page load.
The "temporary" stored procedure that joins 11 tables and does business logic in T-SQL? Still there. Has 47 lines of commented-out code and a comment that says // TODO: move to service layer (2021).
It is 2026.
5. The Premature Optimizer (The Evil Twin)
The opposite extreme, but equally chaotic.
This person has read about CQRS, Event Sourcing, Saga patterns, and the entire Domain-Driven Design blue book β and they're going to use ALL of it. For a school enrollment form.
"I've set up Kafka for the event bus, deployed Kubernetes with 12 microservices, each with its own database, and I'm using the Outbox pattern with a distributed transaction coordinator to handle the student registration flow."
"β¦how many students does the school have?"
"About 200."
The infrastructure costs more to run than the school's annual budget. The P99 latency of registering one student is 3 seconds because the request hops through 6 services and 4 message queues.
This one hits close to home, honestly. We've all been tempted. Azure has too many cool services.
π§ Why Does This Happen?
It's not stupidity. Genuinely. These are smart people. The problem is that system design isn't taught the same way coding is.
You can learn to code by building things, getting immediate feedback, seeing it work or break. But bad system design? That's a delayed failure. It hides. It waits. It lets you merge the PR, ship to prod, go home happy β and then explodes six months later when the traffic spike hits or the data grows by 10x.
There's also this cultural thing in dev communities where system design is treated as a "senior" topic, something you earn later. So juniors and mid-levels often don't think about it until they're already knee-deep in a distributed mess of their own making.
And honestly? A lot of us learned system design the hard way.
The real curriculum is:
π₯ Getting paged at 2AM because your single-point-of-failure actually failed
π Watching a database die under load you never load-tested for
π€‘ Explaining to the CTO why the "temporary" solution is now load-bearing
β What Good System Design Actually Looks Like
It doesn't mean Kafka for everything. It doesn't mean microservices for a startup with 3 users. Good system design is about asking the right questions before writing a single line of code:
What's the scale? β 100 users or 10 million? Now? In 2 years?
What are the consistency requirements? β Can we tolerate eventual consistency, or does every read need to be fresh?
What's the failure mode? β What happens when this goes down?
Where are the bottlenecks? β Database? Network? CPU? Third-party API?
What does the data flow actually look like? β Draw it. Physically draw it.
That last one? Draw it. Whiteboard it. Napkin it. If you can't draw the data flow, you don't understand the system yet.
π₯ A Quick Story from a Real Project
Picture this: a web app for a mid-sized e-commerce client. Simple enough β product catalog, cart, orders, payments. The team was four developers, all competent, all fast. They shipped the MVP in six weeks. Everyone celebrated.
Three months later, they ran a flash sale.
Traffic spiked to roughly 8x normal load. Not crazy numbers β maybe 2,000 concurrent users. The kind of load any reasonable system should shrug off. Instead, what happened was this:
The order creation endpoint was calling the inventory service synchronously β a direct HTTP call, waiting for confirmation before responding. The inventory service, under load, started slowing down. So the order service threads started piling up, waiting. Thread pool exhaustion kicked in. The order service started timing out. The frontend retried. Now you have duplicate orders. The payment gateway gets called twice. Some customers get charged twice.
Meanwhile, someone had put a distributed lock on the inventory update β reasonable in isolation β but it was implemented as a SELECT ... FOR UPDATE on a single inventory row per product. Every order for the same product queued behind the same lock. For the top 3 products in the sale? A traffic jam that would make EDSA at rush hour look efficient.
The dev who built it was genuinely talented. The code was clean. The unit tests passed. There was just never a conversation that went: "Okay, what happens when 500 people try to buy the same item at the same time?"
That question β "what happens whenβ¦" β is the entire discipline of system design in one sentence. And it costs nothing to ask it before you write the code.
The fix took two weeks, one nasty production rollback, and a very uncomfortable call with the client. All of it avoidable with a single whiteboard session upfront.
π‘ The Uncomfortable Truth
If you've read this far and recognized yourself in any of these archetypes β good. That self-awareness is the first step.
The second step is studying system design intentionally. Not just for coding interviews. Actually understanding why you'd pick a message queue over a direct call, when a cache helps vs. hurts, and why "just use a database" isn't an architecture.
Some pointers to get started:
"Designing Data-Intensive Applications" by Martin Kleppmann β treat it like a Bible
System Design Primer on GitHub β free, practical, brutally honest
Draw your current production system β if it looks like a bowl of spaghetti, that's diagnostic information
And the next time someone proposes polling a database every second for real-time updates β send them this article.
Written by a developer who has made at least 3 of these mistakes personally and is still recovering. We're all learning. Ship thoughtfully. π






