Tuesday, November 9, 2010

Maven and Continuous Delivery

There has been an interesting discussion going on recently in the Maven user group list about Continuous Delivery:

http://maven.40175.n5.nabble.com/Continuous-Delivery-and-Maven-td3245370.html


My colleague here at ThoughtWorks, Jez Humble, contends that the SNAPSHOT model that Maven uses is prohibitive to many of the principles espoused in his book. And I have to say, I largely agree with him.

Before diving into why I agree with him, it's worthwhile to add a bit of context. I'm one of a minority of people in ThoughtWorks that prefer Maven to Ant. That's not to say that we don't deliver projects using Maven, but if given a choice, most ThoughtWorkers prefer Ant. Personally, I have had a long history of using Maven, starting with my work with Spring Batch. That's not to say that Maven doesn't have it's problems, but if given a choice, I'll start a project with Maven. (Why that's the case would certainly be worthy of another blog post) I mention all of this because I am not bringing up issues with the release model of Maven to bash it because I hate the tool. I bring it up because I think there can be improvements in how Maven thinks about and deals with releases.

I remember the first time I had to cut a release with Maven oh so many years ago. I had my project working, everything was in exactly the state I wanted it to be into for release. All I needed to do was create the final cut. Like a good Maven user, I went to check out the documentation for how Maven approached releases. I quickly found the section on the release plugin and thought 'Aha, I just need to use this plugin, and my release will be painless, thanks Maven!' Of course, reality is never that simple. The maven release project has two 'phases' prepare, and then the actual release. Below is a list of what Maven does during the prepare phase, shamelessly pulled from the official plugin site:
  • Check that there are no uncommitted changes in the sources
  • Check that there are no SNAPSHOT dependencies
  • Change the version in the POMs from x-SNAPSHOT to a new version (you will be prompted for the versions to use)
  • Transform the SCM information in the POM to include the final destination of the tag
  • Run the project tests against the modified POMs to confirm everything is in working order
  • Commit the modified POMs
  • Tag the code in the SCM with a version name (this will be prompted for)
  • Bump the version in the POMs to a new value y-SNAPSHOT (these values will also be prompted for)
  • Commit the modified POMs
The SCM related bits make sense. Of course you need to create a tag that coincides with the release. However, the majority of the remaining bullets are around modifications to the POM files themselves, and checking that other POM files aren't in a 'SNAPSHOT' state.

I didn't understand that process years ago, and I still don't understand it today. I have my codebase, I've tested it thoroughly, both with unit, integration, automated, and manually testing. I just want to take the 'binary' that I've been using, and promote it to production while also taging the SCM revision that created it. Why does a snapshot state even matter? I suppose with external dependencies there are some concerns about APIs changing underneath you. However, what does it matter, you have working code, right now, surely the changing API is a problem for tomorrow, not the release of today?

In my time at ThoughtWorks, and other consulting experience, I have always prognosticated that the same build that a developer makes on his machine, and ultimately what will come out of CI (because he or she commits that revision) should look as identical as possible to what will be in production. Why add an extra step to change anything at all?

In short, I agree with Jez, why can't every artifact created from mvn deploy be potentially a release into production? The only value I've seen the snapshot concept add is away to describe a dependency as 'whatever the latest CI has produced is'. It seems to me its just as easy to say: 'any release greater than x.y' and call it a day, which is something Maven supports now as well, at least from a dependency declaration perspective.

8 comments:

Jason van Zyl said...

Unfortunately, but naturally, users tend to think that the release plugin embodies all best practices for Maven with respect to releases, but in practice many people accept it's flaws -- for which there are many -- or remake their own tools.

I believe the separation of snapshot and release repositories are required but the extraction of a release should happen as the process of extracting that release from a stream of potential releases. Given you could assert all conditions of the build you should be able to tag the code, and pluck the snapshot out and promote it to a release repository.

I don't think you would have any disagreements with you from a release management perspective. Someone just needs to do the two weeks of work to add the tooling.

Michael Hüttermann said...

in my opinion the best approach is: people over processes over tools.

Thus Maven's release plugin can be the best choice in the case your process is exactly what the plugin offers. In other cases, you need to set up an infrastructure that maps to your release process. I agree with Jason, this often is part of the activities of (technical) release management.

I like Maven and its concepts. That said, the process of chosing the right tool is aligned with concrete requirements. As a result of this Ant can be the best choice in a specific situation, in other cases it is Maven or something completely different.

Curt Yanko said...

I have never cared for the release plugin either, didn't make sense to me. Still the idea of transmorgafying your SNAPSHOTS into a *release* is fine if you your not worried about reproducibility. The amount of effort to compensate for that is on the wrong side of an 80/20 equation imo. Sadly, far too many Maven users devolve into using SNAPSHOTS for the wrong reasons, namely laziness. The byproduct of years of neglect for the build process in general as a second class, low value, activity not worthy a a developers attention or time.

Ant clearly lends itself to CD because it doen't try to manage the build dependencies, a simple *.jar in the 'ol lib folder will do, thank you very much. And why not? It's all in there right? ...managed by good 'ol SCC tools. But Maven wants you to be more prescriptive about those yet out of old habits many see SNAPSHOTS as the mechanism to keep doing things the old,l easy, way.

In Maven, when you have *value* you need to release it (pin it down so-to-speak). Myself and others in that thread did flip the problem around and re-approached it with Dependency ranges in mind which imo provides a much clearer path forward to craft a plugin that can meet the needs of CD. Dependency ranges provide the ease-of-use of SNAPSHOTs but resolve to real, released, versions at build time which can be captured and codified.

I'm sure I have a lot to learn about CD but I'm also sure the Maven community will get there.

Christopher said...

I must say that I followed this thread on the Maven Users list and got a little lost with it. I don't really understand where Jez is coming from.

From my own perspective, I've never had an issue with the release plugin. I like how it works; particularly when you have many developers all working on the same thing and you want to take something out of the CI stream at a particular rev and then release it.

I wonder if this debate is an academic one, or whether there really was some issue Jez came across to fuel the debate. If it is the latter then it'd be great to hear about what happened in reality.

Maybe Thoughtworks should develop a new release plugin also given that I don't see Maven itself being the issue.

dantheperson said...

However, what does it matter, you have working code, right now, surely the changing API is a problem for tomorrow, not the release of today?

Snapshots changing beneath you is a problem for tomorrow, but it's a problem you need to solve today.

So you've released 1.1.1 and a couple days later a critical bug is discovered and you need to do an emergency intra-day release.

Work on trunk has continued on features that are user visible and you don't want to release them till the next regular release after the end users have been notified of the changes.

No problem, just create a branch from your 1.1.1 release tag, fix the bug, and build 1.1.2 right?

Not if you have snapshot dependencies, because when you checkout the source and build, it pulls down the latest snapshot dependencies which are going to be newer than the one you pulled in last week, which might not compile, or even worse will have subtle bugs that only show up during the exhaustive testing that you don't have time to give to this emergency patch release.

So as your builds aren't repeatable with snapshot dependencies, the path you have to take is to start with your last binary distribution, and patch in just the jar you want. And once you've done that you're distributing binary images that you can't rebuild from source. Good luck supporting that.

Danijel Arsenovski said...

@dantheperson
Exactly.

Finally, you can always NOT use snapshots. If the team works on the same artifact, no need to use snapshot mechanism to update the binaries anyway and the current project will be updated through code repository. Dependencies can be updates through mvn versions:display-dependency-updates.

madbande said...

It seems, tertiary education students will soon be able to complete not only the simplest tasks but go through gradually planned research on the given topic for their universities. I believe it because term papers online at papersmart.net don't lack experience in writing short pieces for my class-mates and college friends. This service does its job the same way our goals are set and can refer to the act of reflection of an educated person

Paul Rule said...

John Smart has a great presentation on CD with maven:

http://www.slideshare.net/wakaleo/continuous-deliverywithmaven

If I understand it correctly:

1. Build system branches /module/trunk when a commit is detected
2. Set the version of the module to be ..
3. Set the snapshot dependencies of the module to be latest release
4. Maven build, test, verify…
5. If successful deploy artefact to artifactory so it is available to other builds – presumably the branch then gets tagged (not sure about this)

Changing the versions (from snapshot) in the pom are done using the maven versions plugin.

To change the version of your module:
http://mojo.codehaus.org/versions-maven-plugin/set-mojo.html

If your module depends on a snapshot of one of your other modules, already built and released by CI update those dependencies to releases:
http://mojo.codehaus.org/versions-maven-plugin/use-latest-versions-mojo.html

It sounds simple and effective, I can't wait to try it out on my pet projects.