The original BbA article had a non-functional hypothetical case: changing Hibernate to iBatis. What about a functional change that was achieved with Branch by Abstraction? One that took a long time to complete over many commits. One that you might be tempted to do a branch, and have a lot of developers involved with towards completion before merging back…

Concurrent Development of Consecutive Releases

This client had a lot of change to implement to the same stack and an aggressive rollout schedule that was driven by the marketing department. ThoughtWorks had a lot of developers involved to effect this, in multiple project teams all committing to the same trunk. Agile, client-devs, trunk based development all figured of course. I’ll skew some inconsequential facts to protect the client …. this was about shopping online for “items”.

Concurrently the main parts of the application were receiving functional changes. The backends were being “lifted and shifted” (non functional), there was a slow strangulation of a much older technology stack, and all sorts of highly coordinated fun was going on. Every six weeks or so a release would go into production. Sometimes changes were visible, sometimes not.

Replacing old cart with new cart

This was to be a permanent change, but it was going to take some time. The prior choice “Interim Cart” was very much a Minimal Viable Product solution, and the new one (Quick Cart) was the bells and whistles thing the client really wanted.

In the server-side page template (JSP), there was a mechanism to include the QuickCart or the InterimCart:

<foo:toggle test="QuickCart">
<div class="rightfloat">...</div>
</foo:toggle>
<foo:toggle test="InterimCart">
<div class="rightfloat">...</div>
</foo:toggle>

They could have done this with an if/else, but that would not have been as clean as having an implicit conditional inclusion via a custom JSP taglet. It proved easier to remove the old implementation first, then the abstraction later. It could also cater for one more scenario - deploying with no cart whatsoever. That might seem silly but deploying without it could prove advantageous for functional testing in some CI pipelines. Taking of pipelines, you’ll have one for each permutation of toggles and build flags your might be putting live in the future.

Dropping the upsell sections for various reasons

At run time, the support engineers can flip some toggles if they need to. Hopefully they have practiced that toggle flip in UAT before, and there are formal guidelines as to when to do it. Maybe the UpSell rectangle leverages services from a third party that might be slow or error prone at times. Maybe the production stack is under attack (or load) and to give real purchases a chance, toggling off upsell is what support want to do. They were also pre-authorized to flip those toggles.

<foo:toggle test="UpSellOn">
<div class="rightfloat">...</div>
</foo:toggle>

Of course, both this rectangle and the cart one have CSS that caters for the presence or absence of both.

Dependency Injection plays a part

You want to avoid if/else/end throughout the code when using feature toggles. That’s true in page template technologies, and true in your 3GL language. If you can’t avoid it, you want to minimize it to one, and have that somewhere nearer to the class that boots the app. The place where the abstraction’s components are assembled and configured. Of course we have dependency injection to help here, whether we use a container or not.

For the old vs new Cart case: An interface specifies the things that a Cart needs to implement.

public interface Cart {
    void purchase(Item item, int qty);
    // etc
}
public class InterimCart implements Cart {
    public void purchase(Item item, int qty) {
        remotePerlCart.doPurchase(....);
    }
    // etc
}
public class QuickCart implements Cart {
    public void purchase(Item item, int qty) {
        // whatever is new
    }
    // etc
}

Remember, Branch by Abstraction means you start by interface/impl separating the InterimCart (old) and commit that. Then you take your time, and multiple commits to finish the other thing you’re trying to swap in. Sometimes you’re going to have to refine the abstraction a little after it’s initial creation. It’s all good - your only prerogative is to not break the build, or jeopardize the ability of the trunk to be tagged or branched to go-live. Maybe you should take the opportunity to add unit tests to the new one too, given that type of “better” pays for itself.

For the UpSell case, we had an abstraction too, but with a NullObject pattern implementation of that as the alternate that could be toggle-flipped while hot.

Incidentally, there is a small snafu for JSP in particular in that a tag (as shown in the template above) can’t have something injected into it as it will be instantiated completely outside the control of the Dependency Injection container. You would want to inject in a means to access the toggle state.

Trunk (off topic a little)

Last week I spoke at the Merge conference in San Francisco. As it was closing out Frank Compagner (Lead Tools Programmer for Guerrilla Games a Sony Entertainment subsidiary), said something very memorable in his presentation:

“Branches create distance between developers and we don’t want that”

I think is a great soundbite given all the love for CD these days.



blog comments powered by Disqus

Published

September 24th, 2014
Reads:

Categories