Today, many mature libraries and frameworks exist for each programming language, and many advanced features have been added to the languages. But what about the old projects, when language features were not as advanced as they are today and few mature libraries existed?
Let’s explore some old, well-implemented projects and discover how they were implemented.
Prince of Persia
Prince of Persia is a fantasy platform game, originally developed by Jordan Mechner and released in 1989 for the Apple II, that represented a great leap forward in the quality of animation seen in video games. On April 17, 2012 Jordan Mechner released the source code of Prince of Persia.
Many gamers remember this amazing game, and perhaps some of you played it for months.

Developers from those days remember a time when a typical personal computer might have an 8 MHz processor, 1 megabyte of memory, a 20-megabyte hard disk, and a floppy disk drive. It was a big challenge to develop a game like Prince of Persia.
Moreover, in those days Google was not available to help developers resolve technical issues quickly; some technical problems could take developers many days to fix. And, to top it all off, it was developed using the 6502 assembly language.
Despite all these constraints, the source code is well implemented:
- It’s modularized using directories and files:
Modularity is a software design technique that increases the extent to which software is composed from separate parts; modular code is easier to manage and maintain. Prince of Persia was modularized using directories and files — this modularity is provided by the operating system and can be applied to any language.
The code is split into many files, here’s a list of some of them:

- The naming is easy to understand
When exploring the source code, you won’t find variable names such as a, b or x, unlike in many recently developed projects. The names are well chosen, and no comments are needed to explain why we need them.

- The code is split into many small subroutines
The 6502 assembly language is very low level, and to make the code easier to understand and maintain, the “Divide and Conquer” principle is applied. Indeed, the code is split into many small subroutines, which makes them easy to read and maintain. Here’s an example of a small subroutine from its source code:

Even if in 1989 many constraints complicated the task for developers, the code is very well implemented. So why, in 2014, with powerful computers, powerful languages, many thousands of libraries and Google, are some projects badly implemented?
Languages and frameworks are just tools to build applications, but the main actor is the developer. You can use the best language and the best frameworks and still produce bad code.
Many practices that make code clean are not language-dependent. A good developer must have good sense and make their code clean and easy to understand, regardless of the language used.
Doom 3
Doom 3 is a video game developed by id Software and published by Activision. The game was a commercial success for id Software, with more than 3.5 million copies sold.

On November 23, 2011, id Software maintained the tradition and released the source code of their previous engine. This source code was reviewed by many developers; here’s, as an example, the feedback from Fabien (original source):
Doom 3 BFG is written in C++, a language so vast that it can be used to generate great code but also abominations that will make your eyes bleed. Fortunately id Software settled for a C++ subset close to “C with Classes” which flows down the brain with little resistance:
- No exceptions.
- No References (use pointers).
- Minimal usage of templates.
- Const everywhere.
- Classes.
- Polymorphism.
- Inheritance.
Many C++ experts don’t recommend the “C with classes” approach anymore. However, Doom3 was developed between 2000 and 2004, which could explain why the modern C++ mechanisms were not used.
Let’s explore its source code using CppDepend and and discover what makes it so special.
Doom3 is modularized using a few projects; here’s the list of its projects and some statistics about their types:

Here’s the dependency graph showing the relationships between them:

Doom3 defines many global functions. However, most of the processing is implemented in classes.
The data model is defined using structs. To get a concrete idea of struct usage in the source code, the metric view above shows them as blue rectangles.
In the Metric View, the code base is represented through a Treemap. Treemapping is a method for displaying tree-structured data by using nested rectangles. The tree structure used is the usual code hierarchy:
- Project contains namespaces.
- Namespace contains types.
- Type contains methods and fields.

As we can observe, many structs are defined; for example, more than 40% of DoomDLL types are structs. They are systematically used to define the data model. This practice is adopted by many projects, but this approach has a big drawback in the case of multithreaded applications: indeed, structs with public fields are not immutable.
There is one important argument in favor of using immutable objects: it dramatically simplifies concurrent programming. Think about it — why is writing proper multithreaded code a hard task? Because it is hard to synchronize threads’ access to resources (objects or other OS resources). Why is it hard to synchronize these accesses? Because it is hard to guarantee that there won’t be race conditions between the multiple write and read accesses made by multiple threads on multiple objects. What if there are no more write accesses? In other words, what if the state of the objects accessed by threads doesn’t change? Then there is no more need for synchronization!
Let’s search for classes having at least one base class:

Almost 40% of structs and classes have a base class. And generally in OOP, one of the benefits of inheritance is polymorphism; here are, in blue, the virtual methods defined in the source code:

More than 30% of methods are virtual. Few of them are pure virtual, and here’s the list of all the abstract classes defined:

Only 52 abstract classes are defined; 35 of them are defined as pure interfaces, i.e. all their virtual methods are pure.

Let’s search for methods using RTTI.

Very few methods use RTTI.
To summarize: only basic OOP concepts are used — no advanced design patterns, no overuse of interfaces and abstract classes, limited use of RTTI, and data defined as structs.
Until now, nothing special differentiates this code from many others using “C with Classes”, an approach criticized by many C++ developers.
Here are some interesting choices made by its developers to help us understand its secret:
1. Provides a common base class with useful services.
Many classes inherit from the idClass:

The idClass provides the following services:
- Instance creation.
- Type info management.
- Event management.

2. Make string manipulation easy
Generally, the string is the most used type in a project; many operations are done using strings, and we need functions to manipulate them.
Doom3 defines the idStr class, which contains almost all the useful methods to manipulate strings — no need to define your own methods, as is the case with many string classes provided by other frameworks.
3. The source code is highly decoupled from the GUI framework (MFC)
In many projects using MFC, the code is highly coupled with its types, and you can find MFC types everywhere in the code.
In Doom3, the code is highly decoupled from MFC; only the GUI classes have a direct dependency on it, as shown by the following CQLinq query:

This choice has a big impact on productivity. Indeed, only the GUI developers have to care about the MFC framework; the other developers don’t have to waste time with MFC.
4. It provides a very good utility library (idlib)
In almost all projects, the most used types are utility classes, as shown by the result of the following query:

As we can observe, the most used ones are utility classes. If C++ developers don’t use a good framework for utilities, they spend most of their development time fighting with the technical layer.
idlib provides useful classes with all the methods needed to handle strings, containers, and memory, which makes developers’ work easier and lets them focus more on the game logic.
5. The implementation is very easy to understand
Doom3 implements a hard-coded compiler, and as C++ developers know, it’s not an easy task to develop parsers and compilers. However, the Doom3 implementation is very easy to understand, and its code is very clean.
Here’s the dependency graph of the classes used by the compiler:

And here’s a code snippet from the compiler source code:

We have already studied the source code of many parsers and compilers. But this is the first time we have encountered a compiler whose source code is so easy to understand, and it’s the same for the whole Doom3 source code. It’s magic. When we explore the Doom3 source code, we can only say: WOW, it’s beautiful!
Even if the Doom3 design choices are very basic, its designers made many decisions to let developers focus more on the game logic and simplify all the technical-layer work, which significantly increases productivity.
However, when using “C with Classes”, you need to know exactly what you are doing. You have to be an expert like the Doom3 developers. Beginners should not take the risk of ignoring the Modern C++ recommendations.
