Some core concepts and presentation of a system design

Aviv Almashanu
Pecan Tech Blog
Published in
7 min readNov 17, 2021

I’ve recently joined Pecan.ai and re-learned the importance of properly visualizing, planning, and presenting a system design.

In my new role, I was tasked with re-designing a part of the system. The main goals for the project were decoupling this component from a larger pipeline, and creating better response time and granularity in the components behavior.

In this article, I would like to focus on the flow and some basic concepts I’ve used to present the design in an understandable and simple way to my peers, including PMs and technical managers.

Some basics

Before we begin, let’s go over a few core concepts of system design to help make sense of my explanations down the road.

When creating a system design, I always consider three basic factors: Scale, Decoupling and Abstraction. So what do these concepts mean?

Scale — When that we receive increased traffic to our system, it should be able to scale up and support the increase in requests in a resource-efficient matter. The main enemies of scale are bottlenecks.

Decoupling — In the event of changes to a component of the system (something that happens literally all the time), we would like to make changes to the smallest unit possible. This simplifies the development process and keeps the changes contained so that they are easily testable, and more importantly easily revertable, in case of any unexpected bugs.

Abstraction — Working closely with decoupling, but more about the way an interface is exposed: An interface (meaning the set of rules in which we interact with a component) should be as generic and simple as possible.

If the interface assumes you have prior or specific knowledge of the domain it serves, you might have a difficult time reaching the resources you are looking for. Similar to decoupling, clients having specific knowledge of your domain means that, in the common event of change, you will need to apply it to multiple areas, making the development and release of new behavior difficult.

A classic example of an excellent interface is the Google search bar. You need to know literally nothing about the way Google implements its search and resources. Though they support smart querying, even my grandma can search Google without calling me. They make changes to that system continuously, multiple times a day, while keeping the most important interface looking pretty much the same for years!

Obviously, this is a simplified overview of system design and there are many other concepts to cover — like security, development speed, and the deployment process, just to name a few—but we’ll focus on these concepts for now to make this article readable in a reasonable amount of time.

The design presentation process

The first part of any major change in a system is creating a clear and coherent design for the proposed solution. The design should make clear the different points taken into consideration and also why this solution was chosen and what benefits we are planning to reap from this change.

Assuming that I’ve done my homework and prepared a thorough design, this is roughly what I would have presented.

I usually like working with diagrams and visualizations as they achieve the quickest understanding time, in my experience.

The general process of presenting a design is as follows:

1. Present the existing solution:

Let’s assume this simple and obviously non-ideal architecture.

Basically, our system manages users and their purchases. Given a specific user, I can fetch the users’ purchases.

Exciting design

The existing flow for getting a user’s purchase looks like this:
Make an API request to our entry point, which will be forwarded to the user-purchases route on the user service. The user service will then—while holding the request context—make another request to the purchases service and return all the requested purchases.

2. So what's so wrong with this design?

Looking at the three concepts mentioned before, here are some apparent issues:

Scale:
1.
Every request to the purchase service goes through the user service, meaning that we need to scale both components together, even if we only need to support more traffic for purchases.
2. The user service needs to hold the request open while interacting with the purchase service, meaning it cannot accept new user requests and is an idle resource at the time it is waiting for a response from the purchases service.

Decoupling:
1.
The user service is aware of the purchase service API, and we’ll need to make changes to the user service on every new feature implemented in the purchases service.

Abstraction:
1.
Let's say we want to make an admin request and query a statistic about our user's different purchases. In this implementation, we would need to be aware of the user service as well, and figure out how to get the data we want via the user service, instead of just simply asking the purchase service for this data. This means we need to be aware of irrelevant APIs while trying to fetch our resource, which is a non-intuitive experience.

At this phase, I like to stick markers on the diagram to make the pain points visible and color code their severity to our current state:

Exciting design with notes

3. Presenting the proposed solution

Proposed solution

In this solution, we have decoupled the flow for getting a user and getting the user purchases. Assuming we have some sort of session security mechanism in place, you should not be able to query purchases without authenticating first.

Our previous flow now looks like this:
Make an API request to our entry point, which will be forwarded to the user service. After the user service returns the user, make another API request to our entry point which will be forwarded to the purchases service, which will return the requested purchases.

4. So why is the new solution better? How does it solve the previously presented issues?

On the surface, this looks like a more complex interaction on the client side, so what do we have to gain here? From the presented flow, it’s still not obvious why this new solution is preferable to the previous one. So let's look at our core concepts:

Scale:
1.
Every service request is handled separately, meaning that an increase in user requests or purchase requests does not affect the other, and we can scale each component simply without considering other resources. The components never stand idle because they only handle their own work.

Decoupling:
1.
Each component is only aware of its own resources, meaning that when making changes, they are contained only to the relevant service. For example, supporting a new API for subscriptions on the purchases service has no effect on the user service.

Abstraction:
1.
We can easily extend each service and support new services without changing the behavior of any of the existing service. For example, a new ordering service can be easily added to the system as long as it follows the abstraction rules and doesn’t expose irrelevant behavior creating coupling. An example of a potential abstraction leakage would be if, in order to get the status of your order, you would also need to know the name of the item you purchased. There is no good reason for the ordering system to know this fact, and needing to fetch this data in advance from the purchases service creates an unnecessary coupling in the system.

Finally, summarise the pros and cons of your proposed solution, and make sure to cover all the relevant considerations, as well as answer all questions that come up during the presentation.

some points that are important to consider are:

  1. How difficult will this solution be to implement and maintain?
  2. Do we currently have the know-how to execute this plan?
  3. What concerns do we have for this proposition and how can we keep an eye on them?

If you’ve reached the end of the session with few to no open ends, you have succeeded in creating a good understanding of your design.

Takeaways:

  1. Plan your presentation with a narrative flow in mind. Start from where you are today, and reach where you want to be. Along the way, explain the different difficulties you expect to encounter and how you plan to solve them.
  2. Make sure to cover the basics and level the playing field. Design meetings tend to have a mix of personnel with different levels and backgrounds. Make sure everyone is on the same page, and don’t be afraid to dedicate some time to go over the basics for those who aren’t sure.
  3. Use visual aids! I’ve found that there is no better way to talk about system design, especially with non-technical personnel. This allows them to see the different parts of the system and how they interact very clearly!

Last but not least, some final suggestions:

Send the material beforehand and after the presentation, and make sure to stay available for follow-ups. Designs tend to be complex and can take time to sink in and understand. Your peers might understand new things down the road that didn’t come up in the design sessions. Be open to change and embrace the design evolution. Systems are breathing things and are not meant to stay the same forever!

--

--

Aviv Almashanu
Pecan Tech Blog

A software engineer, hoping to make the world a better place through knowledge and collaboration