Agile Web Development course report
June 9, 2008 by Antti TarvainenI wrote a report about the course I taught at Tampere University of Technology in April: Experiences from Agile Web Development course, spring 2008.
I wrote a report about the course I taught at Tampere University of Technology in April: Experiences from Agile Web Development course, spring 2008.
(This is part of a post I wrote to Agile Finland mailing list. You can read the entire thread there.)
There are three approaches to software design: top-down, bottom-up and outside-in. (People mean different things when they say top-down, so don’t be surprised if my definition doesn’t match yours. What I am describing here should be taken as a clarification of the rest of the post, not as a normative definition. The same goes to bottom-up and outside-in.)
Bottom-up means you start with the “smallest things”. For example, you know that you are going to need a custom communication protocol for your distributed application, so you start by writing the code for that. Then you write - let’s say - database code and then UI code and finally something to glue them all together. The overall design becomes apparent only when you have all the modules ready.
Top-down starts with the overall design. You find modules and interfaces between them, then go on to design class hierarchies and interfaces inside individual classes. You go into smaller and smaller detail until you reach the code level. At that point your design is ready and you start the actual implementation. This is the classical sequential approach to software process.
Outside-in is an iterative method. You start with one user story, and write code for that. Then you go on to the next user story, and so on. You start implementing from the code that is closest to the user and proceed toward more and more implementation detail. In a web application (using TDD) this order could be: acceptance test, route test, route code, view test, view code, controller test, controller code, model test, model code. You only start writing code for a lower level when the upper level requires that.
The understanding - or fashion - today is that TDD is best done outside-in. It may be a little more difficult technically, since you need to isolate layers somehow, so that you don’t have too many failing tests in your green-red cycle. Typically mock objects are used for this. Of course isolating unit tests is a good idea regardless of your approach, so you may want to use mock objects anyway.
Now the important question of course is, how do you make sure that the overall design works with outside-in process. In my opinion it doesn’t hurt to do a little top-down design before you start the outside-in cycles. However, be careful not to over-do it. Using a popular framework helps. The top-down design phase can be very short when you have something well-known and well-tested as the foundation of your architecture.
After the rough design is ready, the first few user story iterations should strive to validate that architecture (especially if you have rolled out your own). Architectural redesign is cheap early in the project but very costly later on. Using outside-in is an improvement over a sequential approach in this sense. With a waterfall process you don’t get feedback until much later, and if the architecture doesn’t work, it will be usually too late to fix it.
Another important consideration is how you deal with requirement changes, as there will be some in every project. It is interesting to note the differences between the three approaches in this regard.
With bottom-up, we have small isolated libraries. The hope is that we can reuse them easily when the need arises. Also, a well used bottom-up design builds up the abstraction level in every layer. The top levels will be easy to understand and easy to change. This is the Paul Graham argument for bottom-up design.
Top-down takes care of extendability by carefully desigining extension points in the architectural design. Elaborate class hierarchies and design patterns are used to make reuse and modifiability as easy as possible.
Outside-in doesn’t concentrate on modifiability per se. The belief is that the best way to provide malleability is to write the simplest - or minimal - code possible. When there is nothing that is not needed, the purpose and the functionality of the code is easy to understand and easy to modify.
Experience has shown that the top-down approach to modifiability doesn’t work. Designing for extendability makes the code unnecessarily complicated, slowing the implementation process. What’s worse is that the extension points usually end up in the wrong places. The requirement changes often come as surprise, in a place you wouldn’t expect. The original design didn’t allow for extendability after all, and an expensive redesign is needed.
Compared with bottom-up, the advantage of outside-in again is the minimality of the code. Writing a module with bottom-up approach it is hard to guess what is actually needed by the upper layers. The interfaces will have unnecessary classes and methods. This takes time, and makes the code harder to use.
Of course my discussion here is both simplified and polarized for the sake of the argument. In real life you will not make a clear-cut choice between the three approaches and stick with it, but you will use whatever approach you believe works best in each situation.
Documentation, design patterns, private methods, variables, comments. One by one, core concepts of software development become design smells.
A purist removes features until there is nothing left. For him, code is not a solution, it is the problem. Will he ever be happy?
So my course ended on Friday. It didn’t go as I had planned.
Although I had stressed out about the course for six months, only a small amount of that had resulted in useful preparation. Many of the lectures and some exercises were last-night wonders, and … um … let me say, far from perfect. One important lecture I just couldn’t prepare in time - so I skipped it completely, much to the frustration of the students when they later actually needed that stuff.
More importantly, I seriously overestimated the amount of things I can teach in thirteen days. I had to do a major rescoping after the first few days, cutting to about half of the original goals.
And the feedback from the students? Almost entirely positive.

Kathy Sierra was right: what the students do is important - what the teacher does is not.
In this case, the students spent most of the course working on customer-oriented projects. Aside from technology (Ruby on Rails), process framework (very lightly enforced Scrum), customer requirements (user stories they could prioritize with the customer), and the working environment (classroom TC217) the students had the power to choose their manner of working. They had the power to make choices, the power to make their own mistakes and the power to learn from them.
And they loved it.
Thanks to prof. Carsten Bormann (from University of Bremen) who gave the idea for this course, helped a lot in the preparation and gave a wonderful guest lecture on Ajax. Thanks to everyone whose material I used on the course. Thanks to the professors of the Deparment of Software Systems at TUT, who gave me the chance to teach this course, and to my boss at Atostek, who let me have the days off to do this. Thanks to the people of Tampere.rb for their help and support. Thanks to the customers for their time and effort.
Most importantly, thanks to all the students. It is you who made this course a success.
There are three wrong reasons not to use this sentence.
There is a good reason: when your audience already knows that none of the above is true.
Each method should have a clear, simple purpose. The name of the method describes this cohesive meaning. Exceptions denote behavior that breaks the cohesion.
The naming of .NET Parse and TryParse methods illustrates how naming and exceptionality are linked via cohesion:
// Parse promises to *parse* and thus not being able to
// parse is exceptional.
try
{
double value = Double.Parse("1.5");
}
catch (FormatException) { /*...*/ }
// TryParse promises just to *try to parse* and not being
// able to is unexceptional.
double value;
bool result = Double.TryParse("1.5", out value);
Consequently, if you change the error handling mechanism of a method, the name of the method should probably change too.
Looking at a line of code, ask yourself three questions:
If you can find answers to these questions easily, the code is maintainable.

I want my blog to have three qualities:
Consequently, I am going to try a new blogging format: exactly five sentences, at most one image and at most one code sample.

Walls cause great suffering.
Not long ago, a wall may was built through a city for political reasons, arbitrarily dividing families. Communication to the other side was regulated, messages censored. The letters were no better than rumors, written and rewritten. There was fear that this was only the beginning, that the city would crumble and fall.
Given all the harm this particular wall caused, one wants to say: “Do not build walls. Let everyone come and go as they wish.” But that would be foolish. Most walls we need. Some protect our privacy and warmth. Others keep the cattle within the meadow. Note this: We build these walls where we want them. When they have done their duty, we take them apart. It is we who control these walls, we whom they protect.
Sometimes I visit the city after I fall asleep. In the dream, a young man looks at the remains of the torn-down wall and asks: “Grandpa, what was VariableWrapperCache?” I smile, because he doesn’t need to know. And I tell him the story of the wall.
Three announcements: