Tuesday, 14 February 2012

Dense Bugs

"The reasons go back to perhaps the most important empirical result in software engineering, one we've cited before: the defect density of code, bugs per hundred lines, tends to be a constant independent of implementation language. More lines of code means more bugs, and debugging is the most expensive and time-consuming part of development." — Eric Steven Raymond, The Art of Unix Programming.

I rather dislike verbosity in code, so I would love the above quotation to be true. Unfortunately at the moment I'm running on received wisdom and gut feeling; I have yet to find any concrete studies on the subject. It certainly wouldn't be easy to perform such a study, given the moving target of popular languages, frequently poor distribution of languages in a given domain, differing fluency of programmers requiring large sample sizes, choice of how one enumerates bugs and when one chooses to do so... I'd hope that a language that makes high-coverage automated testing easier would have a somewhat sharper decline in extant bugs over time, but maybe that's simply wishful thinking.

There are limits to how far this pursuit of brevity can be taken, of course. The more conceptually dense the code the more wetware involved in expanding it to a form that can be understood, and humans are hardly infallible when it comes to such transformations. Misunderstanding an obfuscated chunk of code will lead to the introduction of bugs, and the point at which pleasantly concise turns into impenetrably terse will be different for each reader. Meaningful naming of intermediate values helps me unpack a series of functional transformations of a collection, so I'll fairly regularly expand a series of maps, filters and folds within a let expression, and if necessary interject comments (with small functional chunks of code producing values bound to meaningful names, it is rarely necessary). So far so standard.

On the flipside, various kinds of boilerplate that add nothing meaningful beside additional LOC are all too common, even when avoidable. To take a small example, as the quite lovely Effective Scala notes, there is little benefit to "syntactical ceremony" (I love that phrase) by way of superfluous braces on simple functions or control structures. Yet many languages do not allow for such details to be elided, and even if they do we have a curious tendency to enforce maximal syntactic salt. I have never yet worked at a game developer whose coding standards allowed the quite standard C++ convention of skipping the braces on single-line conditionals and loops.

I call bullshit on this kind of ruling. In fact, I am not at all convinced by coding standards beyond a way to record naming conventions, formatting of comments to generate valid automatic documentation, and very general guidelines for style where the choice might otherwise be non-obvious (e.g. avoid using interfaces for small data-structure objects used in an inner loop, those vtable jumps add up quickly). When it boils down to the dictation of salting levels, especially favouring more rather than less, such documents cease to be useful.

In my opinion, anyway. I'm sure someone disagrees, because the bloody things never cease to be a pain in the neck.

Long story short, death to large codebases and coding standards that promote them, no matter how small the increment.

Game progress ticks along, I'm currently investigating dual quaternion skinning and fiddling with the relationship between the game logic loop and the renderable representation of logical entities. In particular, it was deeply confusing having multiple monsters performing multiple actions all at once, so I've fallen back on the model used by Wizardry 8 (among other games): wait for animations to complete before letting the next logical entity take a turn. I am worried that this will become painful for large groups of monsters, so this will doubtless require iteration.

I'm also going to have to check how to better push animation state onto renderable entities. At the moment it's just inferring that a "hurt" animation needs to play because the entity has less health than it had last turn, and so on. This is simple and makes sense in some cases, but does preclude having a "stagger backwards from explosion" animation that is unique from a "walk backwards carefully" animation, for example.

No comments: