Most of our projects here at Atomic Object take just 2-6 developers. But sometimes we work closely with other teams that are much larger.
On my current project, we’re part of our client’s much larger team, about 20 people. The team started out small, and as we’ve grown in size, we’ve had to refactor our project management practices as well as our code.
Here are a few of the ways we’ve refactored our Agile process.
1. Keep Your Stories Small
At a smaller size, we were successful with having stories that were about a week or less in size. As we have been growing, these bigger stories have been causing us some pains. Lowering the maximum size of a story to 2 days in size has worked well for us.
These smaller stories help to minimize the open time of feature branches. The lesser the open time, the smaller the branch, and the more frequently merges come. The smaller stories help to keep all of our code bases synchronized with minimal effort. Let’s face it, rebasing and merging a large amount of commits onto your own branch is nobody’s favorite source control activity!
Smaller stories also allow for parallelization. At Atomic, we pragmatically pair program. We sit side by side, writing tests and code, and collaboratively developing the code and architecture for our features. However, not every programming task is optimal for pairing. Smaller stories allow you to lay down the groundwork of an architecture as a pair. Once the architecture is decided upon and well formed in code, you can split up and continue developing the individual pieces that make up the feature. Don’t forget about collective ownership though — be diligent with communication and code reviews.
2. Pay Special Attention to Iteration Planning
In smaller teams, it’s possible to spend a small amount of time planning your upcoming iteration and still be successful. Smaller teams can usually work it out among themselves to not conflict and step on each other’s toes.
But in a large team, it becomes difficult for everyone to keep track of what everyone else is doing. Iteration planning becomes a very meticulous and important activity to bring in tasks that minimize conflicts. With a larger team, the velocity will also be higher, and you have to look farther ahead into your backlog.
3. Always Have Working Software
Continuous integration and deployment with automated unit and acceptance testing will only get you so far. Stories play a huge role in the domain of working software. At the end of an iteration, our software should not be broken, and the completed stories should be merged in to a master branch.
The tricky thing is that working software does not necessarily mean feature complete. For example, if you have a link in your navigation that takes you to a “Hello World” page, this is okay. The software works, it compiles, all the tests pass, and it is deployable. When that unforeseen circumstance that requires an emergency patch and push to production, it is easy to implement a feature flag to turn off a link and hide the incomplete functionality.
4. Don’t Let Code Quality Lapse
There are a lot of writings and resources about code quality and why it’s important. John Croisant recently wrote an awesome post about 5 essential code maintenance practices, and Uncle Bob also has an excellent quote in the leading pages of Agile Software Development Principles, Patterns and Practices:
Continuous attention to technical excellence and good design enhances agility.
Code quality will reduce the number of open branches and reduce the number of refactors, thus reducing the opportunities for collisions and merge conflicts.
5. Break Down Epics and Stories
When we break down projects at Atomic, we usually go from epics to stories. In larger projects, it helps to add an additional level to this hierarchy, and break those stories down into development tasks. These development tasks should each take less than a day and leave us with working software. It is usually helpful to do a cursory glance at the pieces of code in play to arrive at these tasks. And don’t forget about any needed refactors either!
6. Build a Team Of Teams
I tend to avoid using specific Agile terms such as scrum and kanban. At Atomic, we don’t use any particular flavor of Agile. We take the best practices from a variety of techniques and create our own flavor of Agile that suits each project.
One of the nice techniques that we can borrow from scrum is the “scrum of scrums.” Scrum can be described as a recursive practice and allows for you to build a hierarchy of teams. Each team elects or appoints a member to sit on the scrum of scrums team and represent them. The scrum of scrums team should be doing things like milestone and iteration planning, and epic and story breakdowns, and should have their own daily stand up to synchronize on what their teams are working on.
7. Incorporate Other Ideas
When rolling your own flavor of agile, project management techniques don’t have to come from the various labeled versions of agile. We can borrow concepts from our computer science backgrounds. Projects behave similarly to the software we write, and concepts usually translate nicely between software and project management.
We can borrow and apply some compiler theory to our practices. Similar to just-in-time compilation, we can do just-in-time breakdowns of epics and stories. Thinking about this from a higher level, if we were to break down an entire large project up front and identify all the requirements, things will quickly start to smell like the old traditional waterfall techniques we are trying to avoid. The key to just-in-time breakdowns is to focus on the balance of keeping a healthy size backlog. If it’s too small, velocity takes a major hit with developers having nothing to do. If the backlog is too big, project agility suffers. Milestone planning is a good time to break up epics into stories.
Similar to lean manufacturing, if your just-in-time is late, it can quickly become expensive. Careful velocity tracking becomes critical. The upcoming iteration that you are planning should have at least the average velocity + 2 standard deviations of points available for work at the beginning of an iteration.
We can also think of software teams as distributed systems. Concurrent processing, shared memory, failure tolerance, synchronization, and message passing are all applicable and can be easily translated to project management practices. For example, shared memory is similar to a wiki where the team can share common patterns and project information. This also helps with on boarding new developers given that large teams will have a larger degree of turnover.
Large agile teams can still maintain the same agility of smaller teams; it just requires much more attention to detail and discipline.
What are some of your favorite techniques for healthy large agile projects?