Dealing with complexity
I re-started working on my game engine. It’s a lot of fun. I’ve been in this team with 2 other programmers since about 2002 and we have an engine that we just keep adding stuff to and we keep refactoring it. Anyway, that’s not really why I’m typing this post. I’ve decided to post some of my observations while doing work on the engine. This is going to be useful both as a reference for me and maybe for other folks out there.
For the purpose of this discussion, I’ll use the term ‘library’ for a piece of somewhat complex software that provides functionnality to an application. A game engine or a graphics engine is a library.
Complexity in software. I looked up what complexity means in a dictionnary. It is defined as “That which is complex; intricacy; complication” [1913 Webster]. Ugh so complexity is defined as complex. Not too helpful. Complex itself is defined as “Composed of two or more parts; composite; not simple; as, a complex being; a complex idea.” [1913 Webster]
So something complex is not simple. Fair enough. Complexity is relative. Something simple to you can be complex to someone else. There is no absolute definition of complexity. So everything really IS relative
However, as far as I’m concerned as a programmer, complexity could be described as and/or be related to the number of explicit choices that has to be made designing the program and also while programming (you can push complexity out of the design phase into the programming phase). The more choices you are presented with, the more decisions you need to make. Complexity emerges from those choices. These decisions you need to make get compounded together and complexity doesn’t seem to be growing linearly.
A library is all about dealing with the complexity for the application. But where’s all that complexity going? It doesn’t dissapear magically. It looks to me like there are 3 situations that can happen:
1) You organize complexity and expose the organized complexity to the app. This is what we can call “pushing it up”. You do this by minimizing the number of decisions you make for the application. You can spot that case if you have tons and tons of options (relatively to other libs providing the same services) on the library.
2) You make some assumptions about what’s needed and what’s not needed out of the complexity cloud and you abstract it. This abstracted (and hopefully simplified) complexity is then exposed to the application to deal with. Some and/or most of the options are not exposed, you just assume what the application wants and needs (maybe you are writing the application yourself!) and you provide the needed features.
3) You expose the organized complexity to data while hidding it from the application. The options are loaded as data. The complexity gets pushed out to data; out of code. The application code doesn’t need to deal with that complexity so the library usage remains simple but is still flexible. You often hear people talk about “data-driven” systems when this situation is happening.
Of course, it’s not all Red, Green or Blue. It’s a balance of those three situations; not a discrete choice. Striking the right balance is a key element to achieving the library you want to build.
Why am I writing about this? I discussed this last night with a friend of mine who’s also doing engine work and we clearly have diverging view on what a graphics/game engine should provide for the application. In a sense, we have different goals in building our respective engines so it leads to different ways of dealing with complexity! All this is nothing new, most programmers know this ‘by instinct’ (or, more accurately, by experience). I think it’s just nice to stop a minute and think about what we do most of the time by instinct. Explicit decisions are always better; especially when engineering long-term codebases.


