The Value of Diverse Backgrounds and Foundational Principles in Software Engineering

By Sergey Nosov

May 13, 2025

Diversity of Backgrounds

Let us take a moment to acknowledge the diversity of backgrounds in Software Engineering.

As David Epstein argues in Why Generalists Triumph in a Specialized World, individuals who pursue nonlinear career paths often bring deeper problem-solving abilities and creativity when they shift into specialized roles like engineering. Your experience in biology, physics, business — wherever you started — contributes a unique lens to your software work. Domain knowledge, communication strengths, and analytical approaches from other fields are not detours — they’re assets.

At the same time, it’s worth remembering the importance of computing fundamentals. Topics like Boolean algebra and combinatorics may sound abstract, but they underlie so much of what we do. Boolean algebra is literally the foundation of digital logic and circuits, and combinatorics (the math of counting and combinations) shows up in algorithms, networks, and data structures. Some of these concepts can be challenging if they were not in your background. Understanding these fundamentals will make even stronger engineers.

Software Engineering Principles

David Parnas, in his seminal 1972 paper “On the Criteria to Be Used in Decomposing Systems into Modules,” taught us a lot about modularity and information hiding. He showed that if you naively break a system into modules by simply following the sequence of processing steps (like drawing a flowchart of tasks), you often get a design where changes ripple everywhere. In one example, he found that with a step-wise decomposition, “the system will only be comprehensible as a whole.”

In contrast, Parnas demonstrated a better approach: design each module around a design decision it hides from the rest. In his words, in the improved design “every module is characterized by its knowledge of a design decision which it hides from all others.” In short, each module should encapsulate and protect some piece of the system’s knowledge or policy. This means that if that policy changes, only one module needs to change. As he summarized, one criterion for good modularization is that each module “hides some design decision from the rest of the system.”

Applying this today, when we design a service or a component, we ask: what can we hide inside it so that other parts of the system don’t need to know the details? That’s information hiding and modularity.

Building on that, we know modular, well-encapsulated code is far easier to maintain. Parnas actually compared two designs and found that in a good modular design, you do not have to understand the entire system to work on one part. He wrote that in a good decomposition the knowledge needed for one module is hidden behind its interface, so the system is not comprehensible only as a whole. This property lets different developers work on parts independently and makes bugs easier to isolate. In practice, it leads to high cohesion within modules (each does one clear job) and loose coupling between them (changes in one don’t cascade).

Now consider abstraction and simplicity. Edsger W. Dijkstra taught us that simplicity is key to reliable software. He famously said, “Simplicity is prerequisite for reliability.” Complex code is hard to reason about and full of hidden bugs. So we aim to write small, clear functions and break problems into simple pieces. Relatedly, he reminded us that programming has a mathematical element and needs precision, but also that we must express ourselves clearly in code. In one quote, he urged us to convince people that simplicity and clarity – which mathematicians call “elegance” – are not luxury, but critical to success. The takeaway: before adding features, ask if the design is as simple as possible. Code is easier to maintain when it is clear.

Tom DeMarco brought a different, human-centered perspective. He pointed out that “the business of software building isn’t really high-tech at all. It’s most of all a business of talking to each other and writing things down.” In other words, people and communication are at the heart of what we do. We could have the fanciest tools, but if we don’t communicate requirements or write clear comments and documentation, projects fail. DeMarco also emphasized team dynamics in his book Peopleware: Productive Projects and Teams, but for now, remember that software succeeds when teams collaborate and communicate effectively. We write code not just for machines but for other people (including our future selves) to read.

As Donald Knuth once said in a similar spirit, let us “concentrate rather on explaining to human beings what we want a computer to do.” That’s a core reason we value clean code and good naming: it helps people understand and maintain our systems.

Grady Booch, one of the creators of UML and a great software architect, summed it up nicely: “The function of good software is to make the complex appear to be simple.” We hide complexity through good abstraction. When customers use our product or another developer looks at our code, they should see a simple interface or a clear algorithm, not the tangled complexity under the hood. This echoes the principle of information hiding we discussed.

Another early thinker, Meilir Page-Jones, focused on structure and maintainability. He noted that splitting modules by their cohesion makes a big difference. For example, he wrote that “almost always, you’ll improve maintainability if you split a communicationally cohesive module into separate, functionally cohesive ones.” Practically, this means each module or class should have a single responsibility or focus. If you have a module doing two unrelated things, you usually make it two modules so each one has a clear purpose. This drives home the same idea: structure your code so that each part is straightforward and does one job well.

Bertrand Meyer, the creator of the Eiffel programming language and advocate of design by contract, also has wisdom for us. He noted that in software development, “good development requires not just talkers but doers.” In other words, we have to balance design and planning with actual building. At some point, writing the code and testing it is what delivers value. Meyer also emphasizes correctness through clear specifications (his “contracts”), but the quote reminds us that execution matters too.

To summarize these takeaways:

These ideas – modularity, clean design, simplicity, and the human side of development – are timeless. They come from different eras and perspectives, but they all agree: software engineering is as much about people and clarity as it is about technology.

References