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.
