Over the past few years, one of the most common questions I’ve been asked is this:
“How do I actually get better at system design?”
Not how to pass a system design interview. Not what books to read. Not what architecture diagram to memorize.
But how to genuinely improve at thinking in systems.
The people asking are rarely junior engineers. Most of them are strong developers. They know how to sharpen their coding skills. They understand complexity. They’ve shipped real features. Some of them have even used most of the major cloud services.
And yet, when it comes to system design, they feel a gap.
There’s a fuzziness to it.
Coding has a clear feedback loop. Your code compiles or it doesn’t. Tests pass or they fail. Performance improves or it regresses.
System design doesn’t give you that kind of clean signal.
And that’s where many capable engineers get stuck.
System Design Is Not Memorization
When I was earlier in my career, I thought system design was about knowing architectures.
If I understood how large companies structured their systems, if I knew enough distributed systems theory, if I had used enough cloud services — that should be enough.
It wasn’t.
Knowing services is not the same as knowing tradeoffs. Using a database is not the same as understanding when it becomes the bottleneck. Deploying to multiple regions is not the same as understanding what cross-region writes actually cost you.
The first real shift in my thinking came when I stopped treating system design as knowledge acquisition and started treating it as exposure accumulation.
You don’t become good at system design by collecting diagrams.
You become good by seeing systems under stress.
Deeply Understand Your Building Blocks
Before you can design well, you need a deep understanding of the primitives available to you.
Not just “I’ve used Redis” or “I’ve deployed on Kubernetes.”
I mean understanding:
- When does this break?
- What are its failure modes?
- How does it behave under load?
- What are its latency characteristics?
- What tradeoffs does it make internally?
- What operational burden does it introduce?
If you’re working in cloud environments, this means going beyond surface familiarity with messaging systems, databases, caching layers, load balancers, orchestration platforms, and managed services.
You don’t need to know every service in existence. But you should understand the categories well enough that when you reach for one, you understand what you’re signing up for.
Design decisions are tradeoffs. You can’t evaluate tradeoffs if you don’t deeply understand the components involved.
But this alone is not enough.
Controlled Experimentation
This is the part I don’t see discussed often enough.
After you learn a service or a tool, you need to experiment with it deliberately.
Load a large dataset and benchmark it. Add indexes. Remove them. Watch query plans change. Build two small services that talk to each other. Deploy them across regions. Introduce latency. Throttle traffic. Simulate failure.
The goal is not to build a product.
The goal is to build intuition.
When you see how a system behaves under stress, you start forming mental models that are grounded in reality. You stop relying on theoretical assumptions.
You begin to understand why certain architectural decisions exist — not because someone told you they’re “best practice,” but because you’ve felt the constraints yourself.
That exposure compounds.
Intentional Over-Engineering (As a Training Tool)
In real production systems, we try to avoid over-engineering.
In learning, over-engineering is a gym.
One of the simplest examples I often recommend is building a URL shortener.
On the surface, it’s trivial. A small API, a database, and you’re done.
But then start adding constraints:
- Make it multi-tenant.
- Make it read-heavy and cache-dependent.
- Add analytics tracking.
- Deploy it across regions.
- Introduce DNS routing.
- Add per-tenant rate limiting.
- Simulate traffic spikes.
Each layer forces new design decisions.
Suddenly you’re thinking about partitioning, replication, cache invalidation, failover, and cost.
Another powerful exercise is mimicking a financial system — something like a voucher platform or a simple payment gateway. Even if it’s hypothetical, you’ll be forced to think about idempotency, transactional integrity, retries, and reconciliation. These constraints sharpen your thinking quickly.
The point isn’t realism.
The point is forcing your design to evolve.
Where Design Intuition Actually Comes From
At some point, something interesting happens.
You start looking at a problem and potential solutions come to mind quickly. You also start immediately recognizing approaches that won’t work.
This doesn’t come from brilliance. It comes from repetition.
It’s stored failure memory.
You’ve seen what happens when a single region becomes a bottleneck. You’ve seen how naive retry logic can cascade. You’ve seen how global coordination can destroy latency.
So when you face a new problem, your brain draws from that history.
It’s not always perfectly logical. Sometimes it’s an informed instinct.
And that’s okay.
Design thinking at higher levels is often about quickly narrowing the solution space to viable options — not generating the most complicated architecture possible.
The Practical Loop You Can Start Using
If you want something tangible to walk away with, here’s a simple loop:
- Pick a small system. Keep the base simple.
- Define explicit constraints. Traffic, latency, availability, growth assumptions.
- Design version one.
- Add a constraint that stresses it. Multi-region. 10x traffic. Tenant isolation.
- Observe what breaks or becomes awkward.
- Redesign intentionally.
- Reflect on what you underestimated.
Repeat.
The reflection step is critical. Ask yourself:
- What failed first?
- What assumption was wrong?
- What complexity did I introduce too early?
- What did I ignore?
That’s how experiments turn into growth.
The Mindset Shift That Matters Most
If there’s one shift that makes the biggest difference, it’s this:
Stop trying to design the “perfect” system.
Start trying to understand systems under constraints.
System design is not about producing impressive diagrams. It’s about reasoning clearly when tradeoffs are unavoidable.
You don’t need to be a principal engineer to practice this. You need curiosity, repetition, and exposure.
And yes — this takes time.
Most of what I’ve learned about system design didn’t come from reading. It came from side projects, experiments, and building hypothetical systems just to see what would happen. Many of them were over-engineered. Many of them were unnecessary.
All of them taught me something.
If You’re Not Sure Where to Start
Start small.
Pick a simple system. Add one meaningful constraint. Build it. Stress it. Reflect on it.
You don’t need permission. You don’t need the perfect idea. You just need to begin accumulating exposure.
Over time, you’ll notice something subtle but powerful: problems will feel less ambiguous. The solution space will narrow faster. Your design discussions will become clearer and more structured.
That’s when you know the muscle is forming.
System design is not a talent you unlock.
It’s a skill you train — deliberately.
And the fastest way to train it is to build systems that force you to think harder than you’re comfortable with.
Start there.