Can Software Engineering Challenge Philosopher David Hume’s Theory of Abstraction?

What if one of history’s greatest philosophical debates about abstraction still shapes how software is designed today?

Nearly three centuries ago, philosopher David Hume challenged the existence of truly abstract ideas. In his book, A Treatise of Human Nature, he argued that the mind never possesses pure abstractions. Instead, what we call general ideas are particular experiences that we reuse through resemblance and habit.

As Hume wrote:

“All general ideas are nothing but particular ones, annex’d to a certain term…”

He illustrated this with a simple example. When we observe a white marble sphere, we perceive its color and shape together. Only after comparing it with other objects —a black sphere or a white cube— do we begin to distinguish color from form.

According to Hume, abstraction is not the perception of an independent idea but the result of comparing experiences and recognizing similarities.

This centuries-old philosophical argument becomes surprisingly relevant when viewed through the lens of software engineering.

Abstraction Beyond Hiding Complexity

Abstraction is often described in SWE books as hiding unnecessary implementation details. While that is certainly part of its purpose, in object-oriented design abstraction serves a broader role: abstraction identifies and models the concepts that best represent a domain.

Good abstraction is not simply about making software easier to use.

Good abstraction is about discovering the concepts that best describe a system, whether those concepts correspond to real-world objects or exist only within the problem domain.

A Customer, a BankAccount, or a Transaction represent tangible entities. However, concepts such as Strategy, Observer, Repository, or MessageBroker are purely conceptual constructs created to model behavior and relationships. Their value lies not in physical existence but in how effectively they represent ideas.

The Four Pillars of Object-Oriented Programming

These ideas are reflected in the classical principles of object-oriented programming:

  • Abstraction identifies the essential characteristics of a concept while omitting unnecessary details. In languages such as C++, abstract classes with pure virtual functions come closest to representing a “pure concept” because they define behavior without permitting direct instantiation.
  • Inheritance enables new concepts to extend existing ones while preserving shared behavior.
  • Polymorphism allows different implementations to satisfy the same conceptual interface.
  • Encapsulation protects internal state while exposing only the behavior necessary for interaction.

Although often discussed together, these principles solve different problems. Encapsulation controls complexity. Abstraction defines meaning.

Different Languages, Different Approaches

Programming languages provide different mechanisms for expressing abstraction.

  • C++ emphasizes explicit abstraction through abstract classes, templates, and generic programming, offering exceptional expressive power at the cost of greater complexity.
  • Java and C# balance abstraction through interfaces, abstract classes, and managed runtime environments, making large-scale system design more approachable.
  • Python emphasizes behavior over structure, allowing abstractions to emerge from what objects do rather than from where they inherit.
  • C provides very little language-level abstraction, leaving most conceptual modeling to the programmer.

Each language reflects a different philosophy of how abstraction should be expressed.

From Cognitive Abstraction to Computational Abstraction

According to philosopher David Hume, ideas are connected through three principles of association: resemblance, contiguity, and cause and effect.

Among these, resemblance plays a central role in abstraction, as general ideas emerge by comparing particular experiences and recognizing their similarities rather than from concepts existing independently of experience.

From this perspective, Hume argued that the human mind cannot possess pure abstract ideas independent of particular experiences.

Programming languages, however, provide formal mechanisms for representing abstract concepts through interfaces, abstract classes, and generics.

Whether these constructs constitute true abstract ideas or simply symbolic representations remains an open philosophical question.

Nevertheless, software engineering demonstrates something remarkable: programming languages allow abstract concepts to be expressed explicitly, making it possible to model complex domains through formal representations.

Therefore, programming languages are themselves abstractions —formal systems designed to express other abstractions.

Interfaces, abstract classes, generic types, design patterns, and other software engineering mechanisms are not merely language constructs or development techniques; they provide the means through which abstract ideas can be represented as computational models.

In this recursive hierarchy of abstraction, software systems emerge as layered representations of meaning, enabling increasingly complex domains to be modeled through successive levels of conceptual abstraction.

Based on this reasoning, software engineering does not contradict Hume’s theory of abstraction. Rather, through programming languages, it provides semantic mechanisms for representing the abstractions that originate through the cognitive process described by Hume, allowing them to be formalized as structured computational models.

Questions for Reflection

  • Does abstraction disappear once complexity is hidden, or does simplification merely create a new layer of abstraction?
  • Is abstraction fundamentally a cognitive mechanism for understanding reality, with programming languages serving as one of its most formal expressions?
  • Does the expressive power of a programming language arise from its ability to represent increasingly abstract models of a domain rather than from the number of language features it provides?
  • If abstraction defines meaning, at what point does an abstraction become so simplified that it no longer preserves the meaning it was designed to represent?
  • Do programming languages merely represent abstract ideas, or do they enable entirely new forms of abstraction that the human mind alone could never sustain?

Abstraction and Software Development in the AI Era

In the age of AI, the value of software development is shifting from writing code to formalizing ideas.

As AI becomes increasingly fast and capable of generating useful and reusable source code, the core activity is no longer simply implementation —the act of writing code— but identifying the right abstractions, validating AI-generated code, and orchestrating the components that transform those abstractions into complete systems.

Therefore, the future of software development is already emerging: it may no longer be defined solely by writing code, but by thinking more precisely about the ideas that code is meant to represent.

Perhaps abstraction is not simply a programming technique.

It may be one of humanity’s fundamental cognitive tools for understanding, organizing, and engineering reality itself.

The Retrospective Nature of Experience

Experience is often seen as a sequence of events that simply happen throughout life. Yet experience alone does not necessarily produce knowledge. It is through retrospective reflection that we transform experiences into knowledge, recognizing patterns, refining our understanding, and developing new perspectives.

We often believe that knowledge comes mainly from books, articles, history, and the experiences documented by other people or organizations. During the initial stages of learning, that is often true; we begin by observing, studying, and understanding what others have already discovered and done.

However, knowledge does not develop only through external sources. A child learns to walk and speak first by observing, then by imitating, and finally by practicing. The same principle applies throughout life. Our own experiences become a source of knowledge, allowing us to overcome challenges, develop capabilities, and discover new perspectives.

The retrospective nature of experience means recognizing that every experience contributes to our growth when reflected upon. Some experiences deepen our knowledge vertically within a specific area, while others expand our understanding horizontally across different fields, connecting ideas and increasing our vision, skills, abilities, and scope.

This concept applies not only to technical or professional development but also to the everyday actions, decisions, and skills we develop as human beings. It allows us to understand the importance of seeing life as a continuous journey where our abilities and experiences become feedback for improvement, progress, and the positive benefit of humanity, society, and the environment.

Life is more than a collection of roles, jobs, titles, or external labels. Those are only temporary expressions of a much larger journey. Many people see work as the final destination of life: completing responsibilities, solving organizational challenges, and achieving objectives.

However, beyond those responsibilities, there is a human being experiencing life, reflecting upon those experiences, developing a broader vision, expanding awareness, shaping new perspectives, and continuously evolving. Expanding awareness represents the process of increasing one’s understanding and perception of oneself, others, and the world around us.

Was zero first then one?

At the beginning, there was a problem to solve, a process to improve, a solution to implement. The biggest problem the most abstract solution required, the most abstract architecture the simplest code snippets to implement, this is how it should be, how could it be otherwise?

The Tao of Programming depicts the problem as follow:

(§1.1) «Something mysterious is formed, born in the silent void. Waiting alone and unmoving, it is at once still and yet in constant motion. It is the source of all programs. I do not know its name, so I will call it the Tao. The Tao of Programming flows far away and returns on the wind of morning. The Tao gave birth to machine language. Machine language gave birth to the assembler.»

(§1.2) «The assembler gave birth to the compiler. Now there are ten thousand languages. Each language has its purpose, however humble. Each language expresses the Yin and Yang of software. Each language has its place within the Tao.»

(§1.3) «In the beginning was the Tao. The Tao gave birth to Space and Time. Therefore Space and Time are the Yin and Yang of programming. Programmers that do not comprehend the Tao are always running out of time and space for their programs. Programmers that comprehend the Tao always have enough time and space to accomplish their goals. How could it be otherwise?»

Writing understandable reusable pieces of software is a task that requires not only strong fundamentals but order and simplicity. Continuous improvement is a well-known quality assurance technique in manufacturing industry: check-do-check. Code remanufacturing should not be a postmortem task but a task parallel to the development phase.

Successful projects require united-enthusiastic teams, people that do not feel comfortable with their job will never deliver extra effort, and most of the times will not even perform quality tasks.

Selecting a technology because of personal moods is the same as selecting a technology
because it’s trendy, it’s a bad decision. Even more, the general architecture of the solution should be chosen for each layer in a manner that changing a piece do not affect all over the solution. Normally the best architecture is that selected by a rigorous team after many hours of discussion, the Architectural Design Document should be figured out in time, not only because it defines the meta-structure of the project but its development success depends on it.

No matter what the development methodology you decide to use, good analysis produces good design artifacts, good design artifacts facilitate coding phase. By selecting simple templates for analysis & design artifacts, changes to them will be done fast. Remember: «Continuous attention to technical quality and good design enhances agility», «working software is the primary measure of progress». Hope you can find something useful here, in the same way, I hope to find something useful over there!

The aim of art is to represent not the outward appearance of things, but their inward significance. — Aristotle

zeros_ones_chain