Tuesday, December 13, 2011

Introducing Artifacts (part 3)

Slowly, we're getting our pieces together. In Cherrypicking Made Easy, I explained how there really is only one code branch Release Management should care about:  the production state.

The only way the production state (main) branch can get updated is copying in code, after the artifacts built from that code have been successfully released into production.

The next step down the food chain is to consider artifacts built from code that is one copy merge away from the code in the production branch. Specifically, this means that the head of the main branch is a direct ancestor of the head of the code branch from which the artifact is built. I'll call artifacts built under these conditions release candidate artifacts or releasable artifacts.

Most other artifacts will be non-releasable, and I'll call those development artifacts.

Whenever a new version of an artifact is built, any preceding builds from the same branch need to be demoted to development artifact status.

In part 2 of this series, I explained how artifacts not only depend on the source code, but can also depend on other artifacts. Usually, you will represent your whole product as an artifact that depends on many other artifacts, and therefore the state of the dependencies will influence the state of the artifact being built in obvious ways.
  • A released artifact may only have released artifacts as dependencies.
  • A release candidate artifact may only have either released or release candidate artifacts as dependencies.
Let's examine a simple application, which consists of three artifacts. This is actually the most common pattern:
  • An application artifact, which has no payload, only dependencies to include all the artifacts required for the application to run;
  • The code artifact, which contains the configuration independent portion of the application ("foo" in the picture)
  • A configuration artifact, which contains the specific configuration files required to run the app ("bar" in the picture).
For example, a "web server" can be considered a combo of the apache binaries, packed up as the code artifact, and the apache configuration files packed up as the configuration artifact. The idea is that different web apps may all share the same binaries for apache, but use different configurations.

Let's assume we're working on two different projects, each modifying foo. Each project constructs a release candidate:

Both release candidates are presented to QA and product management, and let's assume the "right" artifact is selected for release. We promote all the artifacts contributing to the release candidate to the "Released" state, and perform the copy merge in their respective source trees:

Now that there is a new revision on the main branch, the left version of foo no longer qualifies as a release candidate, and therefore we demote the artifact to development status. Note how this demotion needs to be propagated through the dependency chain to ensure that all artifacts depending on the left version of foo are also demoted:

At this point, we have the skeleton of a good release process:
  • We build complex applications by assembling them from simpler pieces via artifacts, each built from a combination of source code and other artifacts;
  • We tie in the merge status of the code branch via the artifact state;
  • We define how the artifact state propagates through the artifact dependency graph;
  • We define how the act of releasing an artifact propagates copy merges through all the source trees of all the dependent artifacts, thereby ensuring that the main branch of every source tree represents the released code of the artifact built there.
From here on, it's mainly decoration. Good decoration is crucial, though, since it is the only way for people to actually understand the process and make sensible decisions about what a release candidate contains and which one to release. 

No comments:

Post a Comment