Why are we still 'releasing' software?

Tools available for hacking on free software projects have improved by leaps and bounds since the Bad Old Days of just a few years ago. However, there is still one process that seems to stink of aging. I'm of course, talking about the archaic process of packaging software for a release.

In practice, I find virtually every stage of maintaining my software smooth and reasonably well supported by the tools I use. I use good revision control processes and even better revision control systems. Github has in a short period of time done wonders for the projects I've migrated over to it, making it easier than ever for developers and contributors to watch changes as they happen and get their patches noticed, reviewed and applied easily. Even without github, a simple SVN/Trac setup did me well enough where I never felt significant pain while developing.

When nearing a stabilizing point with my mature software projects, I often will create a stable branch that indicates two things. The first is that the external API for the branched code will not change, so people can expect that code along any given stable branch is going to be backwards and forwards compatible. The second promise is that no code will be checked in that knowingly breaks something or creates instability that causes our tests to fail. Put another way, every patch that gets applied to a stable branch is in the interest of making that code more stable and solid. Usually, a stable branch means it's about time for a new stable release.

Why do we need releases again? Developers are usually running against the bleeding edge, and many contributors ride that wave as well, and power users will frequently at least build against the stable branches. The primary purpose of releases (not necessarily for everyone, but the reason why I do it) is to ensure that my software passes the 30 second test. This is essentially the idea that, if I've I'm a new user about to try a ruby reporting library called Ruport, I want to be able to do something like this:

gem install ruport

Once I've got the code installed, I want to promptly copy and paste some tutorial code that is interesting to me into my editor, run it, and see if it works. If it does, I'll look deeper, if not, I'll immediately hop back onto google and try to find something else that works. The only chance of me coming back to this code later is if I don't find something that passes this test, or if I find out later that I really need some of its features.

I tend to try really hard to make sure users can have this experience with my code, because I know this is how some people pick their tools and libraries. Package managers aren't a bad thing, so the idea of releases themselves are not the problem.

The problem I see are that we've been building tools to map the way we organize our code in revision control systems, or on our local filesystem, to the way we package and distribute code. Why can't we invert this relationship?

In Ruby, you could use Hoe to simplify the deployment of your code to RubyForge. You could also take advantage of the fact that Github auto-builds RubyGems for you. But this only makes the problem less of a hassle, it doesn't get rid of it entirely.

What I'm suggesting is that in order to really make 'releases' match up with the way we develop our software, it might require our package managers to become revision control system aware, not the other way around. To me, it feels like branches and tags are the 'real' natural way to work with code, and that version numbers are just a convention left over from when we needed to read a static file of release notes to figure out what is going on in new versions of packages we use.

Here are some examples of things I'd love to be able to do:
I'd like to be able to just specify a repository location directly to my package manager, (optionally identified by a label)

gem source rinara git://github.com/ruport/ruport.git

When I install the package, I'd like to be able to specify a branch of the code

gem install rinara:ruport -b stable-1.6

This would then pull down the very latest code in the stable-1.6 branch, since it's likely to be 'more' stable than an officially packaged release.

If I leave out the label, and the package is ambiguous, I want it to tell me:

I want the following to grab me the very latest code (basically the bleeding edge):

require "ruport"

But I want to be able to specify a branch, too:

gem "ruport", :branch => "stable-1.6"
require "ruport"

I want to be able to do the same with tags:

gem "ruport", :tag => "teh_pretty_kittten"

And if the project follows a standard convention of tagging release numbers, I want to be able to just use that:

gem "ruport", "1.0.0"

I'm sure I can think of more things, but I'll stop here in hopes that people see where I'm going with this. Even though I'm talking in the context of Ruby, I'm really thinking about all package management. I feel like release maintenance is a headache that most of us avoid by hacking together deployment scripts to work around our outdated systems rather than working on putting together systems that work as we want them to.

I think it'd be a huge step forward to see tight integration between popular revision control systems and popular package managers. I haven't done much research into this to see if efforts have been made, but then again, I haven't seen any blindingly obvious solutions to the problem that have been generally suggested just yet, either.

What do you think? Do we need to update our notion of 'released packages' to match our increasingly sleek revision control tools, or am I just whining about a problem that already has been solved in a 'good enough' fashion?

I'd especially be curious to hear about projects that address the problem, or people's general thoughts on what's needed to bridge the gap between 'released code' and 'code under development'

Let me know what you think.

Tags:

You might also be interested in:

12 Comments

Excellent thoughts on this topic and covers many of the pain points I'm running into when defining my deployment strategies. the only thing missing is how to apply that to websites, which often are a hodgepodge of content, applications, frameworks and libraries - but that may another topic all together.

"Developers are usually running against the bleeding edge" - I'm not sure this is true. Certainly if you're writing software that uses code from various sources - Rails, Solr, gems, libraries - trying to ride the "edge wave" for everything in your stack is going to result in major compatibility headaches, surely?

Thanks for the kind words. Good point about websites, I never quite figured out the best way to deal with that....

@James Adam,

Good point. I should have been more specific, I was mostly talking about developers on a given project. For example, I only run stable releases of Ruport and its dependencies when I'm working on old code, for new stuff I'm always using the bleeding edge.

I also tend to pull down the edge versions of any software I'm building a large application in, such as Rails. This way, by the time my app is ready, the edge version I've been following will be stable and I can lock to it.

But using edge for every single library would be a nightmare, for sure.

Hi Eric. This looks to me like an interesting new perspective on the subject!

Package Manager versus Revision Control System is a Pull versus Push proposition in a way. I guess that both make sense.

It should not be a big deal for a Package Manager to command the RCS a Push operation on a release that the user wants to Pull out of it. Kind of Just In Time release versus built Ahead release.

Regarding stable vs unstable releases, I believe that the ultimate is something sometimes called "Continuous Integration".

With Continuous Integration there is no notion of stable versus unstable versions. All versions are stable. That is so because a modification that creates an "instability" is rejected. This obviously requires some "test" to check if a modification is acceptable. TDD and BDD styles of development go in that direction nowadays.

Regarding continuous integration, though I don't use formal tools to check the build on every new commit, we've always had the policy of running our test suites and making sure nothing is broken before committing to a main branch.

However, this doesn't imply stability. Most frequently, bugs come out of testing holes or incomplete tests. API changes also indicate 'instability' and CI does nothing to address that.

So there still is a stabilization process, and inevitable forking to create 'stable branches' in any reasonably complex codebase. CI helps tighten the feedback loop, but it does not, in and of itself, get rid of the need for 'cooling off the code' at certain checkpoints in time.

The real issue here is most of my projects are fairly actively developed, so it's not like we reach 1.0 and we think 'oh, it's done except for the maintenance', so freezing the main branch is usually not an option, creating the stable / devel split. That having been said the bleeding edge on most of my free software projects is stable in the sense that it runs and works as advertised by the tests/specs.

If you're in a production environment where you do phased rollouts, you need a simple way of replicating your build even if your release staging area gets wiped. Maybe this can be solved by more liberal use of tags with branches, but if I were putting something into a production system, I would want to know the precise patch number from a branch that my release is building against, both for the main project and any libraries it's depending on.

How would you propose that git patch hashes be used with this? :hash => value ?

"Package Manager versus Revision Control System is a Pull versus Push proposition in a way. I guess that both make sense.

It should not be a big deal for a Package Manager to command the RCS a Push operation on a release that the user wants to Pull out of it. Kind of Just In Time release versus built Ahead release."-----> I agree with you JeanHuguesRobert