The problem with traditional approaches to abstraction and encapsulation is that they aim at complete information hiding. This characteristic anticipates being able to eliminate programming from parts of the software development process, those parts contained within module boundaries. As we’ve seen, though, the need to program is never eliminated because customization, modification, and maintenance are always required—that is, piecemeal growth. A better goal is to minimize or reduce the extent of programming, which implies providing mechanisms that allow small changes to largely already correct code.

Richard Gabriel

We are constantly deceiding if functionality should be seperated to its own unit, or not. Most software principles would plead for many classes, abstractions, public methods, and the need to remove code duplication. This leads to loads of dependencies, complexity and an eventual inability to properly introduce change. Which, in most cases, is not necessary and avoidable.

Don’t repeat yourself (DRY)

Don’t repeat yourself” (DRY) is a principle of software development aimed at reducing repetition of information which is likely to change, replacing it with abstractions that are less likely to change, or using data normalization which avoids redundancy in the first place.

Code reuse requires a process or policy. Where do we store shared code? We might create a shared kernel for single projects, but how do we share code across the enterprise without knowledge of the existence of code. We might have some intuition related to the age and complexity of the company in the form of “someone must have made this before”. This is not reliable for locating existing code. So we must properly name our code, and abstraction does not help in this case. Basically, abstraction allows us to reuse code, but it also causes an inability to actually reuse code due to obfuscation.

This also relates to the issue of inheritance. When our superclass is more than one or two “parents” above our current code, how can we know? Looking from the side of the superclass, change is even more dangerous; we might touch many subclasses, introducing bugs and concepts like dogmatic TDD. At least, we now pretty much know inheritance isn’t the silver bullet it promises to be. And as such, software seems to be leaning towards not inheriting more than 1 parent deep and avoiding inheritance as a whole.

Besides the practical implications of not repeating code, there’s also the question of whether we are actually repeating or if it just seems that way.

Most of the time it’s easier to just inline the code, it takes discipline to reuse existing code. We should remove the need for discipline and organise our code in such a way that allows the developer to repeat. This causes the developer to feel ownership over the code, deeper understanding and higher quality, a higher sense of safety when dealing with change and making testing simpler.

Seperation of Concerns (SoC)

A design principle for breaking down an application into modules, layers, and encapsulations, the roles of which are independent of one another.

An issue with this principle is that the meaning of the word concern can be freely interpreted. Would it be the concern of the CommandHandler to map outgoing data to the correct format, or would this be the concern of an external Mapper class?

If it were the concern of an external mapper class, this implies that it exposes a public method, which in turn implies that it can be used by more than one use-case. Which then in turn will reduce maintainability because more classes rely on our mapper class; which are obscured from the mappers point of view, removing its ability to freely change.

On the other hand; if we were to look at the mapper as solely a concern of the CommandHandler it would allow us to localize behaviour and write highly specialized version(s). It also allows for change more freely, since we know nobody else depends on our mapping functionality.

Another example would be connecting to a database. It would seem obvious that it’s not the concern of a CommandHandler to know how to connect to the database, just that it can. In most cases this would be true, but imagine a small Lambda hosted in the cloud; in that case it would be more imaginable to inline the connection logic.

So while it is good practice to separate concerns and properly break down your applications. It hardly makes a good case for not mixing concerns to form a greater concern.

For many years we were puzzle-solvers, focused on turning algorithms into sets of instructions to be followed by a computer. We enjoyed solving these puzzles, and we often viewed both the complexity of the puzzle and the obscurity of the solution as evidence of our skill.. Aware of our human limitations, we have come to view complexity and obscurity as faults, not challenges. Now we write programs to be read by people, not computers

Harbison

Abstractions as Reality

We should look at imagined abstractions as things on their own; use categories, traits, and remove inheritance, whenever possible. While we can say that an abstraction is only an idea of some concrete object, we can also say that just by being an idea, it is a objective thing as well. In other word, a cat does not equal an animal, and thereby does not extend from. It does however, implement everything it means to be an animal.

This is how we should look at behavioural classes. Keep the shape of its data specialized. Keep the mapping of data internal and private. Do implement its concerns as private functions, but keep the code local.

Move code only when it’s necessary, in other words; stay pragmatic. Abstractions and high separation does have a critical role.