They say that you should start as you mean to go on. In code, that might mean planning, neat and readable code, good comments. I’m not going to do that.
I plan to write about my tortuous journey towards writing better code. It’s a hobby, at most – my job is in technical documentation, so I work with a lot of HTML, CSS and JS. I write some code for doing data processing, but that’s a pretty tiny part of my time.
My coding is mostly in Python, and most of my projects are creating bots for an online chat client. That sounds like it would be a limited range of interest, but it offers a nice scope for creativity. For instance, a recent project I undertook was to refactor some code called firebot. Its sole purpose is to use its nick to show how active the room is.
At the moment, it has seven levels, each of which corresponds to a different nick. It changes that nick based on a variable which incremented when a human (i.e. not another bot) posts a message, and multiplied by 0.9 every 5 seconds. I wanted to change it so that the nick calculations took into account the number of people connected, and used reverse exponential drop-off (so that if going from level x to level x-1 requires the activity variable drop by n, going from x-1 to x-2 requires a drop of n². That should be easy, right?
So, I sat down with a plan:
- every five seconds, recalculate
- also every five seconds, multiply
0.9 < activity < 1.1, set the nick to a neutral emoji. Otherwise, if
activity < 0.9, set the emoji for the nick to a snowflake, else set it to a flame.
- Count how many times activity must be rooted to satisfy
0.9 < activity < 1.1, ‘soften’ (i.e. divide by a number to make the nickname a reasonable length) and then set the nick to that many of the previously determined emoji.
At no point did I say that it was a good plan. Anyway, I didn’t know that it was bad then, so I implemented it, pushed it to testing, and… tested it.
It was bad code. Very bad code. To paraphrase:
In fact, if you’ve got a moment, it’s twelve-exceptions-all-at-once code with a magnificently buggy algorithm, unidiomatic code throughout, two dozen badly-named variables, and an enormous comment at the top, saying ‘This Is Bad Code’.
The big problem was that the bot kept vanishing from the nicklist. I thought that that was probably because if you multiply a variable (in this case
variableForTimeDropoff) by 0.99 enough, eventually you’re going to overflow it, and if you don’t account for that (spoiler: I didn’t) you’ll crash your code. Then again, I tried half-arsing a fix by wrapping the multiplication in a rounding operation (which should stop it overflowing, though it also limits how small the variable can become), and it kept vanishing, so I figured that was either:
- not the bug causing the vanishing (though still a bug)
- not an effective fix
- one of multiple bugs causing the vanishing
Then I realised that the bot wasn’t actually crashing. Or, my Python code wasn’t, rather. Which meant something interesting: the bot had set its nickname to ” – an empty string. Of course, the code did that, just before it changed its nick:
- set nickname to ”
- append emojis to the nickname
- send the nickname to the server
It did step 2 like this:
while activity < 0.9 or activity > 1.1:
activity = math.sqrt(activity)
nickname += emoji
Obviously, there was something wrong with the conditional tree I’d written, but I couldn’t see what it was. It was really annoying, too, because I was having one of those coding days where I was struggling to hold everything in my head, and I was fast running out of time to fix the code. So, grumpily, I reverted my code.
Except, I didn’t, because I hadn’t backed up the original source. And I had to leave.
Fortunately, I found the firebot code, v1.0, on a backup I’d taken a while back, for unrelated purposes. So I will resurrect it! Unfortunately (and here, dear reader, is something you will grow tired of reading) my computer is currently borked, and I think it’s probably my fault. Here’s the start of a joke, write in with your ideas for a punchline: “What do you get when you cross a slightly-experienced Linux user and a workstation upon which they’re installing Arch?” But that’s a topic for next time.
I learned a few things writing this code. But mostly, I learned that if I’m not willing to exactly recreate my code from scratch, it should be under version control.