4  Software Design

Now that your code repository is set up, are you itching to start programming? Hold on for a moment!

One of the most common missteps I’ve seen junior developers take is jumping straight into coding without first thinking through what they actually want to build. Imagine trying to construct a house by just laying bricks without consulting an architect first—halfway through you’d probably realize the walls don’t align, and you forgot the plumbing for the kitchen. You’d have to tear it down and start over! To avoid this fate for your software, it’s essential to make a plan and design the final outcome first. It doesn’t have to be perfect—a quick sketch on paper will do. And you’ll likely need to adapt as you go (which is fine since we’ll design with flexibility in mind!). But the more thought you put into planning, the smoother and faster execution will be.

To make sure your designs will be worthy of implementation, this chapter also covers key paradigms and best practices that will help you create clean, maintainable code that’s easy to extend and reuse in future projects.

Avoid Complexity

A common acronym in software engineering is KISS - “keep it simple, stupid!”. While this is well-intentioned advice, it can only apply to individual parts of your code, as a full-fledged software is a system that consists of multiple components that interact with each other. Here, the best you can hope for is complicated, i.e., to avoid complexity (Figure 4.1).

Figure 4.1: (Software) Systems can be of different complexity. A script or function that linearly executes a set of steps could be considered simple. But most software programs are (at least) complicated: they consist of multiple components that interact with each other. However, these can still be broken down into manageable subsystems, which makes it possible to understand the system as a whole. A complex system, in computer science referred to as “spaghetti code” or a “big ball of mud” [1], contains many individual elements and interactions between them – when you change something on one end it is unclear how this will affect the other pieces as it is difficult to understand how all the elements come together.
Figure 4.2: Complicated systems in software design can usually be represented as hierarchies that show different levels of abstraction, e.g., in this case for plotting the results of a predictive model, i.e., creating a scatter plot that shows the true vs. predicted values together with the \(R^2\) value that indicates the overall performance of the model.