Architecting for Systems Design

Rogelio Consejo
10 min readMar 21, 2022

The System Design scales.

SOLID and scalable

It is very important to know how to scale a system. It is even more important to make sure that your system is able to scale up or down.

What do I mean by that?

The basic idea is that code has two values:

  • It has to be flexible and easy to extend
  • It has to be able to do what it is supposed to do

And most people think that “doing what it is supposed to do” is the main value of software (I know I did), but that is wrong.

If you just want a machine that does what it is supposed to do, you can make one. Think about it: you could teach even the most junior of developers how to do most things if you know how to do them well yourself. Most people go to a coding bootcamp, some without having any experience with code, and they come out able to make something that works.

Before computers, we could make lots of things that worked very well. The first computers did their work pretty well. They just were very expensive and complex and hard to modify. But they did work.

Why did we even notice electromagnetic relays in the first place?

Why did we start using perforated cards?

What was that all about?

Remember those “save icons” of the past? The big black ones and the small multicolored ones? We used to call them “diskettes”. And they allowed us to store complex instructions to the computer (like Space Quest 4, Duke Nukem, and Doom) as data.

Instructions as data. Why would we want that?

Not just to make something that works, it is to make something that is flexible, agile, changeable.

And the funny thing is, even though most developers feel that making clean code and thinking about their dependencies and building solid tests and even refactoring can be a waste of time, it’s the other way around.

You don’t save time by building a house without a design, you waste it. And money too. And patience.

And in software, we have the ability to iterate our designs very cheaply. So you have to learn how to design a little at a time while keeping everything coherent and decoupled.

We haven’t lost the ability to make good code just because “the business” wants it as soon as possible. We are responsible for what we deliver.

We haven’t lost the ability to make good code at all. We never did.

What we did lose were all those blinking lights on the old machines. They will be missed.

But now let’s talk about Systems Design: It seems to me that what most people do when they do SD is that they try to guess the cheapest deployment strategy that they can get away with for their business case. That’s a good start. But you shouldn’t have to guess.

Wouldn’t it make more sense to first design a system that can change its deployment strategy, speed, resiliency, and scale, as needed? What about being able to change the speed, resilience, or scale of different parts separately?

And implement the cheapest possible solution that you can get away with for every part of the system.

Am I talking about “microservices”? Hell no!

And don’t get me started on “microservices architecture”…

To get independent scalability, ownership, independent deployability, independent “releasability” and an all-around awesome code, you don’t necessarily need microservices (or even any kind of service, for that matter). What you do need is to have clean, SOLID, well-architected code.

You don’t need to choose in advance and think about trade-offs of something that you still don’t know. You do have to understand the business, but you don’t have to guess your future decisions.

You don’t have to narrow your options.

What is more important is to be aware of those choices and changes. Not the options themselves, the choices. And let those options open to be able to make those choices only when you know enough about them.

Defer your decisions. And do look back from time to time. If you don’t, you might get lost.

Now, I know that this idea is a bit abstract, so let me bring it a little closer to earth.

You know that your system is going to scale. Maybe you have lots of experience and you can guess how much it may scale and what parts may need to scale. But you don’t really know how much you are really going to need to scale.

You may be able to estimate, but you don’t know.

So it seems to me that the real value of Systems Design is just about getting familiar with the choices, the parts of the system, what tends to fail at scale, and how to prevent those failures. But that does not invalidate the usefulness of good architecture.

I think architecting for SD should not be so much about “what you know”, but more about “what you know that you don’t know yet”. And it should be approached that way.

You may think that you know what kind of database you need, but most of the time, you don’t even know how many databases you need. We want to make everything fit into a mold.

Then we do MVC all over the place, define some weird code standards or put all of our image files in one folder and think that we are doing our architecture that way.

So let’s talk about the main Concepts in SD and look at them through the looking glass of architecture.

By the way, I am not talking about the SD interview (which is what a lot of SD content focuses on, it seems), but more about the actual Systems Design general knowledge, in the context of architecture during the development process.

And remember, it is ok to not know. I don’t know lots of things too.

Scalability

Is this system going to be used by one person, or one million, or 10 billion?

It looks like such a smart question… and yet…

Thinking about the problem this way during the design phase is pretty much missing the point. Of life. Of life, the universe, and everything.

Remember that there is one “thing” called time?

Is this system going to be used by one person, or one million, or 10 billion?

Well then the answer to the question is:

First one person, then a few, then hopefully, one million, then hopefully 10 billion.

Unless you know it is a tool that only you are going to use (and maybe your developer BFF). To me, there is no middle ground, you either make it scalable or not. You don’t want to decide “how big your system is going to be”, just because that question doesn’t make sense. Because of time.

It will be of all sizes. It will change size. It will grow.

Also, you don’t want to decide “how big your system can get”. If it can grow, it should ideally be able to grow as big as the existing technology will allow, at any moment in time. So that means accounting for technology that might not exist yet.

As I said: architecting for Systems Design should not be so much about “what you know”, but more about “what you know that you don’t know yet”.

PRO TIP: When time passes, things change

Again, SQL, NoSQL… Spark, Hadoop… those are details. It is ok to mention them as options at some point in the design of the system, but those are decisions that should be made once you find out what works best. And you should find out by testing, not by guessing.

Does SQL really help you? Does a schema make things easier for you? Then, at that point in the development process, you can make that choice. Not before.

It is way too easy to overengineer. Don’t.

You can always look for a specific technology once you know what you need. And if it doesn’t exist, there is always a chance that you can make it, or at least choose an alternative if you have done your architecture right.

Think CAP (Consistency, Availability, and Partition Tolerance) instead of SQL/NoSQL.

Think about what parts of your system can forego some consistency… availability… or partition tolerance. Then you know when to separate them, and what kind of persistence to use for each one.

You don’t need to have everything everywhere. Having a toilet in your kitchen does not make any sense.

And partitioning your system as it grows, so that you don’t pay for any more complexity than you need.

With things like Amazon’s S3 (not a sponsor), I can see a lot of systems working very well with a text file persistence implementation and never needing to grow to anything more complex. Think about how cheap, clean, easy, fast, scalable, and reliable that is.

Think about the DevOps.

What DevOps?

Exactly!

Does that mean that everyone should only use text file persistence? No.

My point? Think about what parts of your system need to scale, and how. Also, look at what parts need to scale together or at the same rate.

Encapsulate change.

And don’t over-engineer. Keep It Stupid Simple. Don’t make yourself think too hard (I mean it).

Speed

Where will you need speed and resiliency and you can trade consistency for it?

So what needs to work and work fast, even if it doesn’t always provide the most accurate information?

That’s where you want to use a cache (probably).

In general, it can be useful on the user’s side. Or when you want to be able to work with only a relatively fixed subset of some data.

So one more reason to keep things together is that they are cached together. And we can make boundaries between components that may need caching and those that won’t.

But we don’t know until we know.

Your core system should not depend on caches. Your business logic should not depend on caches. Caches are a way to make interfaces faster. Caches should be on the outer rings of your system, just like Content Delivery Networks, and anything that has to do with speed, most of the time.

And you don’t want your business logic to depend on your caching implementation, in any case.

Resiliency

When your business logic does not depend on the details of your implementations, your system becomes more resilient by design.

When you can easily switch from using one monitor to another, then your computer is more resilient about being able to show you things. If you have a mouse and a keyboard, you have more resilience about being able to input things into it.

If you can easily change your broken mouse for a new one, then your computer is a lot more resilient than if you cannot. If you can easily switch your services persistence, you can easily scale it.

And you don’t need to break it into a thousand pieces or use MVC to be able to do so. You only need to have the right interfaces in the right places. The right boundaries and the right dependencies.

Look at it this way: imagine that you are a painter and you only have black and white paint. If you focus too much on the materials that you have, you might end up missing the “whole picture”. You think of black and white.

But when you focus on the image of what you want to paint, the emotion that you want to convey, the lights and the shadows… then you can begin mixing the paint to get just the right grey that you need without even having to think too much about it.

I think there is way too much emphasis on the materials and the tools we use to make software and too little on the flexibility, the semantics, and the structure of what we want to create.

Most people have forgotten about architecture.

Most people forget that the main value of software is being flexible. That’s why we prefer using computers to using an abacus most of the time.

You can use only white like Kazimir Malévich or get obsessed with black like Pierre Soulages. But you have to start by knowing “what” you want to paint and focusing on that, instead of focusing on the tools that you have.

Then you have to make sure that you can actually make mistakes and correct them.

No one uses an abacus anymore, kid

You should not be afraid of bugs, bugs should be afraid of you. That is why we have QA people trying to break our creations all the time.

You have to be able to try… and learn… and adapt... and improve. So does your system.

“Harder, better, faster, stronger”.

Not just strong, but stronger. Not just smart, but smarter. Software should be flexible.

And it all starts with the Single Responsibility Principle and ends with the Dependency Inversion Principle. SOLID.

(Just kidding, it does not end there, but that sounded pretty cool)

But we will talk more about that soon.

For now, stay happy, productive, and smart!

--

--