Three weeks. It’s been a while since the last update.
Most of the last three weeks have involved tidying and documenting code. Documentation is important, but often not for the reasons people think it is important.
When you think about documentation for code (whether that documentation is separate or embedded), you normally think about explanations of how the code is getting a job done.
Actually, most of the time you probably don’t need documentation like that. Occasionally code is obscure about the hows, but that isn’t terribly often.
What’s more important is the why.
Why did you choose a particular container? Why is this done iteratively rather than recursively (or vice versa)? Why are we using algorithm X rather than algorithm Y?
These are the sorts of things that are really important to document.
I find it especially useful when it comes to choices about data-structures. Your language probably has a bunch of them in whatever passes for its standard libraries, each with certain runtime-complexity guarantees and gotchas.
In one commercial project, I stored a particular data-set in a C++ container that wasn’t a std::vector. I pause here for your collective gasps of astonishment at the notion that std::vector isn’t necessarily the best container for every possible purpose.
I had two primary criteria for my choice. One was that the application would spend the majority of its interactions with the container adding or removing items from the front or back. Individual elements would be referenced through iterators stored elsewhere, so I didn’t want them all invalidated every time I stuck something onto one end of the container or removed an item from an end.
Data locality wasn’t really a concern for what I was doing, so that wasn’t a part of my selection criteria.
So, a std::deque was the correct tool.
First, I verified that our compilers enforced the required standards (that is, that the container really performed as described) and then I wrote all of my reasoning down in the documentation, along with a warning: That changing the container to a type that might invalidate iterators would require a redesign to several key data-structures to prevent things blowing up.
Time passed, and I got an amazing job-offer, and moved on. A few weeks later, I got a frantic communication from the person who had been tasked to maintain the code for my bundle of applications.
They’d looked at the container, decided that a std::vector was always the better choice, and changed it. And made a slew of other changes besides. The code had gone into production without testing and had basically blown up on the production servers. They hadn’t read the documentation, and I pointed out why the container was a std::deque, and not a std::vector.
Now, there’s no real strategy that can fix the problem of people not actually reading the documentation, but at least the information was there. All of the reasoning behind the choice was explained so that whoever was looking at the code could make informed decisions about the design.
The process of actually writing it also helps you think your own choices through, and is not something that should be left until the very end.
These last three weeks, I’ve documented a lot of coding decisions that I’ve made, and in the process, I’ve found quite a number of places where I made choices that didn’t really reflect the criteria properly. The end result: I’ve got a lot of code that is very much improved, in many respects.
Overall, it’s been an excellent and valuable process, and one that I will keep revisiting throughout development.
And now the stumbles:
Background music wasn’t being properly restored after a saved-story was reloaded. Sure, it was trying, but it turns out that the current music track was not even being tagged properly in the story-log. Oops. Fixed.
Also a loading issue: All those fancy new image transitions were supposed to be suppressed on the first repaint after a game-load. They weren’t. Had to use another UI flag-bit to track that.
I’ve rewrapped naked font pointers into a structure managed by a std::shared_ptr. The idea is that we can hand those around to everything that needs them, and a font never invalidates while it is still in use anywhere. This was great, for about five minutes, but I’ve absolutely screwed this up. Badly.
If the wrapper ever removes the font pointer, everything goes to hell pretty quickly. That includes switching between regular and dyslexic-friendly fonts.
I need to take a longer look at this. I use the same mechanics for managing Surfaces and Textures without incident. Somewhere, I’ve messed up in my thinking. I probably have an extraneous instance that’s going out of scope while holding the pointer – and it thinks it is the only one in possession.
For now, I’ve worked around it by leaking some font memory (not very much or very often, thankfully). I expect I’ll get to the bottom of this pretty soon. If I start looking at when the destructors are executing, I think it will become prettty obvious where I’ve got it wrong.
Writing is slow. I’ve been working on one specific story-arc before I take the plunge and move onto the next (complex) phase of Chapter Two. Hopefully more writing progress will be forthcoming soon.
Thanks for tuning in, and I’ll probably have more mistakes to share with you next time.