I stopped studying biology at school when I was 16. So when people tell me about what they’re studying in A level biology it’s advanced enough to be interesting but not too advanced to understand. After a recent conversation about it I wondered why it was resonating with me so much. Not only is it cool to learn more about how my own body and the plants and animals around me work, I realised that some of the tools and mental models I’d developed as a programmer are similar to those that would help in biology. I’m not an expert in biology (as you may have guessed from what I’ve already written), so this is probably me just spouting nonsense as usual.
Many levels of abstraction or detail
One reason why there’s so much to biology is that you can zoom in or out and still find lots of detail. If you start at the level of the genome, that is a hard and interesting problem by itself. You could then zoom out a little to the level of the components of a cell – e.g. how does the mitochondrion power the rest of the cell with the aid of the Krebs cycle? Zooming out to cells, or to groups of cells – how does food move through living phloem cells from the leaves to the roots? And so on up to tissues, organs, systems and organisms.
Similarly, in code there is the expression, and one or more expressions can build up into a statement. Some individual statements such as a LINQ statement in C# or a query in SQL can be very complex things on their own. Statements build into blocks, which build into methods, which build into classes, and so on into namespaces, compilation units such as DLLs or .so files, executables, sub-systems and finally systems.
When thinking about an organism you may need to climb up and down the levels of abstraction to fully understand things. Having to hold many levels of code abstraction in your head at once is one reason why interruptions can be so expensive to programmers.
Implementation of a feature can be spread over many things
Let’s take a simple but familiar action: chewing. In order to chew you need a skull, a jaw, teeth and muscles. I’m ignoring the tongue as a thing for moving stuff to the teeth, swallowing, saliva etc. I’m also ignoring the fact that the muscles need to be controlled by the brain via nerves, and need to be supplied with nutrients by the blood, protected from diseases, kept warm etc. So, the bare minimum for at least human chewing is four things that are each complicated in their own right, that must come together in only a very limited number of ways to work well.
If you have a website, where is e.g. security implemented? You might think it’s just the login page, but what stops someone entering the URL of a restricted part of your site into their browser? Are there any places where user input is accepted – are those protected against Bobby Tables attacks and so on? Is there any form of rate limiting in an upstream security device to guard against denial of service attacks?
You can’t always point to a single part of an organism or part of a computer system and say: this and only this place is where a particular ability or feature lives. There is at least a many-to-one relationship between parts and functions. (I think it’s actually many-to-many, as I’ll explain below.)
Many systems at once
The complexity of an organism or a computer system can be sliced up in two ways. On the one hand, you can slice it into horizontal layers of abstraction as I mentioned above – cells / tissues / organs and so on. On the other hand, you can slice it vertically by function.
The human body is made of many systems, which are these vertical slices through all the complexity. The systems handle things like circulation, respiration, digestion etc. For health, all these systems need to be working well, at the same time, in the same organism based on a shared set of resources such as food and water.
A computer system will often have co-operating sub-systems. For instance, something like Amazon will need to take payments, let buyers search for things, let buyers browse for things, let sellers offer things for sale, recommend things to buyers, handle security and fraud etc. These will be so large and complex that they will need to be separated out into systems (actually, into a bazillion micro-services). These systems need to co-operate to make the whole function, and share the resources of CPU, memory, disk space, network I/O etc. (I’m not saying they’d run on the same machine, but the total resources of the Amazon estate need to be shared across all these systems.)
Many constraints at once
Regardless of how you think we have come to end up how we currently are, you can at least pretend that we are the result of an engineering design process. By that I mean a process that tries to satisfy multiple constraints at once.
Skin must be water proof enough to stop you drying out (see victims of bad burns), tough enough to protect what’s inside, flexible enough to let you manipulate things and move, sensitive enough to heat, touch etc. that you can react appropriately to your environment.
A GUI must give you all the information and tools you need now to achieve a goal, not give you too much / many that you’re overwhelmed, fit enough onto the screen that you don’t need to scroll too much or click through too many steps, not make things too small that you can’t identify what’s what or move the pointer / tap on the correct part.
Small changes can produce big differences
A difference in a single gene could cause something like cystic fibrosis or sickle cell anaemia. A missing hyphen caused Mariner I to explode.
Both kinds of system can be very fragile to small changes at the lowest level of detail. The lowest level of detail in both cases is a very fertile and expressive medium, that can give rise to very different behaviour at the higher levels. (Think of just all possible humans, and just all possible programs written in Java.) The cost of this expressiveness is that sometimes the scale of changes appears to get amplified – what seems to be a small change at the lowest level gives rise to bigger and bigger differences as you go up the levels.
Debugging / diagnosing
Diagnosis or debugging means going from symptoms at the highest level (e.g. a nasty rash or wrong values appearing on screen), investigating down through possibly many levels of abstraction to the cause, applying the remedy, and checking possibly many levels of abstraction back to the top. For instance, a doctor might check blood pressure or blood chemistry as a stepping stone along the way to confirming that the condition that caused the rash has gone, as well as checking for the rash itself. A programmer might check log files or database contents on the way to confirming that the bug that produced the wrong stuff on the screen is definitely fixed.
Long series of cause / effect
Drinking too much Coke or tea (or other diuretics) might make you need to go the toilet. There isn’t a simple link between consuming a diuretic and a full bladder – instead it’s quite a complicated relationship. All the steps in this process need to function well to establish the link between ultimate cause and effect.
If you look at an optimising compiler, particularly of a language like C that compiles all the way down to machine code in one go rather than deferring some till later via JIT, there are many steps between source code and binary. (Note that the optimising compiler article misses out steps such as lexing, parsing, IL generation and output code generation.) My experience of compilers is that they are remarkably correct given how big and complex they are.
Finally, both fields have some cool names. I invite you to guess (without following the links) which field each of these came from – it shouldn’t be too hard.
- Conway’s law / Inverse Conway manoeuvre
- Islets of Langerhans
- Wheeler jump
- Nodes of Ranvier
- Duff’s device
This article is not claiming to offer any deep insight into how to be a better programmer. It’s just trying to pull out of my head the swirl of similarities between organisms and computer systems.
Even though there are these similarities, I think it’s crucial that you don’t think of your code as your baby! Jerry Weinberg wrote about the perils of this in his book The Psychology of Computer Programming in 1971. While it’s important to work to a high standard, we also need to accept appropriate criticism of code we’ve written, because it’s an opportunity for it (and possibly us) to improve.
UPDATE 13/11/2019: Someone who actually knows what they’re talking about where it comes to biology pointed out a couple of mistakes, that I think I’ve now fixed. Phloem refers to the cells, and not the food flowing through them (I knew that, but forgot). The Krebs cycle produces the chemical NADH, and NADH is then used by things after the Krebs cycle to produce the chemical ATP. ATP stores energy in its chemical bonds, and so breaking these bonds releases the energy, which helps to power the cell.
Help with any more mistakes – biology or computing – will be gratefully received.