02.15.08
Posted in Game development, General development at 1:08 pm by Kyoryu
A lot of people have probably heard of test-driven development, and the buzz around it. There’s evangelists and zealots, frameworks (unit tests and mock objects and acceptance tests, oh my!), and a lot of heated discussion.
What I don’t see a lot of is the basic ideas of test-driven development, distilled and put into a form that most programmers can relate to.
A typical pattern that I’ve seen is the creation of separate applications or workspaces to isolate a component while you’re developing it. If you were to do that with a stack, it might look something like this:
static public void main(string[] args)
{
Stack s = new Stack();
s.Push(“foo”);
s.Push(“bar”);
string test = s.Pop();
System.Console.WriteLine(test);
}
In some cases, you’ll write this before you even write the stack class, as a way to define your API in an outside-in way.
After you’ve written your Stack class, and you get the results you want, you’ll probably look at the insides of it to optimize it, or make it designed better.
If you’ve done this, congratulations, you’ve used test-driven development. No, really. Everything else surrounding TDD is nothing but tools to help write and run these types of testbed or sandbox programs. For an experienced developer, it really is much ado about nothing.
Permalink
12.28.07
Posted in Game development, General development at 2:45 am by Kyoryu
A couple of thoughts I’ve had on the TDD process that may not be inherently obvious.
- If you already have a complete spec with all of the API, and cannot deviate, you are not doing TDD.
- When writing the test first, the failed compile is a good thing. This helps prove that you don’t have any kind of strange name overwrites that you’re unaware of. The value of this, of course, depends on language.
- Once the compile errors are fixed, the failed tests tell us that the component isn’t “accidentally” doing what it needs to be. If it is, we need to figure out in what circumstances, or why.
- One of the big problems people seem to have with TDD is not refactoring. Refactoring is, in my opinion, the most important step. Without refactoring, using TDD, you will have garbage code (though it’ll probably have a nice API).
- People seem to balk at the idea of writing the minimal code to get the test to pass. They somehow think that this is supposed to be final code. It’s not (necessarily). The point is to allow you to enter the “green” (refactoring) phase earlier and with less work. When possible, we want to be in the “green,” so that we know if we do something that causes a break. If we try to write our ultimate design up-front, that just means that a lot of code is being written at one time without any assurances that it works.
- If you find that you need to refactor to make a test work, comment out the test first, and then do the refactoring. Always refactor when green. Otherwise, you’re getting into the trap of refactoring while changing functionality, and that’s a slippery business.
- People think TDD means no design. I don’t agree. Generally, you start with a class, which implies a certain amount of design. But TDD folks think about design, a lot. They just don’t think about design a huge amount up front, except for high-level architectural decisions, and they retain flexibility to change the design in case they were wrong.
And a final though: Since adopting TDD, I find development without it to be somewhat weird. Just as non-TDD’ers think we’re weird for writing tests first, I think what they’re doing is strange. To me, writing tests last (or not at all!) is essentially writing a bunch of code, and then trying to figure out what it does. It just feels very unsafe, and I just get a weird feeling when doing it.
I don’t know how valid that is, or if I’ll think I’m an idiot in five years for thinking like this.
Permalink
12.20.07
Posted in General development, Uncategorized at 1:46 pm by Kyoryu
“Plans are nothing. Planning is everything.” – Dwight D. Eisenhower
One of the common criticisms I see of any kind of agile development (note: agile with a little a, not a capital A) is that people seem to believe that agile development means that you don’t plan, don’t design, and don’t document.
In my opinion, this couldn’t be further from the truth.
In fact, I’d go so far as to say that agile development involves more planning, more design, and more useful documentation than other methodologies. The key difference is when you do the planning, to what extent, and what information you derive from it.
I’ve worked on projects that planned to the extreme. There was a gantt chart which specified what you would be doing at 2pm on Thursday. Now, that’s somewhat okay, except that the Thursday in question was two years into the future.
Let’s come up with a hypothetical example. Let’s say you’re going to start a brand new Generic Fantasy MMO from scratch. Do you just start diving in and writing code and making art? I’d hope not, and I doubt anybody with a brain would suggest that.
You start with planning. You know you’re going to need art, some levels, some amount of design documentation, and code.
You’ll know you’ll need some areas for people to walk around and swing their swords. How many? Well, that’s a function of how large they are, but we’ll say 100 distinct “areas,” where each area is more-or-less self contained. How will we make them? In Max or Maya? A custom tool? Who will make them? If designers are doing the layout flow, we probably don’t want to have them use Max, so we’ll need a custom tool. How will that tool work? Will it plug together pieces Lego-style? While it give more freedom, and then allow artists to perform a beautification pass? How many levels will be inside vs. outside, and how does that impact what tools are needed? What are the plans for future expansion after release?
Once we have some of these answers, we can start putting together a plan. Let’s say we want a 50/50 mix of outdoors and indoor areas, so we’ll need relatively strong tools for both. We want designers to do at least initial layout, and we’re pretty sure we can use a “Lego-brick” approach. We’ll still need artists to create the bricks. But, we can start using this knowledge to make a plan for what resource we’ll need, when we’ll need them, and how we can go about putting this thing together. In this case, we’ll probably need at least one programmer pretty quick, with at least some art time not too long after that to create some sample “bricks” (once we get past the programmer art stage). We can make a rough guess as to when at least some functionality will be done, and start making sure we have more level designers and artists for when that happens.
And the plan will be wrong. Period, end of story, no matter what. Your estimates will be off. Things will change, your knowledge of the problem will increase, and you will have to adapt.
So, in that case, what’s the point of planning? In this case, the simple act of planning gave us a good amount of the information that we needed at that moment to make the decisions we needed to, and could make, right then. It gave us enough information to start making staffing decisions, and helped us define the scope of what we were doing. It made sure that we thought the problem of “how do we make levels” through, and didn’t miss any major pieces of that particular puzzle.
We didn’t try to nail down too many specific details. We didn’t say exactly what each dungeon should be, or how we were connecting areas, or anything at all of that nature. Those are details that are unimportant at this time. They don’t impact a single thing that we’d do for the next month or so. So, instead of wasting time arguing about them now, do another planning session in a month (or 2-3 weeks, or whenever), revise the plan as your knowledge has increased, and work from there.
People using agile development (I refuse to use “agile” as a noun or to capitalize it) do plan. They plan a lot. They just see the value of planning in the information that comes out of the exercise, not the specific set of dates and requirements.
This seems directly counter to what I’ve seen in BDUF-type organizations. Plans, in those orgs, tend to focus excessively on the detail level, and actually focus less on the high-level decisions. In my experience, those are the wrong things to worry about at the beginning of any project. They’re almost always easy to change, require knowledge you don’t have anyway, and subject to iteration. Yet, somehow, I see planning documents that focus extensively on these minor details, and merely imply the larger decisions via these details.
Permalink
12.02.07
Posted in General development, Uncategorized at 3:43 am by Kyoryu
Everyone’s had to deal with it. The big, nasty project, where everything links to everything, and anything you do can have seemingly random effects in apparently unrelated areas.
So, let’s look at some ways to avoid that.
But first, let’s define what we mean by “coupled.” Two pieces of code (classes, modules, whatever) are coupled if they have to know how each other work in order to function. Couplings can exist one-way, or two-way. Some coupling is good – programs probably wouldn’t do much without them.
Where the sinister evil starts to sneak in is unnecessary coupling, and unnecessary intimacy.
To start with, let’s look at a piece of code:
public class SomeClass
{
public void DoSomethingToCharacter(Character c)
{
string username = c.Connection.User.Name;
}
}
Okay, so it doesn’t do much at this point. But it’s a pretty good start as to where there might be a problem.
And what problem is that?
Well, it’s pretty apparent that this method needs to do something with the character’s name. But to get that information, we have to go through both the connection and user properties.
This opens us up to problems. First, it means we have a physical dependency on those classes, which can create compilation time issues.
Secondly, it means that to get the data we want, we have to know quite a bit about the class we’re using. We have to know that it has a connection object, and that that object has a user object, and that object has a name. I don’t want to know that much. I just want the user name from the character!
The worst problem is that we open ourselves up to build breaks every time we use this access pattern. If any of those classes change, we break. And probably in lots and lots of places.
Let’s look at an alternate:
public class SomeClass
{
public void DoSomethingToCharacter(Character c)
{
string username = c.UserName;
}
}
Ah, yes. Isn’t that pretty? Now our code only cares about the character we class. We don’t know how many layers of ugliness are going on underneath, and we don’t need to. Even better, if one of them breaks, the damage is going to be limited to inside the character class itself – which means an obvious place to fix, instead of hunting down those chains of objects all over your codebase.
Oh, and for what it’s worth – those chains of objects have a name. They’re called “train wrecks,” after all of those objects smashed into each other. It’s not a term of endearment.
And what I’ve been describing has a name as well – it’s called the Law of Demeter. It basically states that a given method can only use methods internal to objects it owns, on objects that are passed in, or on objects it directly creates.
Permalink
« Previous Page « Previous Page Next entries »