Modular Design in Computing

Modular Design in Computing
Photo by Vishnu Mohanan / Unsplash

 Modular design in computing refers to the idea of breaking down complex systems into smaller, more manageable units that can be easily combined and reassembled. This approach has a long history in the field of computing, with its roots dating back to the early days of electronic computers.

 One of the earliest examples of modular design in computing can be found in the use of logic gates. These are basic electronic circuits that perform a specific function, such as AND, OR, or NOT. These logic gates can be combined in various ways to create more complex circuits, such as an Arithmetic Logic Unit (ALU). An ALU is responsible for performing basic mathematical operations within a computer, such as addition and subtraction.

 ALUs are just one example of how modular design can be used to create complex systems from smaller, more manageable units. Another example can be found in the use of microcontrollers and CPUs. These are the brains of a computer, responsible for executing instructions and controlling other components. Both microcontrollers and CPUs are made up of multiple subcomponents, such as ALUs, memory, and input/output devices.

 Modular design has several benefits in the field of computing. It allows for greater flexibility and customization, as different components can be easily swapped in and out to meet specific needs. It also makes it easier to troubleshoot and repair problems, as each unit can be tested and replaced individually. Additionally, modular design can make it easier to scale up or down, as more or fewer units can be added or removed as needed.

 Modular design has played a crucial role in the development of computing technology. From the use of logic gates to the creation of microcontrollers and CPUs, this approach has allowed for the creation of complex systems from smaller, more manageable units. It is an approach that continues to be used today, and is likely to remain an important part of computing for many years to come.

 Modular design is a concept I first encountered when I took my first programming course. I encountered it as the black box effect. It's the idea that you only need to truly understand the inputs and outputs of a system to use it. You don't need to understand the internals of a functional piece of code as long as the inputs and outputs are all what you expect. As time went on, I encountered modular design is nested in modern day computing both in the hardware and the software.

 Let's first just say, computer science, physics, and engineering is all just applied math. XKCD even has kinda pointed this out in a funny diagram:

You're not a purist if you've never seen an XKCD comic.

 Modular design really comes from mathematics. You have some function that has an input and output. You may have seen this in the past in a format such as:

f(x) = 3x or y = 3x

 This would be something you would see in an algebra class. Where basically all you're doing is multiplying an input by 3 and getting an output that is three times larger. You can then start piecing together functions in a modular way:

f(x) = 3x
g(x) = 2x + 1
h(x) = f(x) + g(x)

 You can have functions defined in terms of other functions, but it's not entirely clear why you would care about this without seeing the applications.

Modular Design in Hardware


 It all starts with the signal in a wire. You apply some electricity with a small amount of current and make the voltage go up and down over time. The signal looks like a rectangular squiggle:

 You can notice that the peaks and valleys are stationary in the above measurement of a digital signal going through a wire. However, what you should understand is that this is happening over time. It's moving up and down over time. It's a wave, not unlike the one you see at a beach. If you stand still on a beach and look out at a buoy riding a wave. As it rides this wave, you can imagine the buoy's high points as it gets pushed up and its low points as it gets pushed down.

 If we imagine ourselves watching a buoy that is going up and down. Over time we can see the water change from really wavy to maybe very calm. It's not always constantly wavy. We can then tell a story over time of the conditions of a body of water with just the up and down pattern of a buoy. The high points and low points of a wave tell a story.

 We can't simply control the wave in a body of water at the beach but we can easily control the voltage that goes up and down in a wire. Over time it tells a story of high points and low points. We can record this story as 1s and 0s:

01010101010101010000000000000000

 This is why computers use 1s and 0s. It's predicated upon the idea of measuring the highs and lows of a signal through a wire over time. However, it uses more wires with more inputs and more outputs. For example, you can have 2 inputs and 1 output. You can have it where two wires go into a thing called a logic gate and one output comes out.

INPUT A ───|&&|
           |&&|─── OUTPUT C
INPUT B ───|&&|

 A logic gate's internal mechanisms don't have to be understood for us to use them. We just have to know the inputs and outputs.

 We can extend our metaphor of the buoy and say each input is a buoy. We got two buoys out on the water and we want to know when they are both up at the top of a wave because that may inform us of really wavy waters that day. Let's say we want to know that. We'll think of the output as the person who lets us know when they are both up at the top of a wave. We can label the two buoys input A, and input B. The person who has to shout to us about when they are both up is output C.

 Since we know that each input has two possibilities, we can basically know that there is a limited number of scenarios that can play out at any moment in time. There are infact 4 scenarios.

  1. Both buoys are down. Person C is quiet.
  2. Buoy A is down. Buoy B is up. Person C is quiet.
  3. Buoy A is up. Buoy B is down. Person C is quiet.
  4. Buoy A is up. Buoy B is down. Person C is telling us that both buoys are up.

 This is repetitive and fairly annoying to write out like that. So people have simplified it by putting it into a thing called a truth table:

A B C
0 0 0
0 1 0
1 0 0
1 1 1

 We can just imagine that the 0s and 1s of the person (output C) we have communicating this information to us is 1 when he's shouting to us across the beach and 0 for when he is quiet.

 Normally people just call this an AND gate. We just want the person on the beach telling us when both buoy A AND buoy B are up at the top of a wave.

 There are other logic gates. That have their own scenarios:

 Now, I'm not going to extend the metaphor of the buoys anymore. You can imagine some very funny scenarios for each respective logic gate as to why a person should be shouting to get your attention about buoys.

 What's interesting is that every gate can actually be made out of a NAND gate by wiring them together in various different configurations.

 Basically, we've arrived at the point where we started. We can define the functionality of a part in terms of other parts. Instead of it being just an abstract concept in algebra, it's now low-level logic of what drives electronics and computing.

 We can go ahead and combine logic gates into different ways and start creating basic math operations with it by making something that is called an ALU:

 You can find an ALU inside pretty much any processor or CPU:

Die photo of the 8008 microprocessor, showing important functional blocks.

 So this 8008 processor chip is basically a module that looks like this:

 Now we can see how there is modular design inherent to the hardware of every computer and modern day electronic device. Each chip could just as easily be referred to as a module. This 8008 module has a submodule that is an ALU module. That ALU module has a bunch of submodules that are likely a combination of NAND modules. It's only slightly more complicated than this. However this modular design is inherent to the hardware that software lives on. Now you can get a glimpse of the forrest through the trees. The prevalence of modular design runs deep in both the embedded and the software side of computing.

Modular Design in Software


 Modular design is not just limited to hardware components, it is also a common principle in programming languages. In the Python programming language, it is common to call files written in Python "modules." These modules can be imported into other programs and used as a building block to create more complex systems. This allows developers to create reusable code that can be easily modified and updated as needed.

 Another example of modular design in programming can be found in the Flutter framework. Flutter is a framework for building mobile apps that utilizes the Dart programming language. One of the key features of Flutter is the use of widgets, which are inherently modular in design. Widgets are small, reusable components that can be easily combined to create more complex layouts. This allows developers to create custom interfaces quickly and efficiently, without having to build everything from scratch.

 Widgets in Flutter are similar in functionality to components in the React framework. React is a popular framework for building web applications, typically written in JavaScript. Like widgets in Flutter, components in React are modular in design, allowing developers to build reusable code that can be easily modified and updated.

 The Rust programming language is another example of how modular design is built into a programming language. In Rust, developers are not allowed to create classes, only modules or traits. This approach encourages the use of modular design, as it forces developers to break down their code into smaller, more manageable units. This can make it easier to troubleshoot and maintain code over time, as each unit can be tested and modified individually.

 Modular design is a common principle in software, with examples ranging from Python modules to widgets in Flutter to components in React. By breaking down code into smaller, more manageable units, developers can create reusable code that is easier to maintain and modify over time. This approach is likely to remain a key part of software development for many years to come.