On a past project, I wrote new functionality for an internal call center agent application that required repurposing old functionality and adding some new tools to support a new way of working. This post details how an all-or-nothing approach to development is not the best plan and how our team now implements small-batch deployments.
An all-or-nothing approach to development
We started with a huge requirements doc that outlined every detail of every feature. Because we had extensive experience with the application we were modifying, we knew the process we wanted to optimize. We worked on the project for six months and added every feature they asked for. We even added in some things they didn't ask for, but we knew they'd want.
The testing team hammered at it and poked holes in it for a solid month and finally signed off. We did our install during off hours and released at 6:00 a.m.
An hour after installation, we noticed that the software was running a little slow and there were some oddities that weren’t present in our test environment. We switched everything back to the old version so we could debug.
The problem stemmed from changes we made to our API that affected older code. While we were able to fix the problem—and more bugs—the group who kicked off the project decided to abandon the project and our changes.
So, what went wrong?
The truth is that we started failing during the design phase because we never took the product back to the users to find out what they thought of it. Instead, our “all or nothing” approach caused us to spend six months developing a feature that wasn’t ever used.
Software development is experimental. To survive, you have to do small experiments quickly enough that the future doesn't have time to change too dramatically.
Small batch fixes find problems faster
Working in small batches is the key to finding and fixing problems quickly and efficiently. When you release in small batches, you ensure that you’re working on the current problem. Specific benefits of working in shorter, smaller release cycles include:
- easier code reviews because the code base for review is smaller
- faster debugging
- reduced cycle time
- quick feedback from users and testers allows you to pivot away from dead-ends and evolve along with user expectations.
It's obvious to most technologists that this development process wasn’t using Agile but was using the old waterfall method. That’s true! But don't make the mistake of thinking that just because you are working with small stories in small iterations that automatically means you're immune to similar pain. Let's look at how you can write better feature stories to get better results.
Feature and story creation
You need to clearly define the feature that you’re creating and make some important decisions before starting. The first thing to decide: Are you crafting a minimum viable product (MVP) or are you bloating it with things that would be better to add later?
Are your stories small enough that they can be completed quickly and independently? Stories should follow the Agile concept of INVEST:
- Independent: This bit of work should stand alone and not be related to other bits. You should be able to work on any of the bits in any order. While not every story can be independent, you should look for ways to decrease dependencies.
- Negotiable: The work should be flexible, enabling you to iterate as you get feedback. A good story captures the essence of what is wanted, not the exact details.
- Valuable: The story needs to be valuable to the intended consumer.
- Estimable: While it doesn’t have to be perfect, you should be able to generally estimate the effort of a story. The story's complexity, how it has been negotiated, and the team's experience all go into making the estimate accurate.
- Small: You should be able to complete a good story in a matter of hours or possibly a few days.
- Testable: You should understand how this story will be tested. The story should be created with Behavior Driven Development (Gherkin) tests, and the developers should know how the unit tests, contract tests, integration tests, and any others will work.
Slice stories vertically
Imagine you have a three-layer cake where each layer represents an application layer. The top layer is the UI, the middle layer is an API, and the bottom layer is the persistence layer.
To deliver true value, you want to slice off a whole piece of the cake, not just a piece of one layer. Stories should include work from all the pieces that deliver value.
Yes, there are some stories that won't impact all the layers, but make sure that any layers that need changing are included in the same story. This goes for tests, too. Don't make the mistake of having "coding" stories separate from "testing" stories.
Feature flags
An important piece of your small-batch strategy is feature flags. Simply put, a feature flag is a switch in the code that turns on or off certain functionality. Martin Fowler talks about different feature flag categories in his Feature Toggles post.
Release toggles are used to enable small batch development. By hiding incomplete features behind a feature flag, you can separate code deployments from feature releases. You don't have to keep updating your feature branch with all the things everyone else is pushing, and they don't have to wonder how your changes are going to affect the trunk once you finally get around to putting them in.
Of course, the goal is to have small batches that can be deployed at any time. In reality, there are times when your small batch can't stand on its own. That's where release feature flags come in to save the day.
Trunk-based development
Trunk-based development is the practice of delivering small chunks of code into the main repository branch once or more than once per day. To make that happen, you'll need a few things in place in addition to the couple of topics we've already covered.
Pair programming to get an instant code review
Automated tests that cover unit tests, contract tests, and behavior end-to-end tests where applicable
Fast builds to support multiple builds per day
It takes a lot of work to get real trunk-based development working smoothly. In Google's Trunk-Based Development article they suggest having a core group of advocates that can drive the change.
Lessons learned
Small batches enable rapid feedback. When you're experimenting with software every day, you need that feedback so you can adjust and create the correct solution at the correct time. Where should you start? Pick one thing that you're not doing and start doing it. The "Work in Small Batches" philosophy applies when shifting your team to working in small batches. Pick one thing from the list and work on it. You'll get there, and you'll be happy you did.