Recently I was introduced to a tool developed by my co-workers called Story Mapper. Story Mapper was inspired by an agile design technique outlined in this post; it involves a way of organizing user stories around activities. During the story writing process each story is given an activity such as “registering” or “administrating” so instead of a flat backlog you have groups of stories allowing you to more effectively explain and plan your system.
Here at Carbon Five we use Pivotal Tracker for story writing and its story labels to specify each story’s activity. Story Mapper then uses the Pivotal Tracker API to pull in stories and organize them by that activity/label. This provides a great way to see at a high level your progress implementing each of the system’s activities. Could this activity focused view be applied to the code-base itself?
Organizing Tests by Activity
I used to organize Cucumber features by the various roles identified during story writing. This was great for answering questions such as “What can admins do?” but not so great for looking at the high-level picture. I find that I’m more interested in knowing what you can do on the website rather than what a specific type of user can do.
Given activities ARE the high-level definition of what you could do, I decided to apply this activity based organization to Cucumber features on a recent client Rails project. The project was a pilot program where people could qualify, register, and manage an in-home appliance remotely through the web.
Here’s a view of the project’s features directory (omitting the directories that don’t contain features such as support and step_definitions):
These directory names correspond to the activities that were identified during the story writing process and used as story labels in Pivotal Tracker thus bringing the benefits of Story Mapper to the codebase. Developers can now quickly get a feel for what you can do on the website. It’s also easier to determine where a put a feature file because of the mapping of directories to story labels.
Organizing Code by Activity
After organizing the project’s Cucumber features by activity I decided to try this technique on lower level specs and even non-test code.
As the project progressed, more and more functionality was added to its User class. This class’s file began to balloon in total lines of code. Personally, I don’t feel that additional files add more complexity to a codebase but that classes do. So instead of breaking the User model into separate classes I decided to split its behavior by activity and implement each activity in a separate file.
This resulted in the following structure:
I decided to keep generic account related behavior such as email and password validation in app/models/user.rb. The first few lines of that file also loaded in the activities:
class User < ActiveRecord::Base
# basic account validation, etc.
Each activity file re-opened the User class and added its specific behavior.
class User < ActiveRecord::Base
# scheduling logic
I organized their corresponding specs the same way:
This technique is kind of a crude Ruby implementation of the Smalltalk languages’s class and method categories. Smalltalk gives you the ability to create categories for classes and their methods, making browsing the source code much more easy and enjoyable. With Ruby’s open classes we can imitate this by defining our classes over multiple files. Code comments could also be used to delineate categories of methods in a source file. However it would take a disciplined team to be aware of the purpose of the comments when modifying or adding functionality to a class.
To some people a large class is a smell that the class is doing too much. The idea of breaking its definition over multiple files feels more like a stopgap and a violation of OO principles. Maybe in a language like Ruby organizing code by activity is taking this idea a bit far. However I do feel that for user stories and tools like Cucumber it can very beneficial in documenting at a high-level what a system can do. Feel free to give Story Mapper a try while you’re at it!