Kabukibot2: Good bye, node.js
When I started working on Kabukibot in early 2014, node.js was an easy choice to make:
- It supported Windows and Linux.
- I did not require high performance from my programming environment.
And to be honest, most things worked out as expected. The resulting code was readable, well structured and after a year of production use, relatively battle-tested.
There was just one thing that bugged me about my bot, which would mostly idle in ~40 IRC channels:
That’s right: For basically doing nothing, it would eat between 100 and 200 MiB memory. Annoying, but okay, I have enough spare memory on the machine it runs on, but still.
This motivated me to port the entire source base from node.js to something saner. Go and Rust were both trending and Go seemed much easier to learn than Rust, so I went with Go. So early 2015, I started the port.
Development went fine – well, as fine as possible, considering Kabukibot was once again my excuse to learn a new programming language. After a few months though, work stopped as I cought interest in other (Go-) projects.
The Great Crash
Then came Twitch’s deprecation of
TWITCHCLIENT stuff. Previously, IRC clients could issue a
TWITCHCLIENT N command and signal to the Twitch server that it understood the special commands
Twitch would send for subscriber statusses and timeouts and such.
Instead, Twitch is now using IRCv3 Tags, which makes for a much nicer protocol. But unfortunately, this also completely broke Kabukibot. Nothing worked anymore. Game over.
On top of that, the Go package I chose for the rewrite could not handle IRCv3 tags, which was demotivating enough to let the project sleep a few more weeks.
For whatever reason, I picked the project up in early November 2015, with a much, much smaller IRC package backing it and a new architecture. In node.js everything was done – because it’s JS – in one thread, but in Go it would be cheap and relatively easy to go (pun intended) multithreaded. I didn’t have any real experience in concurrency yet, but it seemed like the perfect opportunity to fix that.
To have a consistent view of a channel, Kabukibot2 would not use one goroutine per message, but rather one per channel. Plus a bunch of other routines that keep things up to date in the background.
Surprisingly, the Go code for most plugins is roughly the same length as the original node.js source. I was expecting much more type juggling, but I was wrong, apparently. :)
On December 12, 2015 Kabukibot was killed and Kabukibot2 took over its place. Most features work identical to the users, a few were improved and some were dropped (most prominently, the ambitous but seriously flawed hangman game).
To my great satisfaction, under the same workload with basically the same feature set, things improved on all fronts. I don’t even need to point out where I flipped the switch from node.js to Go.
Go turned out to be a good fit for the new bot. As expected, handling JSON/YAML is a bit akward, same goes for doing SQL queries, but I got rewarded with easy concurrency, type-safety and easy deployments. This has left me confident enough that I don’t see the need for node.js for me anymore.
This is not a rant about “Ugh, node.js sucks, it just eats memory!”. At least it wasn’t intended to be. This unpredictable blackbox VM is the reason node.js offers a ton of convenience features and some things that took 20 lines in Go would take one in node.js. Correctly applied, node.js can do good things.