Metaphysical Vs. Empirical Programming
Broadly speaking, there are two modes of programming — either you are writing code by adopting a metaphysical orientation/approach, or you are writing code by adopting an empirical orientation. These approaches do not necessarily result from a programmer making a conscious decision, as in “I am now going to write a program by starting from a certain metaphysical premise.” Most of the time (if not almost all of the time), such decisions are being made tacitly, even unconsciously.
Why am I dragging philosophy into the discussion on programming? Isn’t academia the bane of pragmatic approach to programming? Slow down now, let’s not be hasty; let’s tone it down a bit, we don’t want to toss the baby out with the bath water. Firstly, let’s recognize that a lot of terrible things in software have been hoisted upon unsuspecting consumers thanks to the uncritical adoption of the cargo cult of pragmatism at the expense of everything else. How many times have we worked on a project where the pointy haired boss put the gun to everyone’s head and insisted that we cut corners in the name of pragmatism? In my case, more times than I care to remember.
Secondly, let’s also give due recognition to the fact that unbridled academic research is the prime reason we all have jobs as programmers (OK, some of you more advanced readers are now enjoying your jobs as software developers, but to me that’s a distinction without a difference). Whatever got you there keeps you there, so please do not turn your noses on academic research, including the discussion on the difference between metaphysical and empirical programming.
What are the defining characteristics of metaphysical programming? As is the case with any branch of metaphysical reasoning (which takes certain unverifiable premises as their starting point), metaphysical programming always takes one empirically unverifiable premise as its starting point. Which premise would that be? It’s the metaphysical premise of identity. Now, why is that premise empirically unverifiable? Hm, it’s a philosophical question — tomes of content had been written throughout the ages explaining the empirically untenable concept of identity. We won’t repeat the exercise here; suffice it to say that in the world where everything keeps constantly changing, it gets extremely difficult, almost impossible, to establish an defensible concept of identity. The best we can do is admit that identity is merely a convention, a socially constructed consensus that is impossible to verify with one’s own faculties (i.e. both sensory faculties and rational reasoning faculties). Failing to verify this concept empirically, we’re forced to either adopt it as an unquestionable knowledge bestowed upon us by the generally accepted authority of consensus, or to flatly reject it.
So a programmer who starts from the metaphysically founded idea that phenomena they are trying to model have constant, permanent identity is the programmer who is building software based on metaphysics. In such a context, the programmer is modeling abstractions by identifying various phenomena, singling them out as entities, and then assigning unique identities to such modeled entities. For example, if such programmer is modeling a customer, then that customer will be assigned a unique identity. This unique identity, which is a metaphysical concept (since it does not correspond to any empirically verifiable phenomena), will then be used to track that customer throughout the passage of time.
In contrast to metaphysical programming, empirical programming is a discipline that does not waste time on assigning and maintaining any identity to the phenomena being modeled. Instead of being focused on identity, empirical programers are focused on domain. Once the domain has been properly modeled, the notion of time gets introduced, which transforms domain phenomena into so-called range phenomena (more on that a bit later).
This is a fundamental, and also a considerable difference. The moment identity leaves the picture, everything radically changes. In order to understand such radical differences, we need to first examine the fundamental nature of software modeling/development.
Everything Boils Down To Two Things
When we examine the workings of a real life software product (a computer program), we basically find two aspects:
When programmers/developers model the system under study with the intention to fully describe it, they often tend to focus on events that would be of interest. When events occur in the system, they are invariably associated with some values. At the outset, when the context that is about to trigger the event is set, the accompanying values are viewed by the programmers as domain values. After the event transpires, the domain values get transformed (more precisely, mapped), after which they emerge as range values. So in this regard, software modeling boils down to the following sequence of actions:
- Identify the event of interest
- Set the context that enables the event to run (i.e. set the domain values)
- Run the event
- Collect the transformed (mapped) values (i.e. the range values)
In the above arrangement, running the event amounts to mapping the domain values into the range values.
How does the above basic workflow get handled by metaphysical programmers, and how does it get handled by the empirical programmers? Obviously, the approach will be different, based on one’s initial orientation. Let’s examine the line of reasoning that typically gets followed by metaphysical programmers:
When metaphysically minded programmers focus their attention on the desired solution, they base their initial orientation on the fundamental idea of inherent identity. After the phenomena that is involved in the modeled system get singled out into entities with unique identities, each entity assumes certain responsibility (or, collection of responsibilities). Starting from that premise, and from the resulting solution design, the metaphysics-oriented implementation code ends up dealing with maintaining shareable state that gets assigned to entities in the form of attributes.
Let’s now compare the reasoning about the same problem space the way empirical programmers typically employ:
Unlike metaphysics-oriented programming, which is responsibility-driven, empirical programming is event-driven. Events are not entities with unique identities, so they cannot be saddled with any responsibility. Events are transient, they are impermanent, they are not compound phenomena, and can basically be viewed as agents of transformation. A simple event, say an incoming customer call, can potentially result in transforming a domain value (such as 42 products available on inventory) into a range value (such as 41 products available on inventory after the order has been fulfilled). Furthermore, another event (e.g. processing customer payment) will result in transforming the domain value of daily revenue equalling $42,000 into the range value of daily revenue equalling $42,100 (assuming that the customer has paid $100 for a purchased product).
In the above example of empirically oriented programming, we see that there are no entities with associated responsibilities. Instead of worrying about constructing and maintaining entities that are responsible for taking care of their own state, empirically-minded programmers simply care about getting things done. And if their programming logic manages to capture desired events (such as responding to the customer call and then collecting payments from the customer), and furthermore if their implemented logic manages to accurately map the input values (i.e. the domain values) onto the output values (i.e. the range values), then, to all accounts, the job of a software program has been correctly fulfilled.
Yes, But What About Persistence?
While capturing events that map domain values onto range values might be an interesting exercise and a refreshing way of viewing information processing, at the end of the day all those transient occurrences must be retained on the system for future use. So how do we go about persisting those resulting range values?
There is a significant difference in how would metaphysically minded programmers go about implementing persistence compared to how would empirically oriented programmers do it. This difference is completely and entirely governed by the presence (or absence) of the concept of identity.
In the case of metaphysical programmers, who unreservedly and wholeheartedly subscribe to the worldview imbued with lasting identity, the entities must get persisted together with their unique identifiers (also known as primary keys). Once persisted, such entities are susceptible to destructive actions, such as their attributes may get updated (old values get replaced with new values), or the entity itself may get destroyed (deleted from the system).
In the case of empirical programmers, there is no need to persist any entities, nor is there any need for subsequent destructive actions. The only thing that is worth persisting in the empirically minded world is the event log. Here is how empirical programmers view the notion of persistence:
Our world appears to be an endless stream of events. Some of those events are more important to us than some other events, and so we are intent on recording such events. Once such events get recorded, they become facts. Once they become facts, they are immutable (at least until someone invents a time machine that could let us visit the past and alter some events). For example, the fact that I was born on March 16 is an immutable record of birth. No matter what might happen in the future, that fact will never change.
We have seen that both metaphysical and empirical programmers tend to focus on events, domain values and range values. Where the two camps differ is in the level of simplicity. While, as we’ve seen above, the empirical camp prefers to keep things radically simple (i.e. abstaining from constructing entities and assigning identity/state to those entities, plus abstaining from complicating persistence concerns by simply recording event logs), the metaphysical camp prefers to introduce complexity. This complexity is manifested in the act of imputing empirically unverifiable identity into entities modeled after the conventional, customary way of thinking. Rather than being content with merely capturing the fact that an event of interest has occurred in the system, metaphysical programmers rush to build an overarching infrastructure on top of the simple recorded facts. This overarching infrastructure introduces a lot of additional problems into the picture, problems that can never be detected within the empirically-driven analysis and implementation.