Monday 7 August 2017

Levels of Abstraction

At a very beginning feel very different levels of abstractions in sentences: "turn on the computer" vs "on molecular level trigger flow of electrons which cause millions of transistors and hundreds of various electrical circuits to obtain particular state"

In our daily life and during our daily communication we subconsciously adapt level of details in our words so that other side can understood us with minimal mental energy and without necessity to decode many irrelevant aspects of information.

Do we do the same in our code?

Robert C. Martin in his book "Clean Code" describes easy to maintain functions/methods as :

  • small
  • responsible for only one thing
  • working on one level of abstraction

First two points should be quite easy to imagine (however functional languages like scala added another constraint that functions should not only be short but also thin). Yet the third point about levels of abstractions is... well abstract itself thus may be non intuitive.

It is always easier to demonstrate abstract concepts with use of a practical metaphor. And what can be more practical than recipe for Scrambled eggs!

The recipe

  1. Heat the butter in a small frying pan
  2. Drop the melted butter in small chopped slices and finely chopped chives
  3. Break the eggshell with a sharp knife and pour the contents into the pan
  4. Add some salt and pepper and mix until the eggs are cut

The recipe is rather intuitive and no one should have problem with understanding it. On the level of abstraction used in the recipe you only need to understand how to handle couple "elements" from kitchen domain and our final product is ready!

However some relatively complex things are happening "beneath". Those complex things brings up some concepts not important from our food domain perspective.

If we would like to model this recipe in our code then how such code with broken level of abstraction would look like?

The bad code

add(butter)

while(butter.isInConsistentState){
  for(fatMolecule in butter)
    for( atom in atomsInfatMolecule){
      atom.incrementEnergy(heat)
  }
}
add(slicedHam)

while(chive.isInNotManyPieces){
  use knife to create force which will break chive internal structure 
}

add(choppedChive)

knife.storePotentialEnergy
knife.transformPotentialEnergyIntoKineticEnergy
knife.hit(egg)

add(eggContent)
add(salt) 

for(peeperSeed in A pinch of pepper){
 add(peeperSeed)
}

and heat till it is ready

(Proper naming of physical concepts may not be accurate but it just an illustration. Also food names are taken directly from google translate - author knows only several English words from food domain : proteins,fat, carbohydrates, chicken,rice ,broccoli)

Now imagine such recipe would be posted on some page with food recipes. People would be extremely confused. Moreover to understand some concepts it is not enough anymore to be just "good cook" - now it would be good to remember some things from physic lessons.

Now let's return to more natural one level of abstraction

One Level of Abstraction

fryingPan.add(butter)
fryingPan.heat(butterIsMelted)
fryingPan.add(slicedHam)
fryingPan.add(choppedChive)
fryingPan.add(break(egg))
fryingPan.add(salt)
fryingPan.add(pepper)
fryingPan.heatTillready()

This recipe is shorter, more readable and doesn't force you to know concepts from outside "food domain". You can also observe an interesting visual effect which can be used as test for one level of abstraction - in the second implementation there are no indentations!

So after this pseudo-example how this problem touches IT? Very often you will see something similar to this :

function(object){
  domainOperation()
  internals=object.getInternals
  ...
  while(..){
   for{
    for{
     for{
      if(...){
       for{...}
      }
     }
    } 
   }
  }
  domainOperation()
  for(i in object internals) ...
}

For managers

This situation cost your company real money. When levels of abstractions are mixed code analysis will be longer thus more expensive. Also very often changes will have to be introduced on couple levels raising probability of introducing new bugs so new costs again. Remember that.

This code breaks one level of abstraction and very often forces reader to jump between concepts from different domains. So next time you will see a domain concept cut by technical low level detail you should recognize that you just jumped through different layers of abstractions — maybe it’s your fault or maybe someone some times ago wrote a shitty code and said “this is technical debt, this is fine”.