Design is about the abstractions
Abstractions simplify designs and tell us when to stop designing.
Before getting into software engineering, I was on my way to becoming a house designer.
asked what lessons from house design I apply to software architecture.You can read my reply and the lessons on Jordan's High Growth Engineer. I cover the things I noticed software engineers struggle with the most:
Knowing what to design
Dealing with unclear requirements
How much detail to put in a design
From software back to house design
This post is about a concept from software that reshaped my perspective on building design.
I learned about abstraction from software engineering. When I apply the concept to house design, I see many abstractions within houses. In fact, abstractions and standardization are what make modern construction possible.
Abstractions also give us the solution to another challenge with software design: when to stop designing.
When to stop designing relates to how much detail we design. When you've put enough detail, you're done. But how can you reduce the required level of detail to speed up the completion of designs?
Abstractions simplify designs if we:
Design at boundaries and encapsulate details.
Get the API right and allow inner details to change during implementation.
Stop when we've covered all the abstractions.
Abstractions in the home
What abstractions do houses have?
Doors and windows
Electrical sockets and light fixtures
Appliances like stoves and ovens
These abstractions have an expected way of working. There is a standard way to create and use them.
Electricity as a service
We can apply the lessons of how house design uses abstractions back to software design. Take, for example, electrical sockets.
The socket is an interface that provides the service of "electricity." Power delivery to a house has many details:
Wires
Breaker boxes
Power poles
Distribution nodes
Power plants
Fuel
Electrical sockets are interchangeable interfaces. Anything implementing the interface can get power. Thousands of devices work simply because they implement the socket interface and protocol.
As a homeowner, you don't care about these details. You plug a device into the socket, and it runs. You care that outlets are near furniture and have enough power to run everything.
You don't care about any of that as a house designer, either. You only care how an electrical socket functions and serves the homeowner. You know the value it provides, and you describe where to place them.
Abstractions hide complexity
The same is true for software. Users don’t care about the complexity or technology behind it. They don't think about how they're using an abstraction (a UI, a button, an API) or the fundamentals of how it works. They just know that using the interface gives them the desired behavior.
Standards and abstractions make it so you only need to design where to put the socket—not what it is.
As designers of software or houses, we only need to define abstractions and interactions—not the internal details.
Abstractions are reusable and interchangeable building blocks. They limit the concepts a designer and builder need to know. This complexity-hiding is what makes them perfect for simplifying designs. Designers only need to consider the details that they can't ignore. The builder can figure out and adapt the rest of the details.
When designing software or houses, focus on which abstractions to use and how they interact. Designing the interface is more important than the internal details.
For example, when we describe a new part of a complex system, we do not need to explain how the other parts work. We focus on the new component and treat all the existing components as abstractions.
Say we want to add a Project component. The existing Payments and Database components are abstractions. In the design, we don’t need to know how they work. Only the APIs that support customer requirements are important.
How will users interact with Projects?
How will Projects interact with the Database?
How will users pay for Projects?
The Projects component itself is an abstraction. The API (HTTP port or code interface) is like the electrical socket. The details of how it works within the boundary are irrelevant to the design.
When to stop designing
What's important is describing the interactions at the boundaries:
Describe interactions between components using API documentation formats like OpenAPI.
Describe complex order of operations with sequence or flow diagrams.
Those who build it have the flexibility to figure out the inner details.
You only have to design the internals of an abstraction if it is novel. Even then, you only have to design it enough so that the builder understands the interactions.
We stop designing when:
We've identified all the abstractions.
Everyone understands how they interact.
There is usually no need to go deeper. The design is good enough to move forward.
But it isn’t finished. As the building progresses, new issues will arise. Update the design with any significant changes as they occur.
What I’m reading
- has a good physics analogy for software: Entropy - Mental Model.
How to propose an impactful improvement to the codebase and own the implementation by
delivers on its title.- explains Chaos Monkey in How Netflix Uses Chaos Engineering to Create Resilience Systems 🐒
I love the analogy! ❤️
When using an analogy, it's important to elicit the differences, otherwise it's not truly an analogy. One notable difference that comes to mind is malleability. Additionally, user needs and software instability seem to be closely linked, as users may not realize if the abstraction is good until they actually use it. These differences illustrate the opposing aspects of the soft vs hard design in place here. For instance, until a user interacts with your api, it's impossible to know if the data structure (which shapes the interface, much like a plug shape in the analogy) is truly useful.
The flexible nature of software means that design changes often occur during the coding phase, and these changes may not receive the same level of review as high-level design changes. This can result in important modifications happening without the necessary attention. Due to the malleability of software, it can be challenging to maintain a high-level design throughout the building process. This creates a more fluid and less straightforward development process compared to designing hard artifacts.
I love it!
Part of my job now, before implementing a new feature, I was asked to write a technical design docs. Honestly, I don't know where to start and this post is super useful. I should focus on the boundaries and ignoring the implementation details in the design.
Do you have a template or how to write a good technical design doc that we can share/present to other devs?