Software Architecture and Design: Why It Matters More Than You Think, Even for Your  Science Project

Humans are the weak link in software. Therefore, the sole purpose of good software architecture and design is to minimize the potential for human errors and inefficiencies when using or modifying existing software.

If you landed on this page, you must have wondered at some point what software architecture or design is and how it relates to your work. If you’re a scientist or non-software engineer who occasional must write code for their work, you might have brushed this topic off as just another term that “computer nerds” throw around. You might have been even criticized by those geeks for how you wrote your software but never understood why this would even matter. After all, you’re more interested in solving “real” problems and advancing your field, not spending time debating on how to organize your code.

But hold on, there’s a lot more to software architecture than you might think! In this article, we’ll be exploring what it is, why it matters, and how it can make your life easier by improving the quality and reliability of your work. Trust me, by the end of this post, you’ll be a software architecture convert!

What is Software Architecture Anyway?

It turns out that software architecture is a controversial topic, with many definitions floating around. Some experts in the industry distinguish between software architecture and design, with architecture focusing on the overall structure and organization of a system, and design focusing on the specifics of how the system will be built.

Others, like Robert C. Martin (a.k.a. “Uncle Bob”), argue that the distinction between architecture and design is artificial and that they are essentially the same thing. In his book “Clean Architecture: A Craftsman’s Guide to Software Structure and Design,” Uncle Bob argues that the difference between architecture and design is simply a matter of abstraction and the level of detail. For example, consider a large software system that consists of several subsystems. Each subsystem can be described by its architecture and its design. But what are those sub systems then for the overall system? Are they part of the design or architecture?

It should also be noted that software architecture usually describes several aspects of the software systems, which can be described by 4+1 views as summarized by Kruchten, Philippe influential paper (Architectural Bluprints, 1995). The views include the process flow (i.e., how does data and information flows in the system and how do components communicate), the physical view (i.e., how we organize the folders, source and other files), the development view (i.e., components, modules, and packages) and the logical view (i.e., class and state diagrams). However, for most of us scientist and non-software engineers the systems we build are usually quite small and not all of those views are necessary to consider. Most authors would probably rather refer to software design than architecture for the software-“systems” that we build at those levels of detail. 

Therefore the distinction between design and architecture is not really important for the purpose of this article and we’re going to take a broader view of the topic, encompassing both the overarching structure and organization of a software system, as well as the specifics of how it will be built. What we will discover can, in fact, be applied at various levels and to any of the 4 views.

The Confusing World of Software Architecture?

For scientists and non-software engineers, the world of software architecture can seem confusing at first. Writing code is often considered just a tool for solving the actual issues at hand, such as developing new algorithms, optimizing image and data quality, training new AI models, or analyzing large amounts of data. To many of us scientists and non-software engineers, thinking about the structure, organization, logic, or specific language constructs of the code itself seems counterproductive since it is a distraction from effectively solving the problems we are interested in. This is probably the reason why many scientists and engineers prefer to use interpreted languages like MATLAB or Python, which are simple to use and don’t require you to worry about the underlying structure of the code.

This quickly let us believe that the structure of the software is not important and doesn’t seem to matter, right?

Wrong! In fact, the structure of the software can be incredibly important, especially for solving and managing complex problems. From my experience this is true, even for those interpreted languages and we will see why.

So, if it is true that the structure – i.e., the architecture and design – matters, researchers and scientist are faced with two problems instead of one:

  • 1) the actual issue you’re trying to solve, and
  • 2) the structure and organization of your software.

Why Does Software Architecture Matter?

The thing is: the structure of the software doesn’t actually matter to the computer. You could write your code in any number of ways and as long as the underlying mathematical concepts are correct, the computer will produce the same result. Everything eventually gets translated into machine language, after all. It appears that the structure of our system does not have a direct impact on the product itself, which is fundamentally different from our intuition about architecture of buildings. There, the structure of the system has a direct impact on the function and integrity of the object itself. Take the Eiffel Tower. If you would reorder the beams the construction easily can lead to catastrophic failure.

So, if the structure doesn’t matter to the computer why should we care about good software architecture, whatever this means. Consider the following piece of code

function fubar2()
   fu = bar;
   fubar = super(fu);
   furchtbar = butInGerman(fubar);
   gift = wrapitup(furchtbar);
   mixTheMagic(furchtbar, gift);
end

Can you guess what this little function does? What if I tell you it produces the following graph?

If I would have provided the implementation of all the other functions (i.e., super, butInGerman, wrapitup, mixTheMagic), eventually, you might have been able to figure this out. Now, consider the following code, which produces exactly the same result:

function signalPlotting()
   exponential = PulseBuilder;
   signal = calcPulse(exponential, 'simple');
   signal = interpFFT(signal, 4);
   envsiganl = envelope(signal);
   figure();
   plot( signal);
   hold(gca, 'on');
   plot(envsiganl);
end

Isn’t it much easier to understand? Apparently, the structure and organization of software matters more to us humans then to the computer. Imagine, you need to use the first example (fubar2) in your code, every time you read your code you need to think about what it is doing, which will slow you down and making you inefficient. But more importantly you are more likely to make mistakes because it is hard to reason about the code.

The nature of software and the sole purpose of software architecture

Have you ever wondered what software truly is and why it was created? Well, the answer is simple: software is soft! It is meant to be easily changeable. Unlike hardware, software has the ability to adapt and evolve quickly. Take clinical ultrasound machines for example. In the past, all the signal and image processing was done in hardware. Consequently, developing new approaches was time intensive and costly. This changed when Dr. Ron Daigle suggested that all operations can be done in software instead. At the time this seemed difficult, because computers barely provided the necessary memory and computational power needed to quickly process the large amounts of data. Nevertheless, the new flexibility to fast prototype new approaches and algorithms for signal processing and image reconstruction from raw ultrasound data quickly led to new innovations such as fast plane-wave imaging or ultrasound elastography. What took years to develop before, was now possible to achieve in days.

However, as usual, with such great power comes great responsibility. The ease of changing software is also one of the main reasons why many systems are flawed and full of bugs. Software systems become complex quickly, and the multitude of dependencies and intricate workings often surpass the limits of human understanding and dexterity. It’s like trying to play a game of chess blindfolded, where every move has consequences, we can’t fully anticipate.

Therefore, the sole purpose of good software architecture and design is to minimize the potential for human errors and inefficiencies when using or modifying existing software.

In other words, trying to write good software means anticipating where humans might make mistakes or struggle when they must maintain, change, or use a software system.

Many software engineers develop a natural pattern for good architecture based on experience, but often can’t explain why they follow certain practices and guidelines. Some even refer to “bad code smell” to describe questionable code sections.

Bad software architecture will make it hard for humans to maintain over time.

But, with the definition above, these practices can easily be explained. A good example is one of the most common anti-pattern, “copy and paste”, also known as “don’t repeat yourself.” We are often tempted to copy lines of code and paste them in other places instead of writing a function or encapsulating in a class. It seems like an easy and fast way of developing code, doesn’t it? But what happens when requirements change and we have to modify the code? We would have to find all the places where we copied the code, make sure we make the same changes everywhere, and not forget a single location. With every copy, the likelihood of making a mistake increases. The result are symptoms you may have experienced yourself: inconsistent and buggy program behavior over time.

The S.O.L.I.D. design principles also make much more sense in this context. Take the single responsible principle, for example. A module or class should only have one reason to change. If a piece of code is too big, it becomes more likely that we’ll have to modify it over time, increasing the risk of mistakes. Or consider the Open-Closed principle, which states that classes should be open to extension and closed to modification. This is because extensions are usually less prone to errors. If we add a new class or function, it doesn’t have any dependencies yet, making it less likely to cause problems.

Dependencies, in general, are a major issue in software development because humans can only comprehend small, immediate connections. This is also why class inheritance often leads to hard-to-maintain software. Inheriting and overloading methods, especially if the inheritance-tree is deep, creates hard dependencies that are quickly hard to understand. Hidden dependencies, which are dependencies that are not visible from the code itself, are even worse.

Verdict

In conclusion, the next time you write code, consider not just the problem you are trying to solve, but also the problem of structuring the code to prevent potential issues in the future. Try to think about whether your code is easy to use or may lead to pitfalls if someone (including your future you) must change or modify the software in the future. Luckily modern literature on software design and architecture provides great guidelines to help you minimize human errors (You’ll find some good reads at the end of the article). Over time, you will become more efficient in following common good practices, which in turn will allow you to focus more on the actual problems you are trying to solve.

References


Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top