2013
02.20

I’m often asked why I don’t use C++ namespaces, or Standard Template Library, or C++11 or why I don’t put getters and setters to hide those dirty public class members.

Some people have a very strong opinion about what clean code means and get all shivery as soon as they see two return statements in a function.

Well, I’ve been programming for quite a few years and I don’t think there’s such a thing as dirty code or clean code. On the other hand, code can be complex or simple. It can be fast or slow. It can be easy to maintain or not. All those properties can be determined conclusively. I always try to have the simplest code as possible. Less lines of code means less bugs. 90% of the time, simpler code runs faster. Obviously, simpler code is easier to maintain because it’s both easier to understand and requires less work to update. The idea that splitting your code into a myriad of independent layers and hiding implementation behind interfaces will make it easier to maintain is a huge fallacy because 90% of the time, the changes are orthogonal to the layers and all this separation of concerns stuff only means one thing : more boilerplate code to update, longer compilations, slower execution, programmers turned into millefeuille-software maintaining code monkeys.

I could write a long article about why I think STL is a terrible library, or XML a terrible data representation language, why C++11 will turn C++ into the most obscure, seedy language, but that would be painful for everybody. Instead, there’s an article summarizing pretty much everything I think about code. It’s a great, pleasing read and a good pragmatism booster shot :

http://kotaku.com/5975610/the-exceptional-beauty-of-doom-3s-source-code

[update] as mentioned by someone, what I call STL is now called C++ Standard Library. My bad. Changing the name didn’t make it any better ;P

12 comments so far

Add Your Comment
  1. It’s called stdlib FFS. Not STL. STL was another person’s project in the 80s that developed into stdlib.

    And no. The fact libtcod is basically an lone wolf project is PRECISELY because you don’t put effort into cleaning up your code or using standard code that everyone understands. This is very important when making a library or engine you intend for many people to use.

    Anything that decreases the requirement for familiarity with the codebase is most critical to the success of libtcod. Enough said!

  2. Layering, etc.: I generally agree. I see a lot of coders (particularly people who haven’t spent many years writing APIs) get very excited about separation of concerns, adding abstraction layers, etc. And it’s true–these are all good techniques. But they’re techniques for managing systems as they grow more complicated. When you apply any such principle universally, from the very beginning of a project, it turns into a nightmare–because you absolutely don’t know which abstractions make the most sense, or even where the complexity will appear in your system.

    If you’re aware of how to do these things, and are prepared to apply the techniques *when they become necessary*, you write what is in my opinion the “cleanest” code. Code that’s only as complicated as it has to be to get the job done. You pay a price in terms of needing to refactor old code into fresh APIs–but even then, the price is quite low when you’re moving from a dirt-simple API to a more nuanced layered API that solves your problem. You can probably even afford to maintain the old API as a “simple” compatibility layer, at least for a little while.

    In contrast, if you build in abstractions that seem to make sense at the start of your project, only to later discover that they’re not good abstractions at all, you end up being forced to refactor everything from one complex abstraction to another. That’s a much harder task than moving from simple to slightly more complicated, and it’s much more difficult to maintain compatibility by providing an “old abstraction” view on the “new abstraction”.

    So, when I work with a new coder and see them applying these “universal” practices to their code, no matter what level those practices are at (for example: “Make it object oriented!!1!” is another obvious thing that isn’t always actually the simplest answer), I tell them “Here’s a new principle for you: only build what you need to build right now. Leave future problems for the future–chances are pretty good that you don’t have Nostradamus as a system architect. In short: Keep It Simple, Stupid.”

    Sure, you’ll make mistakes, but at least they’ll be small ones.

    P.S. Your feelings about C++11 mirror mine about C++ way back when they introduced templates. (Not the STL, just templates.) 🙂

  3. My two cents:
    – c++ namespaces avoid name collisions without any prefixes that the user does has no choice to write (e.g. using namespace Tcod; and in collision adding either Tcod::random or std::random or Y::random, wheras you have to write TCODRandom). Also it lets you group things into sane hierarchy
    – C++11 has nice threading (finally) and smart pointers (especially unique_ptr) eliminating most of naked pointers and all the rule-of-3 bullshit.
    – independant layers are evil, and yes that means that you need code monkeys (lots) to code/maintain that. We had a semester about programming engineering and when the lecturer snapped he said: Yeah you don’t need all this stuff to code, but when you will write for an ancient system or with 100 of other programmers or you/others don’t know how to code than all this is only thing that keeps everyone sane. I hope never to work with such projects.

  4. @Y.A. My remarks were general, not related to libtcod’s code. I don’t consider it to be good code. Especially the rendering part is neither simple nor easy to maintain. It’s a total mess.
    No the idea was rather to question the current coding standards and dogmas. A lot of people rely on them without ever questionning them. Of course I did it in a pretty trollish way 😉

    @Prevost a lot of people agree that C++ is one of the worst languages from the syntax point of view, even without C++11. That’s also my opinion. But we’re still using it because there’s no real alternative if you consider the whole thing (tool chain, portability, performance, interfacing with existing libraries, ease of deployment, …)

    @Warmist I know namespaces sound like a good idea. But really, does TCOD::Random make you code simpler/faster/easier to maintain than TCODRandom ? Nope. Using the namespace to get rid of the TCOD:: prefix would also be an error because now a newcomer doesn’t understand what is in your game and what is in the library.

  5. About getters and setters, one thing i remembered: TCODConsole::root is a naked static pointer, while TCODRandom::getIntance() returns a pointer. So this imho needs to be made getters or static pointers also it would make sense to return a reference instead of pointer (returning a pointer just confused newcomers, do i delete after using?) Also afair there is a lot of examples in a pattern “TCODRandom *r=new TCODRandom; ” where simple object would be enough.

  6. @Warmist I agree it’s not very coherent and again, this post was not a way to say “libtcod’s code is an example to follow”.
    TCODRandom uses a singleton pattern to get the default RNG, because you need a function to initialize the object on first call. I could initialize it statically and just use TCODRandom::default, but there’s no point allocating memory if the game doesn’t use it. On the other hand, the root console has a dedicated building function (initRoot). I can’t use a singleton here because it would require a lot of parameters (console size, font,…). Adding a dumb getter wouldn’t improve anything except giving the false impression that TCODConsole::getRoot might work the same way as TCODRandom::getInstance.
    C++ references is also something I tend to avoid because when you read the code, nothing tells you whether you’re manipulating a reference or a plain object. A raw pointer has the advantage to be explicit and to show exactly what the CPU is actually doing. Of course, there’s always the question whether it’s up to you to free the pointer or not. This is generally stated in the documentation but I can’t see how to make it explicit while reading the code. So yeah, that’s a half-backed C++ wrapper with a lot of C philosophy in it. Maybe I should have kept only the C interface, like SDL. It would have been more acceptable for a C++ purist.
    Maybe the examples should be simplified indeed. I don’t know why I seem to systematically allocate stuff on the heap in them.

  7. I agree completely about making code easier to understand using simple language features, and avoiding taking the concept of ‘everything is an object’ to the extreme, which can result in having to understand many layers, patterns and interactions between objects. SFML is a good c++ library in this respect, it keeps things extremely simple and consistent and doesn’t use any complicated design patterns.

  8. Maybe i’m a bit off-viewed, but i think that defining good /clean code is almost impossible:
    I compare developing to writing a book ,maybe it’s just a naive approach, but a programming language is just that: a language with a grammar and syntax to observe.
    Exactly the same as for any spoken/written language.
    You have to write something that the other counterpart can understand.

    Here we are not talking about the “reader” point of view, but the “writer” point of view.
    And i think that ,even if some kind of rules can create a cleaner code, its “beauty” is purely personal.
    This is due to every developer grasp on the language, knowledge, and way of thinking.

  9. Absolutely agreed! Especially with minimizing layer code. I understand trying to predict future needs (eg., writing setters/getters because later you may need to enforce some conditions), but it’s so much work and obfuscation for a prediction that fails 90% of the time. I think that instead of trying to write pre-emptive code, you can just leave it to search & replace in the (rare) time when you actually need to change from a property to a getter/setter!

    The only part of the article I don’t agree with is on stringstreams; they require more machinery but they’re much less error-prone than printf-style.

    About Y.A.’s point, I think most of libtcod is pretty elegant except for the low-level bits; but you can’t really get meaningful stuff done at the system level *and* have elegant, easy-to-understand code at the same time. At some point the rubber has to meet the road and get dirt all over.

  10. I like lambdas. Life would be dull without them. But we can all code how we like, can’t we? There’s no need to turn it into a fight.

  11. For hobby projects, you can code how you like, but in the professional world, you have to follow the “state-of-the-art”. The problem is that people and trends that define state-of-the-art often forget their original intent. For example layering and separation of concerns has one goal : split the application into small independant parts so that it’s easier to maintain. But nowadays, layering and separation of concerns are loved for themselves which lead to applications that are harder to maintain. They may not be more complex, but they certainly require more work to maintain because of the overuse of those concepts. Coding standards have always been abused by people starting to love the standards and overapplying them while forgetiing why they were created in the first place.

  12. But we aren’t in the professional world, are we?