June 6, 2007

Instant Feedback: A 10 minutes example.

You want an example to see what Instant Feedback can be? For this purpose, I will use ezWeb which is a small tool I prototyped for experimenting Instant Feedback. It is a Python package for iterative development of web application with a goal to write as less code as possible to get a default version, while sticking to habits when developing Python web applications in order not to be limited afterwards.

Sue needs a system for managing Seminars for knowldge diffusion to students. Sue have identified two main actors: herself as organizer of seminars, and students as attendees. For the moment the system is not required to manage speakers. For the moment, the most important stories are (with their id in parenthesis):
  1. A student can list the forthcoming seminars (ListSeminars).
  2. A student can view the summary of a seminar (ViewSeminarSummary).
  3. A student can register for a seminar (RegisterToSeminar).
  4. A student can view the participants of a seminar (ViewSeminarParticipants).
We can define a description of these stories using ezWeb in order to have a skeleton for our web app.

from ezweb.stories.model import *
from ezweb.stories.cherrypy import generate

student = Actor(
name='Student',
usecases=[
UseCase('View list of seminars'),
UseCase('View the summary of a seminar'),
UseCase('View the participants of a seminar'),
UseCase('Register for a seminar')
]
)

seminars = Application(
name='Seminars',
actors=[student]
)

generate(seminars)

We start with the ListSeminars story using Instant Feedback. Sue says: "There is no information to provide, and for each seminar we want: the name of the seminar, its date, and the room where it takes place."

Using ezWeb we can translate her specification as:

from ezweb.pages.model import *
from ezweb.pages.cheetah.generate import generate

# we need a list
list_of_seminars = List(
name='list_of_seminars',
details='get_details',
content=[
ListItem('name'),
ListItem('date'),
ListItem('room'),
]
)

# the page contains only the list
view_the_list_of_seminars = Page(
name='view_the_list_of_seminars',
css='/css/style.css',
content=[
Header(title='Seminars'),
sidebar,
Main(name='List of seminars', content=[list_of_seminars]),
Footer(content=[footer])
]
)

seminars = Site(
name='Seminars',
module='seminars',
pages=[
view_the_list_of_seminars,
]
)

generate(seminars)

Note: A page is split in four parts (only the Main is detailed above):
  • Header: name of the application,
  • Menu: containing links to the various features,
  • Footer: containing recurrent information,
  • Main: containing the input / output for a feature.
The generate() function produce templates pages for the application, which are generated in the same folder as the application skeletons (generated from the stories). With this small amount of information we can already present a UI to Sue in order for her to provide feedback.



Sue says that it does not looks like a list, couldn't she see an example of seminars?

The following code excerpt is the one generated by default from the description of scenarios.


class ViewListOfSeminars(object):

def __init__(self):
pass

@cherrypy.expose
def index(self):
return bindings.view_the_list_of_seminars()


To provide Sue a more accurate vision, we simply add fake objects in the default code for the feature.

class Seminar(object):
def __init__(self, name, date, room):
self.name = name
self.date = date
self.room = room

class ViewListOfSeminars(object):

def __init__(self, ctrl):
self.ctrl = ctrl

@cherrypy.expose
def index(self):
seminars = [
Seminar('Agility, XP, Scrum', '2007-05-15 14:00:00', 202),
Seminar('Java Generics', '2007-05-29 14:00:00', 202),
Seminar('Python', '2007-05-22 14:00:00', 202),
]
return bindings.view_the_list_of_seminars(seminars)

Then we can show Sue a more realistic list of seminars. This time she just says nice.



On the basis of this small interaction, we now have an acceptance test for the story ListSeminars. On this basis we can write an automated test, and really implement the story: defining the database tables together with the code to extract information from the database (i.e. the true implementation for ViewListOfSeminars.index).

Tools such has ezWeb may be implemented in various languages and environments. The time it took to develop it (about 2 weeks --but only two dozen hours of work-- for a usable version of ezWeb in the context of CherryPy, Cheetah, SQLObject) worth the ROI (return on invest) as we use it in the project for air quality.

Instant Feedback: Is this what you mean?

I like TDD because it gives me feedback. I know if my code pass the expressed specification. Notice the expressed qualifier of specification.

When speaking together, an idea lives at four different levels. First, the idea lives in my mental representation. Second, it try to express as best as I can the idea with my words and the meaning of these words in my mental knowledge. Third, you hear these words with the meanings of these words in your mental knowledge. Finally, the idea lives in your mental representation on the basis of your understanding of the words you just heard.

To make it short: There is a (huge) gap between our mental representation of the same idea. And the less we share common meaning for the same words, the bigger is the gap. Imagine discussing with an end-user who is a medical doctor (GP) in order to build a system to manage her office. The gap is an ocean.

Going back to the TDD loop, the faster I go from the spec to a Running Tested Feature, the faster the end user can confirm my understanding of the idea she expressed. The motivation of Instant Feedback is aimed at reducing the gap and increasing the speed to transcript the end-user request to a piece of software. Its roots are Agility of course but also rapid application developpement and live prototyping.

Instant Feedback can be defined as a 15 minutes exercise: 5 minutes for bluiding the user interface and an empty shell of the function, and then 10 minutes for make it run (even with hard coded data). These 15 minutes are performed together with the end user: We act as a compiler from her words to a program. It requires powerful tools for allowing this rapid prototyping approach. Moreover, the goal is not to throw away this piece of description and software but to use it as a basis for the complete developments.

The integration of Instant Feedback in an agile development cycle can be done as follows:
  1. We start by a User story workshop (User Stories Applied, M.Cohn).
  2. The end user select the user stories she want to be implemented during this iteration.
  3. For each user story we discuss the details and use Instant Feedback when discussing the way the end user will interact with the application and the behavior of the application. Then, we move on to the implementation of the story.
The result of the Instant Feedback exercise can be seen as an acceptance test for the story if you do not wish to stick with its result. But it can also be a basis for the true implementation of a feature.

May 29, 2007

Testing for Blackbox re-Engineering or The lost source code...

Working on a system for chemistry computation, we are integrating software components written in various languages (FORTRAN, C++, Shell, Python) using Python. As the new server is 64 bits (instead of 32 for the existing ones), we have to recompile a lot of stuff, but unfortunately the source code of some of the components have been lost.

We are re-implementing such sourceless components in Python and our specification is reduced to what the component is supposed to do based upon the name of the component and the command line arguments depicted by --help. In addition we can run the component on a know input (a NetCDF file) in order to compare our implementation to its expected output (a csv like file).

One of these component produces a file containing numerical data as four strings separated by spaces. Each number is written using the exponent notation. Our problem was to compare the string version of numbers produced by a Fortran program (like 0.35315E+02) to a string version of numbers produced by a Python one (like 3.5315E+01). These files (2200 lines each) look like:


== tests/computed.ps.data ==
-7.5000E-01 4.8644E+01 3.5315E+01
-6.5000E-01 4.8644E+01 3.5333E+01
-5.5000E-01 4.8644E+01 3.5291E+01

== tests/expected.ps.data ==
-0.75000E+00 0.48644E+02 0.35315E+02
-0.65000E+00 0.48644E+02 0.35333E+02
-0.55000E+00 0.48644E+02 0.35291E+02


In fine, it turned to be quite simple. Files are compared line by line and, lines are compared value by value. Even if the format of values if not the same, once translated to float, values can be directly compared.


expected = open('tests/expected.ps.data')
computed = open('tests/computed.ps.data')

for e_line, c_line in zip(expected, computed):
for e, c in zip(e_line.split(), c_line.split()):
assert float(e) == float(c), \
"expected %s differs from computed %s" % (e, c)


Python, thanks for your supporting my lazyness...

March 23, 2007

Satisfaction Driven Development

Lots of * Driven Development acronyms exist out there. While Agile and Lean development targets user satisfaction, most of them sound quite technical like (Test / Behavior / Model / etc). Our development process should be driven by the end-user satisfaction, so I like Satisfaction Driven Development as a reminder that software is only a means and never a goal in itself.