Note: I wrote a significant amount of this post in 2011, back when I was actively working on Stop That Hero!, and enough still resonates today that I decided to publish it.
It’s only in the last few years that I’ve started to appreciate the importance of software architecture, and especially as I write the game engine for “Stop That Hero!” Before “STH!”, I haven’t had much experience with writing entire programs from scratch. Most of the code I’ve written professionally fit into an existing framework or architecture, so high level architectural work wasn’t something I had to worry about in my day-to-day work.
Beginner Programmer Woes
When I first learned how to program, I was focused on getting the syntax correct. Programs, even if they were completely original and not copied out of a book or magazine, were simple. More complex programs usually didn’t get finished before I lost interest. Any non-trivial programs that were successfully completed were the epitome of what we in the biz call “spaghetti code,” which means I was lucky to get something working at all. See my Pac-man clone in QBasic as an example of what teaching yourself how to program can result in.
Then I got to college, and I learned C++, and concepts such as recursion and stacks and objects. I was still using QBasic as a hobby, and my new code was definitely cleaner, but I struggled with putting everything together in a cohesive whole. And programming on a modern OS required a message pump, which meant I had to change the way I did things drastically. You couldn’t add empty loops if you needed a delay anymore.
Ok, so most likely, you’ve been there before, too. My story above isn’t unique. Lots of programmers went from DOS to a multitasking OS. The thing is, I think I fell behind in terms of learning how to program in this new world order. When I stopped using QBasic, I didn’t write a lot of C++ code outside of class requirements until I nearly had my degree. It turned out that I learned C++ wrong at first, which is why I didn’t enjoy programming in it as much as I did with QBasic. Once I read Accelerated C++ by Koenig and Moo, it made a lot more sense and was a joy to work with. That book is a great way to learn C++ for a beginner. Even though C++11 has since been released, I still highly recommend the book today.
Program Design Is Hard
But it still didn’t change the fact that larger applications were hard to make. If I knew what class or function was needed, I could write the code just fine. It was determining what class or function was needed that was the hard part. Or to put it another way, I struggled with “where should this code live” questions. Basically, software architecture was hard, and I didn’t know it was even a thing to be concerned about. Heck, years ago, I was concerned with how to put together a basic game loop. Solving that problem means I had everything I needed, right?
What I knew about game engines is based on what I read. Countless books and articles broke down the anatomy of a game engine by talking about its subsystems: audio, video, input, networking, etc. At the time, I believed this subsystem knowledge was enough to make a game. If you had a way to render to a screen and detect input, you had the bare basics to make a game. It’s just a matter of implementation, right?
Since I taught myself QBasic, and my first projects we isolated endeavors, I thought I knew how to put a piece of software together. I was able to put together an entire game, so how hard could it be? After all, they don’t give 70% reviews to just any QBasic games, right? I’ve even managed to put together complete Ludum Dare entries.
Why Is Everyone Else So Much Faster?
But I was also aware that some of the other Ludum Dare participants were able to make their entries way more impressive within hours of starting than my games end up by the deadline. Ludum Dare was historically a “write your game from scratch” competition, so it’s not as if they had full game engines available (although that’s changed with Unity entries). What was I missing?
Well, experience, for one. Some of those impressive entries are made by people who have been making games for way longer than I have. Even if we started at the same time, I haven’t been working on as many games as they have. They might have worked in the game industry and so know how to make games on a deadline. Even if they didn’t have game dev experience, they might have worked on financial software. Either way, they’ve likely written a lot more code than I have, so putting the software together to implement their game designs is possibly second nature.
Another thing people seem to have is boiler-plate code, such as code for menus, buttons, and sprites. XNA users have a huge advantage here, and Unity users are practically cheating. As I run and deploy to GNU/Linux, neither option is available to me, and since I work in 2D, there aren’t a lot of game engines available. A lot of the libraries that I could piece together also don’t fit my needs. Either they do things in a way I don’t want to do (GUIchan versus IMGUI), or they are not cross-platform. Instead, since my first Ludum Dare, I’ve written a lot of boilerplate code as I needed it. Each competition, I created more and more base code to leverage for the next project.
But I was oblivious to some of the fundamental architecture needs of a game engine, and so I still struggled to put together a finished, playable game in 48 hours. After all, the subsystems were everything. Just tie input to what’s happening in the game, and make sure the player can see feedback on the screen. Why is this so hard?
Learning Software Architecture
Most people will tell you to get a copy of the book Design Patterns by the Gang of Four. It’s a great book and features a number of patterns. Now, if you want to refresh yourself on what a pattern entails, it’s fine, but it isn’t great for learning about them in the first place.
I found Head First Design Patterns to be a great, easy-to-read introduction to major patterns.
But patterns knowledge isn’t enough to know how to organize a major software project. If I want to be able to provide a single interface to a bunch of disparate pieces of code, the Facade pattern is what I need. But what about determining that I need a single interface to those pieces of code in the first place?
And Test-Driven Development is supposed to be about code design. By writing tests, you already have a user of the code you’re writing, so you know how to design the functions and interfaces. TDD can help you design a single class, but it’s not going to help you drive the design of a full application. More and more, I realize that my lack of experience with writing larger applications is making my TDD efforts more of a struggle than they need to be. Uncle Bob Martin wrote about this topic in TDD Triage (the link has since died):
Here’s the bottom line. You cannot derive a complete architecture with TDD. TDD can inform some of your architectural decisions, but you cannot begin a project without an architectural vision. So some up front architecture is necessary. One of the most important up front architectural activities is deciding which architectural elements can be deferred and which cannot.
So patterns and TDD aren’t enough. Some architecture decisions are necessary, and I hadn’t made any. No wonder I had trouble in the past!
Ok, it’s 2014 again. Since 2011 when I first wrote this post, I’ve learned quite a bit about software architecture and designing software. Experience is one of the greatest teachers.
I’ve learned to focus on the data flow. Where does data come from, and where is it heading? I’ve learned to focus on what large pieces I need and worry about how to hook them up to each other later. I’ve learned how to separate the GUI from the internals, and that each has their own massive design decisions to worry about. I’ve learned that software architecture is less about the overall design of a software project as much as it is about the constraints on it.
I also learned that software architecture concerns don’t come into play as much if you are hacking together quick prototypes, but addressing the major software constraints can be huge if you intend to have a reusable set of code to use from project to project.
I’ll write more about the specifics in a later post, but it seemed important to document to struggle, especially as I did not identify my lack of knowledge about software architecture as an issue at first.
If you’re an indie developer, what were your major insights into software architecture? Do it come into play for you, or do you find that it isn’t anything to be concerned about in your day to day?