Antifragile System Design 3: Evolutionary Design

Hannes Rollin
8 min readOct 25, 2023

--

A complex system that works is invariably found to have evolved from a simple system that worked.

John Gall

Charles Darwin, as he might have been painted by a twenty-something van Gogh (DALL·E)

This is the third post on antifragile system design; formerly, I’ve discussed the concepts of optionality and redundancy. Continuing our forage through the list of necessary conditions for antifragility, let’s have a look at evolution and evolutionary design.

Without quite noticing it, you’re looking at a dinosaur: A medium-form text structured, written, and edited solely by a mere human. Within not so many years, we’ll likely be surrounded by a monstrous corpus of AI-generated posts, documents, and even books, where the role of human creativity will have been relegated to that of a prompt typer, calling it “prompt engineering” being akin to overstyling your run-of-the-mill SQL wrangler as a data scientist. But let's stop for a moment and consider the long, long process that goes into the making of a writer. Before AI, I mean.

The countless hours of diverse reading for pleasure, beginning at a young age. The more serious reading and painstaking re-reading of academic literature, textbooks, and non-fiction. The first cumbersome attempts at writing, often unwittingly stunted by well-meaning but ill-informed parents and schoolteachers. The extravagant experiments during adolescence, bloated with importance and rarefied with artificial secrecy but later smiled upon indulgently. The first structured long-form texts, taken apart mercilessly by seasoned professionals. That heart-rending but unreadable first novel, collecting mold in a hidden spot in the attic. The sinuous, circuitous, criss-crossing paths taken, searching for meaning, connection, and a place in life. The first public recognitions for writings after nearly unbearable numbers of rejections. And finally, after so many years and so many hard knocks, a mature style, a unique voice, a devoted readership. All the hallmarks of evolution can be seen at work: Humble beginnings, quantity, variety, competition, selection, clever combination and mutation, and time. Note the word “clever.” I didn’t say “random” for a reason.

This, although your biology teacher might have neglected to mention it, is the nucleus of evolution—a deep inner knowledge of the end, all the while fiddling with the craft, tackling the world, experimenting, and painstaking improvements in the desired direction, although there rarely is a direct way, and many a false lead must be backtracked.

Likewise, when pondering antifragile system design, you know roughly what your system, eventually, is supposed to be able to do. Conversely, you may be tempted to put all thinkable requirements into the first few versions of your systems; after all, as Westerners, we tend to think the goal is, well, the goal. But of course, there are several problems with that. First, you don’t know precisely what the end result is going to look like, owing to staggering complexity issues and the system’s environment fluidly changing as well. Your initial feature list is always off-target. Second, no matter how much thinking goes into anticipating possible risks and breaking points, you’ll miss some for the same reasons. Therefore, your antifragility will be insufficient. Third, we’re annoyingly bad at understanding chaos and complexity. Read your Systemantics:

A complex system designed from scratch never works and cannot be made to work. You have to start over, beginning with a working simple system.

In software development, the agile movement came along as a response, attempting to introduce an iterative, pragmatic development process with frequent customer feedback and a focus on quickly “delivering value” (an insufferable phrase). From an optimization standpoint, agile development is hugely problematic since it resembles a greedy optimization algorithm like gradient descent, where the next step is determined from the current position and perspective alone. As an illustration, you’d be like a mountaineer attempting to climb the highest mountain but stupidly going up whenever you meet an incline. While it makes intuitive sense, you’ll usually only find what’s called a local maximum—this is often just a hill surrounded by mountains.

A local maximum rarely is a global one (DALL·E)

Taken together, it’s clear that you must design with the end in mind but leave the path to the end open. I know that this is a rather contested definition of evolutionary design when concerning the real world—just look up “intelligent design”—but as systems designers, we don’t have to wait until the philosophers, theologians, and physicists have finished debating, for they never will before humanity gives way to the next apex rowdy species, and we’re more interested in nice antifragile systems rather than some abstract truth. Nevertheless, Edward Carpenter’s 1904 essay “Exfoliation” is indeed a delightful read for a rainy autumn evening. For those who have ears to hear.

Garden-Variety Evolution

Darwin’s theory of evolution might be boring, incomplete, and not even quite up to the task of explaining the natural world, but it highlights several important points for antifragile system design. In essence, it says that variety within a species is solely due to random mutations (a fact contested by epigenetics, but let’s slip that for now), and therefore some members of the species are better adapted to the environment than others, which (by a stunning leap of imagination) gives them higher probabilities of reproduction and healthy offspring. The well-worn phrase “survival of the fittest” means, in fact, “survival of the best-adapted.” The best-adapted can be rather unfit in the athletic sense, as a glimpse of government pecking orders immediately reveals.

It is not the strongest of the species that survives but the most adaptable.

— One of the things Darwin never said

Weirdly, as a computational tool, this reductionist evolution theory works reasonably well. No matter your optimization problem, if you can (1) provide a large, diverse population of initial partial solutions, (2) compute the “fitness” of each specimen in relation to your problem, and (3) use crossover to produce probabilistic offspring with randomly combined traits and random mutations, you will eventually reach an optimal solution, given good enough randomness, a big enough population, and loads of patience. This tool, known generically as genetic algorithms, has been mathematically proven to solve many optimization problems, especially in the context of scheduling and other permutation problems. The only weak point is premature convergence to, you guessed it, a local optimum.

Notice the similarity to agile development: Darwin’s stupid evolution theory just maximizes momentary delivery of value (successful reproduction), which can hardly explain the evolutionarily increasing intricacy of the living world; after all, bacteria are unsurpassed at successful reproduction. But I digress.

Conditions for Evolution

Without further shooting at poor Darwin, we can extract a few useful lessons here: Set up an antifragile system with a large enough degree of variety. This immediately entails two things: First, any antifragile system needs to be modular, with redundant and subtly different modules being able to do the same thing. Quantity and diversity are essential. In the event of stress and shocks, natural or artificial selection needs a diverse buffet to select from. Second, you need a mechanism to introduce and uphold variety—naively, this can be plain randomization, but an epigenetic approach is even better—online adaptations become part of a subsystem’s heritable traits instead of being ephemeral configurations. As a systems designer, after all, you know the desired end result.

While it seems like a dark art, it’s pretty straightforward in practice: Consider API design. Ordinarily, you might define a REST interface providing CRUD operations for all primary entities of your data model and be done with it. But this is hardly evolutionary since you have a population of n = 1. What’s sometimes called “evolutionary prototyping” is anything but; when you fiddle with a prototype to make it better and better fit for some purpose, that’s more aptly called continuous improvement.

If your API will be subject to immense load, your system is critical for your organization, and you have a significant budget, think about designing three or four simple microservices with outwardly identical API definitions that work nonetheless slightly differently, say, one uses synchronous database connections, one relies on asynchronous messaging, and one has an in-memory copy of many of the necessary datasets. Each approach has its advantages and drawbacks, but you don’t quite know in advance which will turn out on top. Have your load balancer distribute the traffic randomly to all three microservices, let each microservice scale elastically given some load thresholds, initiate each replica with slightly different configuration parameters, and closely observe which approach works best in which situation. Quantity, variety, competition. In later incarnations of your systems, cleverly combine and tweak your learnings for the next-generation API. Selection, recombination, mutation. And, of course, you can automate large parts of this process if you can muster the resources and enthusiasm.

You’ve certainly spotted the repeated overinsurance property of antifragile systems. If we want to imitate nature, we must not be cheap.

Another more popular way to introduce evolutionary design to software development is setting up several largely independent teams and assigning all of them the same goal, letting each team choose their tools, methods, and ways of working. This is another takeaway from Darwin: Selection only works if there is competition. Absent limited resources and competitive environments, things will limp and linger. Entropy reigns.

In the natural world, the absence of limits becomes less and less of a problem, so this is taken care of.

In genetic algorithms, the competitive drive is imitated by forcing reproduction and assigning mates probabilistically proportional to individual fitness. You can use this trick in your system design, or you can select mating pairs manually. The crucial point is this: The evolutionary development of your system in the direction of your desired end needs variety and a selection mechanism that is inspired by your goal. Don’t just run up the next hill and call it “agile.”

No Antifragility Without Evolutionary Design

Evolutionary design is perhaps the largest and most difficult part of antifragile system design. If we want a system that thrives and improves in the face of volatility and disruptive change, the system must be set up so that it has more than one way to react (optionality), but that’s not sufficient since we can’t expect to know the best ways to react beforehand. Your system needs to be able to evolve—many ways to combine reasonably good components into better-adapted ones. Only completely understood complex systems in an unchanging environment could conceivably be optimally designed without the need for evolutionary improvement. Such systems don’t exist.

Evolutionary design, however complex in practice, is simple in principle:

  • Begin with a simple design, not least because of Gall’s law
  • Have a large, diverse population of subsystems
  • Harness the forces of limitation and competition
  • Select and recombine promising subsystems with clever mutations
  • Keep the big picture in mind: Don’t settle for lesser hills if you can reach Mount Everest

Next Up: Modularity And Decentralization

We’ve seen that evolutionary design entails variety, which necessitates modularity. While modularity seems a simple enough concept, much can be misunderstood, and much can be done wrong. We’ll have to talk about coupling, architecture quantum count, the curse of complexity, and why the heck the new breed of cloud-native decentralized databases are really just distributed key-value stores. See you there!

--

--

Hannes Rollin

Trained mathematician, renegade coder, eclectic philosopher, recreational social critic, and rugged enterprise architect.