Start United States USA — software Transcending the Limitations of the Human Mind

Transcending the Limitations of the Human Mind

105
0
TEILEN

Here is how to put readers ahead of writers when it comes to abstraction, decomposition, and distractions while offering tips for leaks and code design.
No, this article is not about mind-altering substances, rather it is about how to deal with complexity in general and in software development in particular.
All but the simplest problems that we face in software development exceed the capacity of our minds. Our short-term memory can barely hold 7-8 items on a good day, and our computing capacity is almost non-measurable against computers.
How is it then that we can design and implement software monoliths of millions of lines of code despite our apparent limitations? How can we learn and understand such large and complex systems? How can we maintain such systems? How can we get better at dealing with large systems, or perhaps more importantly, deal with smaller systems more easily?
Everything means literally everything. Not just the business concepts, their rules, and relationships, but all the technical details we have to track, like remembering special meanings to certain values, whether one method must be called after another one, whether the object needs to be explicitly initialized before usage, what effect certain values would have elsewhere, and so on.
There are two conclusions to be taken from this theory if we want to maximize the amount of problem complexity we are able to handle (or minimize the difficulty of handling a given amount of complexity):
Every line of code is written exactly once (if we consider modifications a completely new line) but are potentially read many times during the life of the software.
Developers of large systems do not (on average) have more brain capacity than any other developer and they aren’t magicians either. What they do have, however, is a very simple trick to be able to cope with increased complexity.
Consider the following task:
Most people will not solve this task in one go, but decompose the task into smaller ones. For example, first solve „4 * 5“, then solve „3 * 2“, and then add the two results together. Instead of one big task, three smaller ones are executed, with the end result being the same.
Decomposing a problem is, unfortunately, not as easy as it sounds. In the previous section, we assumed that each of the resulting small tasks at the end was independent and, therefore, they were solvable without considering the other ones. This is, however, very rarely possible in software development. There are always dependencies between parts of software, between objects, modules, etc.
So why is this a problem? It is a problem because if we have to think about things coming from other tasks, we are wasting our Cognitive Capacity with stuff not directly relevant to the task at hand. Dependencies transfer Cognitive Load „up“, from the „dependee“ to the „depender“, ultimately defeating decomposition if left unchecked.
This method is called abstraction=i6\;;;;;;ry2 because the goal is to hide details and in turn offer higher level (more abstract) view of everything that is below, to the layers above.
This concept was already used above to solve this equation:
For the last step, adding the results of the two multiplications together, we don’t actually have to know how to multiply, we just have to know how to add. Why is the knowledge of multiplication no longer required at this last step although it does depend on the multiplication steps?
A leaky abstraction, even if it leaks just a little, can have a very big impact on its surroundings because of amplification through dependencies. Each dependency will carry the same amount of additional leaked knowledge, thereby multiplying the effect of one leak with the number of dependencies on the abstraction. Therefore the more an abstraction is used, the more rigorously it has to be designed to eliminate or at least minimize leakage.
Consider the following class:
A proper abstraction (for the above requirements) would look like this:
This fulfills all the requirements the same way the previous implementation does but requires much less knowledge, therefore causes much less Cognitive Load in the developer using this class.
Another example from a banking application:
It turned out that this class was used for answering remote calls from other systems based on XML messages. It can be therefore considered an abstraction of a proper response message according to the required protocol. In this case, however, there is significant leakage here because the usage of the objects of this class should not require the caller to know all the fields of this class. The class should look more like this:
You might have heard the saying “ If it compiles, it works “ in relation to using some class or library, usually in the context of a functional programming language. This refers to a perfect or near perfect abstraction, where misusing the interface is virtually impossible, therefore all syntactically valid code constructs are likely to be semantically correct too.
Not all languages are powerful and expressive enough to arrive at a near perfect abstraction all the time. Still, in the confines of a given language, technical leaks should be always avoided, especially in core concepts, where amplification through multiple dependencies could make problems much worse.
We’ve already seen an example of a technical leak with the CashTransferResponse class. Its original „bean“ implementation looked like this:
It was, therefore, possible to write this code somewhere else:
Another form of this type of abstraction error is having methods to initialize or close. Consider this DatabaseTransaction for example:
Obviously, this class needs the developer to make sure the transaction is closed properly. The developer is probably expected to use Java’s try-with-resources construct. It’s pretty easy to see, however, that the compiler can not really check whether the transaction is always properly closed, so there is technical leakage.
This is one way the technical leak in the DatabaseTransaction could be avoided:
We developers, being naturally attracted to algorithms and design patterns, sometimes concentrate too much on the technical aspects of our code: thinking about how to apply certain patterns, how to make a clean separation between certain parts of the code, etc.
Java Beans (see CashTransferResponse above), for example, are distractions in this sense.

Continue reading...