Dude, where is my build system?
TL;DR: There are many great build systems out there. However, some of them had at least a few quirks, that I always thought could be solved in a better way. Others couldn’t really convince me right from the start. This article is a tale of my journey with build systems, and why I ended up building just another one.
When I was young, my parents often told me not to be so lazy about the housekeeping. Repetitive tasks are just boring, right? When I started working in software development, I realized that being lazy is not such a bad attitude after all. In fact, it can activate a great desire of optimizing and automating as much as you can :) Of course, there is always this question, if it’s really worth the time, but this never hold me back. Let’s get on-topic and talk about build systems!
At my first developer job, which was around 5 years ago, they’ve used MSBuild for build automation. It is an XML-based one. Most C# developers surely know how a project file (csproj) looks like. Well, this is the most easy way to use MSBuild as a build system. But with rising complexity, you would have to dig deeper into the MSBuild task reference or even the MSBuild extension pack. To the latter, I even contributed a task myself. Unfortunately, providing such tasks won’t reduce the verbosity that you inherently have to deal with. For your entertainment, here is a random MSBuild code snippet:
After some time, we started with a new project. Since everyone on the team knew about the pain we had with MSBuild, pretty much any other choice seemed to be a good choice. There was quite a supply of build systems on the market, so let’s examine.
RAKE and other products outside the .NET ecosystem often disqualified due to their system requirements. They required the installation of some sort of a runtime and therefore had a rather heavy ceremony.
During a small conference we had the chance to see a talk about FAKE by the author himself. Overall the underlying DSL looked pretty concise and neat. But in the team we still feared that it might be not mature enough. Maybe a wrong assumption, but I will get to FAKE later.
Eventually, we picked PowerShell — not PSake in particular. We picked it mostly because some guys in our infrastructure department were already using it and it was closer to what we’re used to do in C#. No fancy stuff to learn, except a few subtle difference in the syntax. That’s what we thought. We’ve later found out, that there were a lot unknown unknowns, which caused a lot of effort and headache. For instance, default values for string parameters or automatic collection unrolling.
After changing my job — not because of the PowerShell scripts — I no longer had to deal with any build scripts. Except! … for my own open-source projects. Meanwhile I also used PowerShell for them, but I was playing with the idea of moving to FAKE for a long time. Truth is, I always postponed and never made this step. There was this pressure of having to learn F# first — a steep learning curve in my mind’s eye. I expected to need some serious F# skills as soon as my build would become more complex. Also, a second language always implies a mental mind-switching. However, I wanted to get things done and not waste time with the build script. And not to forget, this particular choice would always affect potential contributors.
One day, I discovered CAKE and I was super excited. It looked promising, because it was almost pure C# and had a very active community. I was using it for some time, updated my project, and also told my old team to try this gem in build automation. However, when my build became more complex, I realized that this is still not what I was looking for. Every now and then, my script didn’t compile due to syntax errors — or better, due to missing syntax highlighting. There was no auto-completion, which forced me to make a lot of documentation lookups. Meanwhile this is possible via an extension, but only in VSCode. The documentation is very good indeed, however, as a C# developer I’m used to have it at my fingertips — namely IntelliSense. Overall, CAKE improved the status quo quite well, but for me it still ended up in a scripting experience with too much complexity. A simple CAKE script:
There are two more issues, which apply to all the build systems I described. Inherently, they are missing refactoring safety. Build steps are defined as simple string literals, which are later used to define dependencies. Renaming one build step, requires to manually adapt other occurrences of it. Otherwise you’ll see an error when executing. The second issue, is the manual API implementation of additional command-line tools. I’ve been in the situation of trying to contribute. Unfortunately, I quited doing that, because I was very bored writing all that documentation, tests and other details.
Now for those people, who share at least a little of my experience, I can suggest another option. This option is meant to solve many of the issues I’ve just mentioned and should just make you really really lazy :) However, if it actually does, is left for everyone’s own judgement.