Importance Of Technical Design

How To Use Technical Design To Improve Your Code

Software engineers from those graduating from coding boot camps or college to seasoned experts are continually trying to improve their skills. Mastering technical design is the end game skill that will tip the balance of making or breaking technical projects. Mastering technical design is a career-long journey.

Technical design is not something easily learned overnight nor quickly absorbed through books. It takes time and experience to learn and apply correctly.

It is not enough to say everything needs thorough technical design consideration. Too much can cause other issues called “over-engineering.” There is Where is the balance? I’d say the balance comes with experience, but ultimately even the most experienced engineers may still get it wrong. Diversity in thought and experience will help make better designs.

Let’s explore what technical design is and start learning how to improve on it.

Pragmatic code vs. code design

Programmatic code and logical execution blocks are the building blocks of technical design skills. These building blocks are where we cross into the concept of layers, concerns, boundaries, and architecture around the execution of code blocks.

Programming is logical. It is mathematical in nature and takes inputs and returns outputs in a scientifically measurable manner. However, technical design is more about arranging all those various pragmatic execution blocks into a system. A good system makes it easy to build complexity on top of it. It isolates business logic from other logic and is relatively easy to add to and remove when business logic changes.

let’s dive into a high-level story that will help identify design decisions. Decisions that inevitably lead to bad designs and hard to maintain architectures.

A team’s journey that leads to a lack of technical design

The setup

In the beginning, a team of software engineers from various levels of experience is tasked to build a new original project with no legacy code. The team decides to split the project into units of work (tasks) and spread them out over several months in iterative development cycles. Every team member is a solid coder and can crack any algorithmic puzzle in under 40 minutes.

However, none of the team members have strong skills and experience in complex system design, patterns, or strong engineering. The team agrees on a specific technology stack and programming language to use, sets up a source code repository and a build system, and sets off on their agreed-upon tasks.

Many modern frameworks for web services and UI are fairly opinionated and help regulate your code’s structure. This baseline structure is there to start the team off in the right direction as it forces some degree of consistency. Following what the framework gave them, the team makes good progress. They even get ahead of schedule after several months. They are checking in code to the repository and knocking the tasks off the list. Things are looking good for the team as they show unprecedented speed in building the product.

The crash

Time passes, then the business needs to pivot. The company fell through with the existing deal and ended up acquiring another deal. As a result, the design needs to change radically, but the deadlines do not budge. The engineering team has been diligent about accomplishing each of the original tasks. However, most of the work does not share any infrastructure or common code with one another. They step back and assess the changes needed to meet the new changes in requirements.

After all of the progress made, the situation is such that business logic is mashed right into the code’s core. It is coupled like glue, code is duplicated worldwide, and cross-cutting concerns are all handled differently in various sections of the code. They have no shared core between them.

Then, they come together and find their best options are large refactors to change significant swaths of the business logic baked throughout. The alternative is a total rewrite. Therefore, the cost of either option blows their estimates beyond the business deadlines and almost doubles the original engineering cost. The situation turns dire, and a hard conversation is needed to be had.

You’d be surprised at how often the story is repeated. It happens at large companies and startups. Sometimes the teams pull off magic and make things turn around, but more often, those projects fail and need to be restarted or revived. Learning how to build the foundation of a project and a system that is extensible through the art of technical design is a fundamental skill.

The art of technical design

A well-thought system utilizing strong technical design concepts would have made the blocks of code better engineered and more robust to withstand the business changes. This concept is known as “loosely coupling.” Changes will always happen. Better-designed code is more pliable and less costly to change and, often with less regression risk. We have all experienced a codebase where a team does not want to touch it due to regression risk.

The “art” of design is understanding how all the execution blocks of code need to piece together. They become reusable, isolate concerns, and make a loosely coupled system that is robust to change. Each moving piece and system in the codebase forms a specific purpose and responsibility. These pieces complete the overall needs of the software. If an engineer new to the codebase can quickly see that relationship and understand the overall design quickly, it is in the right direction.

A team with virtually no system design that follows the basic outlines of a framework can build software with a minimal learning curve. While maybe efficient, a framework generally does not have the design needed to support large codebases for complex products alone. The simple design will crumble inevitably due to the lack of patterns and architecture, leading to inconsistencies and coupling throughout.

Examples one should expect of a loosely coupled system

  • Modules and components in the system are reusable. Good isolation of concerns and business logic makes a lot of code shareable.
  • Making logic edits to used modules does not create unreasonable side effects in other parts of the code. This includes potentially regressing expected behavior in seemingly unrelated code.
  • The code is highly readable, from the code blocks to the modules and components used. It is quick to observe dependencies.

Over-engineering

However, there is such a thing as an overly complex design. The cost of too much design emphasis can lead to complexity. Too much complexity makes it harder for other engineers to contribute, slows the pace of development, and makes it easier to make new designs that break expectations.

If a new engineer does need several weeks to contribute to a codebase, needs a technical manual, or needs training videos, then the codebase is over-engineered. Over-engineered projects will end up undermined (unintentionally) by other engineers that could not fully grasp the original design, leading to new designs that break consistency. Inconsistency allowed to blossom will turn an over-engineered project into a monolithic system.

An important acronym comes to play here: “KISS” or Keep It Simple, Stupid. KISS in technical design emphasizes the artful side of software engineering. You want a loosely coupled and durable system design, but also you want it as lightweight and straightforward as possible. This allows anyone jumping in to understand it quickly.

Examples of over-engineering in a system

  • The design includes adding multiple opinionated frameworks/libraries.
  • Several various complex systems that each require highly different semantics.
  • Subsystems and patterns added that do not solve a fundamental problem for the technical system or business.

The balance is in the technical art

To summarize, I’ve reviewed how critical having a thought-out system design is and how having too much-thought leads to over-engineering. Going back to where we started at the beginning of this article, technical design is an art. There is no shortcut path beyond reading from others and sheer experience on your own. The balance between too little design and too little design is a real struggle.

Ultimately, business decisions will drive the balance. In addition, the engineering teams need to be a part of the conversation and acknowledge where the balance will be. Engineers will argue where the balance lies and take gambles with the codebase for trade-offs. Is it better to keep it simple or add in systems to handle the complexity that may or may not be required in the future?

Understanding the history of the code and the design decisions is essential before laying judgment. Your co-workers will help fill in knowledge gaps by often being quite willing to share their thoughts and knowledge over the historical decisions that shaped the codebase. Business decisions tend to drive the direction and result.

In conclusion, understanding the design considerations that make a codebase a thing of beauty instead of a nightmare to work on will take you far. Being able to identify and discuss these considerations will make you invaluable to your team and product.

Improve your technical design skills

Next, let’s dive into specific areas of design below that will help hone your skills.

Code And Design Reviews

Promoting Loosely Coupled Concerns

Reducing Repetitive Code

Code Readability