A Programmers Take on “Six Memos for the Next Millennium”


Six Memos for the Next Millennium is a collection of five lectures that Italo Calvino was going to give in 1985. Unfortunately he passed away before he was able to deliver the lectures. Because of that the book is just a collection of his notes. He also hadn’t started on the sixth one, so the book only contains five. I became aware of the book because Jonathan Blow gave a great talk about it, and about how Italo Calvino inspired him:

The reason why I’m writing about the book is that while I think that they are great memos about writing, the more I think about them, the more they apply to programming. Which is a weird coincidence, because they were supposed to be memos for writers in the next millennium, and programming is kind of a new form of writing that’s becoming more important in this millennium.

Being a game developer, I also can’t help but apply these to game design. So I will occasionally talk about games in here, but I’ll try to keep it mostly about programming.

The memos are about

  1. Lightness
  2. Quickness
  3. Exactitude
  4. Visibility
  5. Multiplicity
  6. Consistency

The sixth one is not written, but I think consistency is very important in programming, so I’ll write about it a little bit.

Lightness

Italo Calvino is referring to lightness as opposed to weight. In all his memos he only says that he prefers the thing he talks about over its opposite. So he prefers to take weight out of his stories, but that doesn’t mean that there isn’t also value in weight, and other authors may prefer weight. But I agree with Calvino in that I prefer lightness in my stories. A specific kind of lightness that is. Calvino says that

I hope that what I have said so far shows that there is a lightness that is thoughtful and that is different from the frivolous lightness we all know. Indeed, thoughtful lightness can make frivolity seem heavy and opaque.

He has lots of examples for this in the book, but the one that I connected most with (maybe because it’s from a book I actually read) is Milan Kundera’s book The Unbearable Lightness of Being. It’s a book about very, very heavy topics. But it feels like a light book. Calvino says it like this:

His novel shows us how everything in life that we choose and value for its lightness quickly reveals its own unbearable heaviness. Perhaps nothing escapes this fate but the liveliness and nimbleness of the mind – the very qualities with which the novel is written

And as a game developer, the obvious example to mention is Jenova Chen’s work. His first famous work was literally titled Cloud and what could be lighter than that? He later created Flower, which is a game about being a flower petal floating in the wind, and Journey, which has similarly light movement. The game that he is currently working on is called Sky. I absolutely loved Flower and Journey, I think Journey in particular is one of the masterpieces of our medium. And I think its lightness has a lot to do with that. There is a simple delight in weightlessness, and it’s rare that someone manages to turn that delight into a full game.

A small interesting aside is that when Jenova Chen spoke at the NYU Game Center last month, he says that in the dichotomy between light and dark games, he prefers to make light games. He still values dark games, because his favorite game is Dark Souls, but in his personal work he prefers light. It’s a quirk of the English language that light and lightness are so closely related, but it’s also fitting because weight drags you down, away from the light.

Anyways, I was going to talk about programming. What is lightness in programming? I think lightness is related to “simplicity”, but it explains why I make certain choices that I wouldn’t make when talking about simplicity. Meaning I have had arguments about which solution to pick, and which one would be simpler, and in hindsight I was actually arguing for the lighter solution, not the simpler solution.

For example it explains why I still like to write code in C++. While C++ is an incredibly heavy language, it allows me to write light code. The idea of “you don’t pay for what you don’t use” is very important in C++, and is very rarely violated. In other languages I always feel like little weights are added to my program when I have to do something in a way that would be more efficient or more elegant in C++. (like having to heap-allocate something that could just be stored inside of another object, or having classes be bigger in memory than just the members that I added, or having overhead for virtual function calls that I don’t need)

The “you don’t pay for what you don’t use” concept gives us an idea where weight comes from in code: It comes from dependencies, and especially dependencies that we didn’t want or aren’t even using. It’s the old joke about object oriented programming: You wanted the banana, but what you got was the gorilla holding the banana. (and sometimes the whole jungle) But to further support that meaning of the word, when people talk about a “lightweight” library or framework, they usually mean a library that doesn’t have many dependencies itself, and that doesn’t force you to use more than what you need.

When I think about heavy objects, I’m immediately thinking about the “character” or “vehicle” classes of most game engines. For some reason these two classes in particular are usually messes. I’ve seen character classes that contain code for animation, model rendering, IK, skeleton queries, interactions with environment objects (like doors, chairs etc.) inventory, damage handling, effect playing, physics interactions, weapon handling, melee combat, camera code, ragdoll code, bone-attachments (like playing a effect attached to the hand) UI checks, AI code, random other game-specific logic, random file IO, lots of player-specific code (that’s still being paid for by all non-player-characters) special code for quadrupeds, water interactions, ammo handling, LODing, transform math, vehicle interactions, sound code, auto aim code, and more.

All that in one class that’s thousands of lines of code. How does that happen? Well the quadruped code gives us a hint: At some point people decided that they needed animals in the game. Animals need a lot of the code that characters already do: Animation code, damage handling, model handling, and random other interactions. Just turn off half of the other code and you’re fine. And then add some more code for quadrupeds.

It’s clear that this results in very heavy classes, and it’s also clear to the programmers who make those decisions. It’s one of those “yes, we’re concerned about it, but we can’t deal with it right now” situations that keeps on not getting dealt with for years, until you have a monster class, and now you can do even less about the problem.

The more I program, the more I think that inheritance is a big source of this. In that same engine, I once tried to create a new object that had overlap with characters, but it needed to be flying. And I didn’t want to add logic for flying to the messy character class. It was actually possible to write a new class that used a bunch of the existing logic, because most of the logic actually didn’t live in the character class. Usually the way it worked was “if you want to be able to receive damage, inherit from this base class and override this function.” The new class started off really clean, but because of this inheritance, we immediately started accumulating little bits of functionality to totally unrelated topics in the same class. Damage handling was now in the same class as animation code and effects code. And soon we had another monster. And once you have a monster, it’s easy to justify adding a little bit more logic into it, after all all of the other logic that you need to talk to is already in the monster class.

The only way I have ever seen this handle well is in an entity component system. An entity component system takes the “prefer composition over inheritance” to an extreme: You don’t even have a central class in which you compose all of the functionality. You just add components to a collection, and all components in the collection are part of the same logical object. Just the fact that you don’t have a central object forces you to be a bit more disciplined: If you want to add logic for foot IK, you’ll probably add a new component which can talk to your animation component. You can’t be lazy and say “I’ll just add it to the character class for now, and we can move it out later.” For an entity component system to work, it’s very important that it’s easy for components to communicate with their siblings, meaning it has to be easy to get the pointer to a sibling to talk to it. If you still have to coordinate through a central class, that central class tends to accumulate behavior like our character class above.

I personally like the system that the Unity game engine has. (whenever I bring it up as a model, people complain that Unity is slow. You could make their entity component system very fast, they just can’t do it because old code has to keep on working, so it’s hard to make things data-oriented or multi-threaded) One additional thing that that engine does is that it’s super easy to add glue code between components. It’s very easy to add small scripts that do the communication between two different components on the same object. I think the ease of adding scripts is one of the reasons why they have few clever components that try to magically find out what the right thing to do is based on their siblings. If you want custom behavior, just add a small script.

Talking about lightness, an interesting case is the C++ class std::function. It’s a heavy object, but it allows you to get away from inheritance, and as such it makes it easier to keep code light. For example you can write an update loop that doesn’t require inheriting from a base class. I have seen engines with requirements like “you have to inherit from CGameObject to be in the update loop,” where CGameObject also has other things on it, like a transform matrix. That’s another pitfall where inheritance leads to weight in the long term: The more classes something is inherited by, the more people will want to add functionality to it. You always get cases where there is common functionality between ten derived classes, so why not just add it to the base class? Well because there are another five objects that don’t need the shared functionality. std::function is an escape hatch out of this surprisingly often. This is actually the main reason why I should really learn Rust: I think their “Traits” form of polymorphism looks like it’ll be much better about this, while being lighter weight than std::function.

Quickness

Italo Calvino starts by telling an old legend:

Late in life the emperor Charlemagne fell in love with a German girl. The court barons were dismayed to see that their sovereign, overcome by ardent desire and forgetful of royal dignity, was neglecting imperial affairs. When the girl suddenly died, the dignitaries sighed with relief – but only briefly, for Charlemagne’s love did not die with the girl. The emperor had the embalmed body brought to his chamber and refused to leave its side. Archbishop Turpin, alarmed by this morbid passion and suspecting some enchantment, decided to examine the corpse. Hidden beneath the dead tongue he found a gemstone ring. As soon as he took possession of it, Charlemagne hastened to have the corpse buried and directed his love toward the person of the archbishop. To extricate himself from that awkward situation, Turpin threw the ring into Lake Constance. Charlemagne fell in love with the lake and refused to leave its shores.

Calvino goes on to tell how, since it’s such an old legend, there are many different versions of it. Most of them are longer, but somehow the length doesn’t actually make them better. He likes the quick version.

As a game developer, the value of quickness is most obvious when comparing the Blizzard games Warcraft III and Starcraft 2. Quickness alone probably explains why I preferred the story of Warcraft III. To illustrate, watch the introduction of two missions early in Warcraft III. Just watch until the cutscene is over and gameplay starts:

Now I already set you up to expect “quickness,” so maybe you were expecting something shorter. But to make the point, here is someone playing Starcraft 2:

I’m not going to link a second video for Starcraft 2, because this one is already long enough. This is the second level in the game, and the periods between the levels only get longer from here. The person who plays this talks a little bit, but even without that it takes almost seven minutes to get through all the things between missions. And if you don’t talk to everyone between missions, you’re missing out on the story.

When Calvino talks about “quickness,” he doesn’t talk about rushing the story. The old legend he is telling doesn’t feel rushed, even though it’s certainly quick. The Warcraft III introductions don’t feel rushed either. But in Starcraft 2 in comparison it can feel like people are just rambling on and on, and the story suffers.

In general I feel like quickness is a property that games have lost. I got a SNES classic last year, and I had forgotten how quickly those games get to the action. No long tutorials or slow intro missions. The first level is always a good level. And getting to the first level doesn’t take long. Take F-Zero for example:

You’re in the game, racing at high speeds within seconds of turning on the game. I don’t know precisely when we turned away from this, but I feel like it must have been with the advent of 3D gaming. Controls are much more complicated in 3D games, so suddenly you needed tutorials. And maybe an intro level that’s not too hard to allow you to get used to the controls. But often it just ends up feeling like the game doesn’t respect my time.

So what about programming? Quickness matters most for me in iteration times. As an example, let’s take how long it takes to write a unit test. I strongly believe that unit tests should live in the same file as the code that they’re testing. The reason is quickness. I’m not a TDD zealot, but I do write some of my code in a TDD style. When I do, my speed of programming is partially determined by how long it takes me to write and run a test. So you want to avoid adding steps to that. Like having to create a new file. Or having to launch a separate executable. If the environment is set up correctly, I find that coding with tests can be faster than coding without tests, simply because tests can have faster iteration times. Without tests you often have to start the game and load a level where you can test your content. That can add more than a minute to iteration times. I personally like to just scroll to the bottom of a file and add this code:

#ifdef TESTS_ENABLED
#include "gtest/gtest.h" TEST(foo, bar)
{ ASSERT_EQ(5, foo(bar(10)));
}
#endif

This is pretty low-friction for me. I have the ifdef and the include memorized, so I can quickly add tests to any file. But I recently realized that even this may be too much. The video games industry is oddly backwards about testing, so I constantly try to push people to write more tests. Recently, I told someone who was writing some math code “you know, you would be much more productive if you just wrote a small test for this, to iterate more quickly” and he mumbled something about wanting to write more tests but never getting around to it. I realized that he didn’t know how simple it is to write tests. I showed him the above block with an example call to his function, and suddenly he was much more open.

So how could this be even easier? We need at least one include, and we need the ifdef check if we want the tests in the same file as the production code. We need to be able to compile this without the tests. We also need the macros because of C++ complexity reasons. It’s hard to see how to make this any easier. (neither the preprocessor nor templates are powerful enough to make any of this go away) The D programming language shows how to make it much easier. In D you can use the unittest keyword in pretty much any scope to quickly write a test:

unittest
{ assert(foo(bar(10)) == 5);
}

This takes away all excuses. It’s even important that you can use the normal assert macro here, as opposed to the custom ASSERT_EQ macro of googletest. In my C++ example above the other programmer didn’t immediately start writing tests, because his math code was buried deep in the internals of a class. He had to pull it out to make it testable. That wasn’t hard, but it was something he had to do. Just being able to write unittest at any scope in D makes that part much easier. So this matters.

In fact iteration times is one of those areas where it’s never really fast enough. I use the tup build system because it adds less than a tenth of a second of overhead to a build. Does it really make a difference if the build system adds a second of overhead or a tenth of a second? Yes, you bet it does. When it comes to iteration times, a quantitative difference will quickly turn into a qualitative difference. If a build takes a fraction of a second, I can recompile and reload code every time that I save a file. That may not be doable when compiling C++, but it is doable when compiling shaders. Before using tup, I had to hack that workflow by doing a custom build from game code whenever the file watcher detected a change to the file. That’s ugly because you’re going around the build system, and you may be putting your build into an inconsistent state. If the build system overhead is near zero, suddenly you can just run a normal build on every save. And if there are derived build steps from your shaders, those will get compiled correctly, too. The moment that you have shaders that reload in a fraction of a second, you start working differently. You experiment a lot more. You tweak numbers more. You investigate small oddities of behavior to find the underlying cause and to get a better understanding of the code. Things that you needed to schedule time for (even if just mentally “I’ll get to that later”) you suddenly just do.

So is a fraction of a second fast enough for iteration times? I’d like to think that there are more qualitative differences for even faster compiles. Bret Victor’s talk Inventing on Principle comes to mind. Just like his visualization of “here are many possibilities for a jump,” I feel like there should be similar visualizations like “here are many possibilities for this shader.”

Exactitude

As is appropriate for the topic of exactitude, Calvino is very precise in what he means with the word:

For me, exactitude means above all three things:

  1. a well-defined, well-considered design for the work;
  2. the evocation of clear, sharp, memorable images;
  3. a language that is as precise as possible in its choice of words and in its expression of the nuances of thought and imagination.

The value of these things might be self-evident, but there are many, many works for which the above does not apply. I once gave a talk about The Game Outcomes Project, which looked into what made successful games and what made unsuccessful games. And one of the main things they found was that clarity and communication of vision was incredibly important. Of course “clear vision” is one of those things that is hard to get if you don’t already have it. But I think it can serve as a useful filter for ideas.

In programming I value exactitude most when it comes to robustness and reliability. As such, I very much disagree with Postel’s law: be conservative in what you do, be liberal in what you accept from others. That law is for internet connections. If somebody sends you a packet that isn’t 100% correct, you should still accept it. But you should only send out data yourself that are 100% correct. I understand why people do this on the internet: If your browser can’t load a website because the website doesn’t send out 100% correct HTML, people won’t blame the website, they will blame the browser. For that reason it’s more an unfortunate practical matter than a law. When it comes to your own data, or your own internal network, accepting bad data is exactly the wrong thing to do.

The big problem with Postel’s law is that it leads to weird semi-standards being established. Yes, you’re not supposed to do X, but in practice it works out. And you get weird edge case behavior because things aren’t properly specified in the “liberal” area of data. There have been security bugs because different software interprets the edge cases differently.

In your own code things get even worse, because now you are responsible for fixing the weird edge case bugs. In my experience, the more layers that follow Postel’s law, the more difficult bugs you will end up with. If a designer does something weird in the editor of your game, (like say accidentally using a naughty string) but the editor allows the designer to save it, and then the compiler allows it, and the loading code of your game allows it, the bug you may end up with may be really confusing. All you see is bad data in the game. You have to first debug the loading code to figure out if it messed with your data. You then find the edge case that should never be hit that came from bad data in the compiled file. So you logically conclude that the bad data must have come from the compiler. When you finally figured out what happened in the compiler, you see that a certain pattern in the source format can end up causing the compiler to write bad data. So how did we get that pattern in the source file? Off we go to the revision control history to figure things out. Was it a merge error? No it was actually saved out that way from the editor. So we open the file in the editor, and can’t find the problem there. In fact when we save it out again, the problem goes away. Because we hit a weird edge case. So we ask the designer what they were doing, and finally get a clue for what caused the issue.

How do you solve issues like this? Verify data every step along the way. Verify on save in the editor. Verify in the compiler. Verify on load. These edge cases that were caused by earlier edge cases are the worst bugs, in fact they can often be weird one-off bugs that go away by themselves. (as in the above example where the problem goes away if you just save the file again) Be precise about what you accept, and accept nothing else.

This is also one of the reasons why I prefer statically typed languages. The more messy you allow a programmer to be, the more likely it is that you will get really weird edge cases like this. Not saying that programmers will be messy all the time. 99% of the time they will have great behavior and not put in weird edge cases. But then something comes along that is causing a big problem, but it’s really hard to change, so they put in a little weirdness just this once. And if you’re maintaining that software over years, you will eventually accumulate enough of these little weirdnesses that making any large change becomes a giant pain. Not saying it’s easy to change large projects in statically typed languages, just saying it’s easier compared to dynamically typed languages.

Visibility

As we’re progressing through the book, the chapters are becoming less and less finished. In Visibility, Calvino talks mostly about the interaction between visual imagination and written words.

[…] the only thing I was certain of was that all of my stories originated with a visual image. One of these images, for example, was that of a man cut in two halves, each of which goes on living independently; another was a boy who climbs up a tree and travels from tree to tree without ever coming back down; yet another was an empty suit of armor that moves and speaks as if someone was inside it.

For me, then, the first step in the conception of a story occurs when an image that comes to mind seems, for whatever reason, charged with meaning, even if I can’t explain that meaning in a logical or analytical terms. As soon as the image has become clear enough in my mind, I begin developing it into a story, or rather the images themselves give rise to their own implicit potentialities, to the story they carry within them. Around each image others come into being, creating a field of analogies, symmetries, juxtapositions. At this point my shaping of this material – which is no longer purely visual but conceptual as well – begins to be guided by my intent to give order and sense to the story’s progression. Or, to put it another way, what I’m doing is trying to establish which meanings are compatible with the general design I want to give the story and which are not, always allowing for a certain range of possible alternatives. At the same time, the writing, the verbal rendering, assumes ever-greater importance. I would say that as soon as I begin to put black on white, the written word begins to take over, first in an attempt to match the visual image, then as a cohesive development of the initial stylistic impulse, until little by little it rules the whole field. Then it is the writing that must guide the story toward its most felicitous verbal expression, and all that’s left for the visual imagination is to keep up.

For me as a programmer, I thought “Visibility” was going to be about something else entirely. The word makes me think of software that shows how it works. I don’t do web programming often, but when I do, I still use Knockout as my library of choice. And the main reason is visibility. Knockout shows how it works. Knockout has a simple promise: It will re-evaluate code automatically when data changes. Like an excel spreadsheet, except for UI code. With that alone a huge amount of state propagation and cache invalidation bugs go away. How does it do that? It has two simple concepts: Observable variables and Computed variables. Computed variables are like formulas in excel spreadsheets. They get re-evaluated when one of the input variables changes. But I can write arbitrary code in the computed variables, so how does it know which observables I used? Easy, observables are not normal variables. You have to call a function to access them. So it just has to remember which observables I called that function on. The model makes perfect sense and I can see how the whole library is built on top of that. I don’t need to know the details because I can imagine how they work myself.

The last time I tried web-programming I did the tutorials for Vue.js and for React. Vue looks very similar to Knockout, except somehow everything works magically. Normal Javascript variables are suddenly observable. I don’t know how it works. Is it super expensive internally? Does it magically transform my structs? It doesn’t show how it works. React is even worse. The tutorial starts off with several layers of deep magic, including writing html tags in the middle of Javascript code.

Why do I care how it works internally? Who cares if the internals require a huge amount of code and weird magic to work, as long as they work, right? But I’m concerned about the law of leaky abstractions. While doing the React tutorials I got some error messages that looked truly terrifying. I’m concerned that if things go wrong, I won’t be able to fix the issue. Or if things are slow, it will be very tricky to figure out how to make them fast. I don’t want to have to make random changes until things are working better.

Some of this can just be solved with good tutorials. Maybe Vue.js actually works exactly like Knockout, just using automatically created getters and setters. Just explain that in the tutorial, and I’d be happy.

The error messages of React reminded me of C++ templates. They were created with visibility in mind: Nobody could debug macros, and templates were supposed to be a more easily understandable alternative. What went wrong? When stepping through STL code, you often come across functions that seem to do nothing, like this one:

template 〈class _Ty〉 inline
_Ty * _Unfancy(_Ty * _Ptr)
{	// do nothing for plain pointers return (_Ptr);
}

What does this function do? It just returns its input. Why have such a function and why call it _Unfancy? The answer to that question lies in how you do conditionals in template code. This is actually part of the template-equivalent of a switch/case, and this is the “do nothing” default case. Templates are a weird accidental functional programming language, so people often write really weird code like this to do branching logic like switch/case or if/else. Visibility is definitely low on this. There are talks of making “if constexpr”more powerful, so that this could be an if/else. The C++ community is oddly resistant against that though. The other planned thing that will help here is that modules will remove some of the symbol salad, reducing the number of underscores, and removing those parens around the result. (those are often defensive coding to protect against insane preprocessor use)

Another weird one is how customization points are done in C++ templates. Those also often end up as functions that seem to do nothing. But at compile time, depending on your type, the compiler might have chosen a different function to call that actually does something. Talking about these as problems in visibility makes it obvious what the problem is: If this is a customization point, it should look like a customization point. Not like a function that does nothing. But I shouldn’t be complaining too much. Templates are better than macros, and it looks like things are slowly getting better.

Multiplicity

In the final chapter Calvino talks about a certain kind of richness or complexity that is inherent to life, and he calls it multiplicity. The word is used for example in this quote:

He would assert, among other things, that an unforeseen disaster is never the consequence or, if you prefer, the effect of a single factor, of only one cause, but rather is like a whirlwind, a point of cyclonic depression in the consciousness of the world, toward which a whole multiplicity of converging causalities have conspired.

Calvino uses other books to illustrate this as well, like a book that has one chapter for every room in a Parisian apartment complex. Or here is another quote I like:

The best example of these networks that radiate from every object is the episode from chapter 9 of Quer pasticciaccio brutto de via Merulana in which the stolen jewels are recovered. We get accounts of every precious stone – its geological history, its chemical composition, its artistic and historical connections, along with all its potential uses and the associated images they evoke.

In game design, the closest thing I can think of, that explores this richness and complexity of everything, is a game design approach that Jonathan Blow and Mark ten Bosch talked about in this talk:

In that talk they explain a design philosophy in which you focus on one idea, and you explore all of its implications and its richness. You can see this most easily in Jonathan Blow’s game Braid, in which he explores the implications of the ability to control time.

I also think that Nintendo games follow a similar design philosophy. Here is Game Maker’s Toolkit talking about how it applies to Super Mario Odyssey:

And the shrines in Breath of the Wild also allowed the designers to fully explore their gameplay mechanics.

How does this apply to programming? I think certain parts of programming have this richness, this good kind of complexity. The unix shell for example. Many separate programs that do one task, and they do that one task so well that they have explored every single angle of it. But most important is that those programs can talk to each other through a shared interface. This kind of richness is often missing in programming. What is the unix shell equivalent in C++? Maybe the STL containers and algorithms, but they’re not nearly as rich as the unix shell.

I also feel like many concepts aren’t sufficiently explored. I said above that Knockout is following the same state propagation model as Excel spreadsheets: If any of your inputs change, you get updated automatically. That simple model makes a huge number of bugs and complexity go away. Try to open a random file in your codebase, and measure the fraction of code that is responsible for pushing state around: Making sure to recompute X whenever Y changes, making sure that the new value of a variable gets pushed to all the places that use it, and making sure to invalidate all the caches that have duplicated some state for performance reasons. (like how the transform of a game object is usually cached in at least the physics system and the renderer, but usually in many other places as well) I bet you it’s more than half of your code, and in Knockout it’s so nice that all of that goes away. I feel like it should be used for more than just Excel spreadsheets and UI code. I’ve tried to explore it more fully, but it’s slow work that I can only really do in my spare time, where I already have dozens of other projects.

I think programmers are hesitant to do this work because our main problem is complexity. And when I say things like “explore a concept fully” I am also immediately thinking of over-engineering and unnecessary complexity that’s going to come back and bite me. Calvino also talks about how this is a problem in the books that he is referring: A lot of them are unfinished works. Sometimes the author spent decades on them before he passed away, and the unfinished book was published posthumously. It’s also a problem that Jonathan Blow ran into, The Witness took him seven years to complete, and Mark ten Bosch has been working on Miegakure for something like ten years. In that sense it’s amazing that Nintendo can release these very rich games at a regular schedule. I should look into how they do that…

Consistency

Calvino hadn’t started on his talk about consistency when he passed away, so we don’t know what he was going to say about it. But Jonathan Blow actually does a great job talking about the value of consistency in game design in the talk that I embedded at the top of this blog post. So I will just tell you to watch that.

In programming, consistency is such a commonly voiced value, I almost don’t have to say anything. The one thing I’ll add to the usual is that I think we’re pretty good about presenting consistency to other programmers (avoid surprises in interfaces etc.) but we’re not that good in presenting consistency to users of our software. Dan Luu has two blog posts that seem relevant here. Here is one on the huge number of bugs that we expose our users to, and here is one about UI backwards compatibility. Here is an example quote:

Last week, Facebook changed its interface so that my normal sequence of clicks to hide a story saves the story instead of hiding it. Saving is pretty much the opposite of hiding! It’s the opposite both from the perspective of the user and also as a ranking signal to the feed ranker. The really “great” thing about a change like this is that it A/B tests incredibly well if you measure new feature “engagement” by number of clicks because many users will accidentally save a story when they meant to hide it. Earlier this year, twitter did something similar by swapping the location of “moments” and “notifications”.

I honestly don’t know what to do about that. In video games, it feels like our games are now complex enough that lots of developers simply can’t manage the complexity any more. Features just stop working, even if no one removed them intentionally. See for example this video comparing Far Cry 2 to Far Cry 5:

I can tell you exactly how the above happened: some of these features just broke during development and nobody noticed until it was too late. Other features were re-written, maybe when porting the engine to a new console version, or just because somebody wanted to rewrite them. The new feature then didn’t have all the features of the old version, either because there was no time, or because the person doing the rewrite didn’t know about all the features that the old version had. Some things were just redone for the sake of redoing them, like the damage animations. Animators always want to redo all the animations, just like artists want to redo all the art, designers want to completely change the design and programmers want to rewrite all the code. Sometimes people just delete old stuff with the intent that somebody will redo them, and they never get redone. (like the underwater death animation that was “replaced” by a “stumble and fall to the ground” animation that makes no sense underwater) And of course the projects are always under time pressure, and features like “destructible vegetation” get down-prioritized early on after a rewrite of the vegetation system. After all there are several other new features that are high priority, not to mention all the bugs that were caused by the rewrite. So “destructible vegetation” becomes a down-prioritized “nice to have” feature, never mind the fact that the engine used to have it.

How do you solve it? I don’t know. I mean clearly more automated tests would help, but the difficulty lies in convincing people to actually write tests, learn how to write good tests, and to maintain their tests. I once wrote a lists of potential improvements that we could do at work, and it had dozens of entries, ranging from “more automated tests” to “more static analysis” and “write postmortems for crashes” and “put a sticker on the programmer’s desk every time they introduce a crash, to see who collects the most” (a form of public shaming),  “more strict code reviews” and others. But the problem obviously doesn’t lie in lack of ideas, since we have known most of this for decades.

I have a hunch that whoever solves this will make a lot of money. It does seem to me like Blizzard has much more solid game development practices. I remember watching the development of Starcraft 2, and even early versions of it looked super solid. Nintendo also clearly has this figured out: They used the same Mario engine for many years, and they also used the same Zelda engine for many years. It helped that the Gamecube, Wii and Wii U had very similar hardware.

It’s important to not confuse consistency with stagnation. Developers who ship the same game (or nearly identical games) multiple times will soon die. But I do think that if you have a development process where things don’t break all the time, you would free up a lot of time that we can put into making better games.

Conclusion

Italo Calvino was clearly a genius, and it shows in his choice of topics. Which game designer has ever said that they want to make a game that has “Lightness, Quickness, Exactitude, Visibility, Multiplicity and Consistency”? We strive for much simpler goals like “fun” or “intense” or “scary”. I like Calvino’s choice of topics because they make me think. Thinking about lightness revealed what I actually like when I talk about simplicity. And thinking about quickness revealed why the story in Stacraft 2 had always bothered me. I like the topic of multiplicity most though, perhaps because it leaves me with the most open, intriguing questions. How do you get that kind of richness in a reasonable amount of time? It’s something I’ll spend time investigating, hopefully without falling into an impossible to finish mega-project…