It’s always the right thing to do to fix a bug, isn’t it? I mean, they’re bugs for a reason – surely you should squash them whenever you find them?
The answer to that is (surprisingly for some people), ‘no’. Don’t get me wrong, it’s usually the right thing to do – the problem is though that some bugs are so ingrained that people (who often didn’t realise there was a bug at all) have been working around them for so long that your fixing of the bug will result in it actually reducing the net quality of the project’s code, across the board. Fixing your bug will cause more bugs purely because the code will actually be working properly.
It seems like a counterintuitive point, so I’m going to give an example from the lib we’re using here – a bug we inherited from the Discworld mudlib that we have been pimping out over the past year and a half.
We have a system of ‘properties’, and these let you tag an object as having a particular quality about it. You might tag a player as ‘has just had their ass kicked’ or you might tag an item as ‘being an umbrella’. Other parts of the mudlib will look for these kind of properties and treat those objects as a special case on the basis of that. It’s a useful system, but not without its flaws as I’m sure you can imagine.
You can also add ‘timed properties’, which are just the same thing except after a certain period of time they are silently removed from the object. You can add a property for 30 seconds, or for 30years… it doesn’t make a difference, it’s just a number. When the property is accessed, the first thing that the code does is check to see if it should have timed out. If it should have, it does so and then we all pretend that it was never there in the first place.
The code is *supposed* to handle it so that it is only time spent logged in that counts. There’s a whole mess of code that is supposed to manage that – manipulating the stored timed value based on login and logoff time. The thing is, that code doesn’t work – I’m not sure if it ever worked. So, over the decade or so that timed properties have been available, people have written code under the assumption that it’s a real time, not game time, limit. They’ve used it to do things like ‘stop person X buying anything at this shop for a week’, knowing that it’s a week in the real world.
The code was obviously intended to work one way, but it doesn’t – but it’s not an obvious bug because the other way also makes perfect sense to anyone using it. Sometimes you actually *do* want something to be based on real time, and there’s no reason to assume that timed properties should force you to do the alternate. Nonetheless, it’s a bug.
I’ve left it, exactly as it is, because it’s a perfect example of a bug that will actually cause more problems from being fixed that it causes by it being broken. Code has been written on the assumption that it works one way, and fixing the bug will cause all of that code to break. So the bug goes unfixed, and it shouldn’t be fixed – really, what should happen is that the code for managing the game-time timing should be removed entirely because it’s not actually functioning – it’s not causing a problem, but it *looks* broken to anyone who might be browsing. In short, we should essentially commit to the idea that the ‘bugged’ version of the code is the ‘working’ version of the code.
There are many examples of this kind of thing as a project gets big enough – especially if there’s enough developer turnover so that the people looking at the ‘broken’ code are not the people who made the original design decisions. It is important to realise here that the original intention of a function is not a binding contract on everyone who follows – it really doesn’t matter what Creator X *intended* with System Y – it’s all about how it works in the here and now, and that’s a complex equation filled with moving parts. When a piece of code is functioning sensibly despite being bugged, it’s an own goal to ‘fix’ it just because wasn’t what was originally intended, especially if that fix also causes other problems elsewhere.
The collaborative creation of code is a fraught endeavour, and people are frail and fallible. It’s only to be expected that people will make mistakes when they code, and that those mistakes won’t be understood as mistakes by the people who follow (because the functionality is not obviously broken), and that people will look at the ‘broken’ code and feel an urge to ‘fix’ it. All of that is all but inevitable. upon ‘fixing’ such a bug though, if the end result is that you break a whole lot of other things, you also have to be prepared to stand back and say ‘The original intention of this code is no longer binding. We work with the world as it is, not how it ought to have been’. ‘It was a bug-fix’ is not a defensible reason for introducing unnecessary new flaws into a project, and it’s certainly not a valid reason to insist that everyone gets used to the new status quo.
This links back in to the idea of the game that exists being an informal contract between your developers and your players. While a change like this has the best of all possible intentions (fixing a defect), the resultant arbitrary shuffle of a player landscape is a cost too great to bear for honouring the original design decision. If it’s not obviously malfunctioning, if it’s not obvious unbalanced, and if fixing it is just going to cause other stuff to go haywire, then it’s not a bug that should be fixed. Keep it working like it was, but clean up the code so that the current version is ‘correct’ and not a side-effect of a buggy implementation.
I guess this entire post can be summarised with one statement. ‘Remember why you are fixing bugs’ – it’s to improve the game and to reduce the number of defects. It’s not to honour outdated philosophical positions or to enforce historical design purity.