I am thinking of writing a small booklet about better builds. this blog post might end up as a part of it. Let me know if you’re interested by taking a look here.
While I do love to use Continuous Integration Servers such as teamcity, Jenkins and more, I try to use them as much as possible as “dumb triggers” to my build actions. They do one thing and one thing only, and that is to trigger the correct build script with the correct resources (source version, artifacts form other builds, etc), and nothing more.
I don’t add any custom build actions such as compilation, source checking etc, because I believe that the build actions themselves have a very strong connection with the correct version of the source.
For example, at an earlier version of the source you might need to compile a specific set of projects, and in future source versions, maybe new projects were added, some were removed, or renamed etc. If you have your build scripts as part of your source control, and versioned along with the source, they will always match the current source version.
So what do those processes trigger? A script that is inside source control, tied directly to the current project`s source committed version. It might be an xml file (ugh), or a rake file, or a finalbuilder script file (my favorite).
Then it becomes easier to revert to an older build version to recreate it, or to test differences between versions, or to build multiple different branches of the same applications in parallel for different clients.
See if you can take your current CI build, and have it run on a version of the source from 6 months ago. How many changes will that require of you to make it work? If your build is tied to source version, things should feel much simpler.
Single Responsibility
To me it is also about the single responsibility principle. A Continuous Integration server should do one thing well – manage builds, their resources, and their scheduling. It should not also need to worry about what is inside each build. In uncle bob’s newspaper metaphor, the builds would be like different sub headings. Each does one thing, and the caller of those functions would only call them, without caring what they do.
Thinking about it the other way around, if you have your whole build script running inside one large set of teamcity custom actions, all managed in the browser, and then you refactored that script to separate script files that are part of the source code, you’d be closer to what I am trying to do.
So when I see companies boasting about how many custom tools their CI server can do, and how you can manage it all in the browser easily, I remind myself that the more I use those custom build features without the context of a current source version they are attached to, the more work I will have to do to change them every time I need to go back and forth between versions.
Version Aware Artifact Paths
Following that line of thinking, I might argue that version aware build script “cubes” in source control should also be internally-deciding on what artifacts the encompassing CI process that is running them will be exposed to, and allowed to share with other builds. Today such things are managed solely at the CI tool level, but it would be good if the tools enabled artifact paths to be tied to the current version of the source.