Saturday, June 21, 2008

Creeping Featuritis

This post was going to be on state management, but it turned into a discussion on featuritis.

My previous post on state management covered high-level game state. While rummaging around the web this week I ran across a few sites that offered finite state machines and whatnot. That's generally what people mean when they talk about state management, so I thought it'd make a good topic.

When designing a new class or building a tool to help build a game or when refactoring, one of your first questions should be, "where do I want flexibility?" Making code flexible in ways that you aren't (and won't) use means it'll take longer to get the code written and working, it'll be harder to use, and it's that much longer until you actually get something working.

So before I get into the FSM discussion, I thought I'd discuss flexibility for a bit.

One of the programmers pitfalls is gold-plating. Creeping featuritis. Whatever you call it, the tendency to make the bestest evar lies somewhere between pride and sloth on the road to hell. I believe strongly in YAGNI (see Wikipedia too): You Aren't Gonna Need It. YAGNI is a way of life, as all good principles are. It's not something you think about once in a while; it should guide a lot of your coding decisions.

For example, I'm working on editors for a game right now. My #1 goal is to get the game shipped. To have a decent, working game with a working combat and advancement system that I can use as experience when developing my next game. Whenever I think of adding some new feature to the engine, or making some system more complex and ever more awesome, I stop myself, and then figure out if that feature belongs in version 2, or 3, or 7. I write down my choice, and then go back to getting the current game working.

In fact, working on the editors is kinda backwards. The main reason I didn't start with a simple game engine is because I'm planning on using XNA, and I know way less about XNA than I do about WinForms. I figured I'd read up on XNA while building the editors, and by the time the editors were done, I'd be ready to jump into XNA coding. I like top-down coding: get something simple working, and progressively add more features. Some programmers think you should start at the bottom, making awesome libraries, but the problem there is that you never really know what you actually need.

I've learned a lot about WinForms while building the RPG4 editor (where 'RPG4' is the working title on the game). If I read a few more books first, chances are I'd still be farting around, trying to learn the framework better. I bunch of stuff I did early on was just stupid. At least, looking back, the design is bad. But I know that in part because I learned what I needed to learn as I went. Whenever I found myself adding yet another function into the main Form.cs file, I knew there had to be a better approach. Of course I knew why it was a bad idea; I knew that years ago. A decade ago. But here I had very explicit evidence about how much extra baggage you can add to a form until it becomes unwieldy.

There's a difference between too many methods in a class and too many features in a project.

I think it's a mistake to make one game (or tool or even a day-job work project) and keep adding more and more features. There's a difference between adding icing and adding cruft, and it's really a question of scale. I think it's better to err on the side of caution, of too little rather than too much. With too much, you've wasted time and energy. If you have too few features, you can always add more later.

But too many features in one class (instead of refactoring) is a different beast. I'm planning on a sequel. This is kinda like Fred Brooks' advice to "build one to throw away." I'm not worried if there's some cruft in my current project, for two reasons: (1) the design purity isn't a sign of my worth as a human being, and (2) I'm learning here! I don't need to refactor mercilessly, because in games you code it, ship it, then burn the source code. Being able to reuse systems next time is great, but being able to build a bigger, better, and faster widget next time is worth alot, too. Especially if you're going to be at a different company...

Technology changes quickly. Every year, there's new video cards out. New pixel shader standards. New consoles to consider. Phones and mobile platforms get more complex every year. It's very difficult to change a complex system to handle data in a fundamentally different way. Trying to add 3D into a 2D game engine is... blech. Besides being difficult, the solution is gonna be clunky at best.

If you build clean, tight systems, you can build them quickly. They do what you want to do, they're easy to work with, easy to change, and when you need to throw them away, you're not throwing away a lot of time and effort. I remember deciding to rebuild a 3D engine at one company, and the artists were shitting their pants. They'd seen engineers before, and what usually happened was the engineers would build, and build, and build, and then two years were passed and the thing still wasn't working.

That's what you get when you build from the bottom up. You spend time adding features you don't need, you add classes that wind up never getting used, and the whole thing is difficult to work with, too, because it's so huge. And then that hugeness starts producing bugs that are difficult to find. And then you work all weekend digging through code to find the bug, and you get it fixed, and everybody thinks you're a genius -- but two years are passed and funding ran out and the project gets cancelled.

A genius doesn't build a house of cards and then impress people by replacing one of the cards in the middle of the house. That takes a lot of talent, but the house of cards isn't what the company wanted. They just want to ship a game! Any fool can make a complex system. The smart programmers are the ones that know that a simple system with all the features that you actually need does more for the project.

No comments: