May 15th, 2008

Party Of One: Maintain

The previous entry on one-man open source projects has talked about the development facet. Today, i’m going to talk about perhaps the most unappreciated and overlooked part of any project – the maintenance. It is my belief that if you want your project to become and stay successful, you must treat the maintenance part with utmost care and make sure that it receives all necessary resources, even much more so than the development does. The typical work environment usually enforces this with the relevant managerial structures. When you’re in charge, it might be very tempting to forego the more “boring” and “tedium”parts in favor of the “fun” and “creation” development tasks. However, it is quite easy to manage both aspects, and doing so is the subject of this entry.

While one needs to stay committed during the development tasks, it takes a concentration and focus for the maintenance.

As with the development, there are three main parts to the maintenance facet of the projects and here is the list with the most important one at the top:

Many developers consider bug reports as a sometimes unwelcome interruption to the development flow. Some will blame the users for their incorrect understanding of the APIs, some will postpone the existing bugs indefinitely and perhaps even half-jokingly call them features, and some will follow the existing industry trend of gathering up all the bug reports until the last two weeks before the release and handle them in a condensed and focused “bug-squashing” effort.

Unfortunately, these approaches ignore the vital part of a bug report – the submitter, or the OP in some parlance (stands for original poster). They all overlook a simple point that the person on the other side of the bug report has gone way beyond what we usually do. Not only has he decided to download your distributables, not only he has found enough time to play with your test applications or even write his own one, he was also courteous enough to find a way to contact you and describe his experience with your product. The least you can do is to fire off an immediate and polite reply.

Not all bug reports are valid. Some will come from users who have no idea what are they doing. Some will not contain enough information to reproduce them. Some will come via channels that you explicitly discourage (for example, personal e-mail instead of the official project tracker). Some will contain vague hints of threatening to switch to alternative products if the bug is not addressed immediately. Some will have nothing to do with your project and originate in other libraries. You just have to remember one thing – it is always your fault.

People just don’t have enough time. They don’t have enough time to read the documentation. They don’t have enough time to search the forums and the mailing lists. They don’t have enough time to look at the source code. Most certainly they don’t have enough time to “download the head and compile”. If your API provides too many ways to achieve the same task, and the bug report is on a non-recommended way – it’s your fault. If your documentation does not have an immediate answer – it’s your fault. In fact, the documentation is a chicken and egg problem – if your library needs documentation, it’s unfortunately your fault as well. The best thing to do is to accept this catch-22 situation and try to assess the best ways to alleviate the user pain.

Make sure that you have as many ways as possible to report a bug or raise a question. Having archived mailing lists and forums is great for the “ivory tower ego” type (with a knee-jerk instinctive reaction to “first search the forums and RTFM”), but people just don’t seem to do this. Even a simple requirement of registering to a mailing list to send a mail is a turn-off for many. Replying to a personal e-mail by redirecting the questions to the archived mailing list / forum might not be a welcome thing for users that do not want to divulge potential IP aspects of their applications in public.

Respond to a bug report immediately, and it will do wonders to establish early adopters and give you extra testers. Mark every bugfix code path with bug number and simple description so that you don’t accidentally refactor it out after a few months. The worst thing that can happen to a new feature is no feedback at all. It doesn’t mean that it’s flawless. It simply means that nobody’s using it.

If i may digress a little, here is what you would frequently see in open-source projects that don’t interact with such entities as steering committees, governance models and incubators:

Add to this a spruce of Maven-generated goodness to create an illusion of documentation and you have a recipe to quite a few open-source projects out there. If you’re fortunate enough, you get a little of code health (more on this in a moment) and real documentation; sometimes you’re not so lucky and too much code health results in rewriting the entire project from the scratch, getting back to what we love the most – throwing out old code and writing new one. In reality, your priorities should look like this:

Having the features at the very bottom of the list might sound alarming for a commercial product (what are we going to sell, exactly), but the first three are overwhelmingly more important for the long term health of a one-man project. Going back to the proposed ten-week release cycle for a minor release – yes, sometimes a minor release will not have any new features at all. In order to understand this priority list, allow me to analyze the internal implementation complexity over the cycle of a few consecutive releases.

If you have a clear project vision as mentioned in the previous entry, every new feature needs to conform to that vision and interact seamlessly with every existing one. Otherwise you end up with featuritis and feature soup. In an ideal world, adding a new feature has the same complexity no matter how many existing ones you already have. In a real world we’re all humans, and we all make mistakes (as we get more experienced, our mistakes tend to get more elaborate and destructive). Wouldn’t it be nice if every once in a while this graph would go down instead of climbing up?

The big question, of course, is how exactly can you do that? The smaller one, but important as well, is can you go back to the internal complexity level of the previous major release? As we’re talking about the internal implementation complexity, lowering means changing. It can be simple refactoring, or can go as far as introducing binary incompatibility and breaking existing applications. How far can you go until your users or even early adopters abandon you? And finally, what is more important to you, your users or your vision?

These are not simple questions, and they do not have simple answers. For me, it boils down to one concept – code health. Code health is the intangible metric of your confindence in the code stability, in your ability to find and fix a bug and in your ability to adapt the code to the new requirements. In my opinion, it is the second most important thing after prompt bug fixes, and it has three main parts:

These are the things that you would do when the code becomes too complex. I would even say that these are the things that you should do before your code becomes too complex. In fact, you shouldn’t take the pride in how complex your code is.

The internal refactoring is, of course, the simplest step to prevent code complexity, but sometimes it is not enough. As i mentioned already, we all make mistakes, and some of the mistakes are in the exposed API layer. It might be a bad method name, a missing parameter, an incorrect modeling of the user flows or even too many ways to achieve the same thing. As long as you either have a better way to achieve the same functionality, or a very compelling and preferably non-technical reason to remove an existing functionality – do not be afraid to break APIs and compatibility. If you’re small, nobody will care because nobody will notice. If you’re big, the users will trust you and come with you. Otherwise, it may be a very good indicator if you can get big or not.

The last thing that i want to talk about is the task schedule. This is how you break those ten weeks and allocate different tasks. Here, you should follow the sources of instability and weigh the risks against the benefits to decide when each task should be done. In general, the instability comes from new features, internal rewrites and bug fixes, and you should minimize each stage and confine it to end as early as possible during the release cycle.

Adding new features is the biggest instability risk and should be done in the first four weeks of the cycle. Here, you can hope that your early adopters and testers will be kind enough to test these features and provide enough timely feedback. If they don’t, and you make a mistake that is exposed only after the release is done – you need to wait for the next major release to remove / replace it. If you follow the proposed a-major-release-a-year schedule, you won’t wait too long.

The internal rewrites should not (in principle) have much effect on the users code. The reality, of course, is not ideal. Your users can depend on an undocumented incidental behavior. They can decide to use internal classes or even resort to hacks without ever asking for a proper way to achieve the required functionality in the project forums and mailing lists. However, the internal rewrites generally have less impact than new features, and as such may be done until two weeks before the release candidate. The recommended release candidate date is two weeks before the final release.

Bug fixes should be done all the time. In fact, the earlier you do it, the better it is for your project. If you engage in this cycle the very same day as the bug report arrives, you have much greater chance of getting an actual confirmation from the user that it works. And if you are lucky enough, every once in a while this user will turn into an early adopter. And once release candidate is out, you should fight your every desire to add a new seemingly miniscule feature or do any sort of refactoring.

Since you have only four weeks to add new features, you’ll need to sharpen your skills in making compromises on what goes in and what stays out and waits until the next release. If your users threaten to switch to an alternative product because of “just this one feature” that is missing – it might be a good thing for your project in the long run. However, just because you “need” something to show to warrant a new release doesn’t mean that you must force a solution to the specific user request or a new feature that you want to add. Let your unconscious work on it for a while.

The next entries will continue talking about the specific project tasks.

  • Part 4 talks about the promotion
  • Part 5 summarizes the discussion

The image is available under Creative Commons Attribution NonCommercial license from the following flickr.com streams:

  • Meerkats by 814 carthage