It's worth mentioning that the project uses a slightly modified implementation of Semantic Versioning. So 1.0.0 is a significant release: it indicates that the project is no longer in a beta status, rather it's considered stable, mature even. Any public API the project provides is considered frozen for the duration of 1.X.X release train. Any mistakes we have made (and I fully expect we'll discover plenty of them) in terms of interface design, we are stuck with for a while. This part is a little bit frightening.
Oh, I should specify that the project is Thermostat, an open source Java monitoring tool. Here's the release announcement from our announcement list archives. My last post (woah, have I not posted anything since February? Bad code monkey!!) also mentioned it.
Thermostat consists of a core platform including a plugin API, and ships with several useful plugins. Leading up to this release, our focus has been primarily on the core platform and API. Releasing 1.0.0 is somewhat exciting for us as we can move into primarily maintenance mode on the core, while building out new features as plugins. Writing brand new code instead of lots of tweaking and refactoring of existing code? Yes, please!
But what I really want to write about isn't the project itself, but the process and the things I learned along the way. So, in no particular order:
Estimation is hard
This project was started by two engineers about two and a half years ago. There was an early throwaway prototype, then a new prototype, which eventually became today's code base but looks nothing like it. Over time things started to look more and more reasonable, and we started thinking about when we'd be releasing a 1.0 version. I want to say that probably for more than a year, we've been saying "1.0 is around the corner". And each time we said it, we believed it. But we were, until recently, obviously wrong. Now there are various reasons for this, some better than others. In that time, there were new requirements identified that we decided we couldn't release 1.0 without implementing. Naturally, estimates must be revised when new information appears. But a lot of it is simply believing that some things would take significantly shorter than it actually did. I want to think that this is something that improves with experience, and will be mindful of this as we move into building out new features and/or when I'm one day working on a new project.
Early code and ideas will change
When I think back to the early days of this project, before it even had a name, it's hard to imagine. This is because it is so incredibly different from where we ended up. Some parts of our design were pretty much turned inside out and backwards. Entire subsystems have been rewritten multiple times. We've used and abandoned multiple build systems. And this trend doesn't seem to be slowing down; we've had ideas brewing for months about changes targeting the 2.X release train that will change the picture of Thermostat in significant ways again. One really awesome result of this is that nobody working on the project can afford to indulge their ego; any code is a rewrite candidate if there is a good reason for it, no matter who wrote it originally or how elegantly. And everyone understands this. Nobody gets attached to one implementation, one design. It's nice to be working in a meritocratic environment. It's a sort of freedom: freedom from attachments, and freedom to innovate.
Good test coverage helps make changes safe
So this one is something that's probably been noted by a lot of developers. I know I've been taught this in school, read it in various places, and so forth. But it is working on Thermostat that has really driven it home for me. In the early days, we didn't really have any tests. It made sense at the time; we didn't really know where we were going, the code base was small and undergoing radical changes very regularly. But time went on, and it became clear that this project was going to be around for a while, and both the code base and the group of contributors were growing. So, we started adding tests. Lots and lots of tests. No new code was accepted without tests, and over time we filled in gaps in coverage for pre-existing code. The happy result has been an ability to make very invasive changes with the confidence that side effects will be minimal, and likely detected at test time. I cannot exaggerate the number of times I've been thankful we put in the effort to get our unit and integration tests to this level.
Automation is king
Have a repetitive, error-prone task? Script that. Over time Thermostat has grown a collection of useful little helper scripts that save contributors time and effort, over and over again. From firing up typical debug deployments, to release management tasks, to finding source files with missing license headers; we write this stuff once and use it forever. These type of things go into version control of course, so that all developers can benefit from them. Also, testing automation. The common term used is of course Continuous Integration Testing, and for ages we've been using a Jenkins instance to run tests in sort of a clean room environment, catching problems that may have been hidden by something in a developer's environment. This has saved us a lot of pain, letting us know about issues within hours of a commit, rather than discovering them by accident days, weeks, or months later and having to wonder what caused the regression. I'll have to insist on a similar set up for any non-trivial project I work on.
That's all I have to say. Hopefully it won't be so long before my next post. I've actually been meaning to make a "battle station" write-up; I'm a remote employee, and invested time and money in a convertible standing desk setup and some clever mounting techniques to keep my workspace neat despite the number of devices involved. Until then, Adieu!