DEV Community

Cover image for I Read Designing Data-Intensive Applications and Finally Understood System Design
Ritesh Rajpurohit
Ritesh Rajpurohit

Posted on

I Read Designing Data-Intensive Applications and Finally Understood System Design

Recently, I was reading Designing Data-Intensive Applications by Martin Kleppmann, and one quote made me stop for a while:

“There are no solutions; there are only trade-offs. But you try to get the best trade-off you can get, and that’s all you can hope for.”

— Thomas Sowell

This line explains system design better than many tutorials.

When I started learning backend and cloud architecture, I used to search for perfect answers:

  • Which database is best?
  • SQL or NoSQL?
  • Monolith or microservices?
  • Serverless or containers?
  • Consistency or availability?
  • Which AWS service should I use?

But the more I learn, the more I realize that these questions are incomplete.

The better question is:

What trade-off am I choosing, and why?

There is no “best” database

A common beginner question is:

Should I use SQL or NoSQL?

But SQL and NoSQL are not enemies. They are tools with different trade-offs.

A relational database like PostgreSQL or MySQL is great when:

  • Your data has relationships
  • You need transactions
  • You need flexible querying
  • Correctness is very important

A NoSQL database like DynamoDB is great when:

  • You need low-latency access at scale
  • Your access patterns are known
  • You want predictable performance
  • You are okay designing around queries first

The trade-off is simple:

SQL gives flexibility. NoSQL gives scale and speed for specific access patterns.

Neither is always better.

It depends on the problem.

Caching makes things faster, but not simpler

Caching sounds like an easy win.

Add Redis. Add CloudFront. Add an in-memory cache. Done, right?

Not really.

Caching improves performance, but it creates new questions:

  • What if the cache has old data?
  • When should the cache expire?
  • What happens if the cache goes down?
  • Who updates the cache after a database change?

So the trade-off becomes:

Caching gives speed, but adds invalidation and consistency problems.

That does not mean caching is bad. It means caching should solve a real bottleneck, not be added just because it sounds advanced.

Microservices are not automatically better

Microservices look attractive because big companies use them.

But big companies also have big teams, mature DevOps, monitoring, tracing, deployment pipelines, and years of production experience.

For a small team or early product, microservices can create more problems than they solve:

  • More deployments
  • More network calls
  • More failure points
  • Harder debugging
  • Distributed data problems
  • More operational overhead

A monolith may be boring, but boring is often good.

The trade-off is:

A monolith is simpler to build and debug. Microservices help large teams move independently, but add operational complexity.

If your app has 5 users, you probably do not need 12 services.

Consistency vs availability is a real product decision

Imagine you are building an event registration system.

If one part of the system cannot talk to another, what should happen?

Should users still be allowed to register?

Or should the system stop registration until it can guarantee the data is fully correct?

For a casual community event, temporary inconsistency may be acceptable.

For payments, bank transfers, exam results, or ticket inventory, correctness may matter more.

The trade-off is:

Availability keeps the system usable. Strong consistency protects correctness.

The “right” answer depends on what failure your product can tolerate.

AWS services also have trade-offs

Cloud platforms like AWS give us powerful building blocks:

  • Amazon S3
  • Amazon EC2
  • AWS Lambda
  • Amazon RDS
  • Amazon DynamoDB
  • Amazon SQS
  • Amazon CloudWatch
  • Amazon CloudFront

But good architecture is not about using the maximum number of services.

Good architecture is using the minimum number of services that solve the problem well.

For example, if I am building a small registration app for a student event, I might start with:

  • A frontend form
  • A simple backend
  • One database
  • Email confirmation

That may be enough.

If the same system needs to handle thousands of users at once, then I might add:

  • API Gateway
  • Lambda
  • DynamoDB
  • SQS
  • CloudWatch
  • CloudFront

That version is more scalable.

It is also more complex.

So the real question is not:

Can I build it with all these services?

The real question is:

Do I need this complexity right now?

My new system design checklist

After reading this idea, I started asking better questions before choosing tools:

  1. What problem am I solving?
  2. What are the read and write patterns?
  3. What needs to be fast?
  4. What needs to be correct?
  5. What failure can the system tolerate?
  6. What will this cost?
  7. What complexity am I adding?
  8. Can I start simpler?

That last question is important.

Many systems do not fail because they were too simple.

They fail because they became complex before they needed to.

A sentence every engineer should write

Whenever we choose a tool or architecture, we should be able to write this sentence:

We chose ___ because __, but we accept __.

Examples:

We chose PostgreSQL because we need relational data and transactions, but we accept that horizontal scaling may require more planning later.

We chose DynamoDB because we need low-latency reads at scale, but we accept that we must design access patterns upfront.

We chose Lambda because we want event-driven execution without managing servers, but we accept cold starts and service limits.

We chose a monolith because the product is early and the team is small, but we accept that we may split services later if team boundaries demand it.

If you cannot complete this sentence, you may not understand your own architecture yet.

Final thought

The biggest lesson I took from Designing Data-Intensive Applications is this:

System design is not about finding perfect solutions. It is about making informed trade-offs.

There is no perfect database.

No perfect architecture.

No perfect cloud service.

Only context, constraints, and decisions.

Earlier, I used to ask:

What is the right answer?

Now I try to ask:

What trade-off are we choosing, and why?

That one question makes you a better engineer.

Top comments (0)