User Stories and Activity Diagrams

I have been using Omnigraffle to create activity diagrams associated with user stories. Some people may find this overkill but I find doing these diagrams makes transitions clear that might otherwise be missed until later when they lead to costly re-architecting of the screens and flows.

I use Omnigraffle for UI wireframes, so it seemed natural to use it for activity diagrams, which is what I have been doing. The issue is keeping the diagrams up to date. Today, while playing with the Ragel state machine compiler I discovered Ragel’s awesome states/transitions diagramming feature. I learned the feature uses Graphviz to create the diagrams from the parser grammar. This led me to Ruby Graphviz, a Ruby library for interacting with Graphviz. It got me thinking that maybe there was a simple DSL that could be used to describe UI activities and screens and then automatically generate diagrams from the description. This would make keeping descriptions and diagrams in sync very easy.

My first step is always to get a piece of the most basic idea working, then add another piece, etc until a basic prototype is complete. Given this approach the first step was to use ruby-graphviz to create a basic diagram similar to one of our activity diagrams (a very basic one). So here was a quick pass experiment for using ruby-graphvis to create a diagram. The next step would be to define a basic DSL and then write a parser and state-machine and incorporate the graph creation into the state machine as it parses the DSL activity description.

require 'graphviz'
# Create a new graph
g = :G, :type => :digraph )
us_num = 47 #placeholder, will get from file name in final version.
# set global node options
g.node[:color] = "#333333"
g.node[:style] = "filled"
g.node[:shape] = "box"
g.node[:penwidth] = "1"
g.node[:fontname] = "Trebuchet MS"
g.node[:fontsize] = "8"
g.node[:fillcolor]= "#294b76"
g.node[:fontcolor]= "white"
g.node[:margin] = "0.0"
# set global edge options
g.edge[:color] = "#666666"
g.edge[:weight] = "1"
g.edge[:fontsize] = "6"
g.edge[:fontcolor]= "#444444"
g.edge[:fontname] = "Verdana"
g.edge[:dir] = "forward"
g.edge[:arrowsize]= "0.5"
my_nodes={"WF1"=>"New Account Screen", "WF2" =>"Account Recover Screen", "WF3" => "Welcome Screen"}
my_decisions={"D1"=>"", "D2" =>"", "D3" => "", "D4" => ""}
g.add_nodes("Start",{:label => "", :shape => "circle"})
my_nodes.each do |key, value|
# When adding a node the node name becomes the label so can access later.
g.add_nodes(key,{:label => value})
my_decisions.each do |key, value|
# When adding a node the node name becomes the label so can access later.
g.add_nodes(key,{:label => value, :shape => "diamond"})
g.add_nodes("End",{:label => "", :shape => "circle"})
#hello = g.add_nodes( "Hello" )
#world = g.add_nodes( "World" )
puts g.inspect
# Create an edge between the two nodes
g.add_edges( g.Start, g.D1)
g.add_edges( g.D1, g.WF1, {:label => "[create account]"})
g.add_edges( g.D1, g.WF2, {:label => "[attach account]"})
g.add_edges( g.WF1, g.D2)
g.add_edges( g.D2, g.D3, {:label => "[failure]"})
g.add_edges( g.D2, g.WF3, {:label => "[account created]"})
g.add_edges( g.D3, g.WF2, {:label => "[account exists]"})
g.add_edges( g.WF2, g.D4)
g.add_edges( g.D4, g.WF2, {:label => "[failure]"})
g.add_edges( g.D4, g.End, {:label => "[recovered]"})
g.add_edges( g.WF3, g.End)
# Generate output image
g.output( :png => "US#{us_num}.png" )

This above very simple example creates the diagram below.

This needs some cleanup of the aesthetics but the possibility is interesting. The next steps will be to come up with a very simple dsl, as simple as possible, maybe something that is markdown compliant so it fits inside our wiki pages cleanly. It would need to cover start and end states as well as intermediate state nodes. It would need to also handle decision points (diamonds) as well as transitions (edges) and notes. The simplest form would be to allow listing all nodes and all decisions, and then listing the connections between them. Labels would also be needed on all of these. IDs would probably help for wiring up the connections.

Then it is a matter of running a parser on the wiki pages, looking for the syntax associated with the activity descriptions and generating a diagram using some ruby code. This might be a nice experiment to familiarize myself with Ragel to better understand its strengths and capabilities.

I hope to get to spend some free time on this over the coming weeks, as it seems like a fun side project.