Archive for October, 2010
Distributed Agile: Physical story wall still useful
When I started working on my current project there was no physical story wall, instead the whole project was being tracked on Mingle.
The current state of the Mingle story wall was sometimes visible on a shared monitor and sometimes wasn’t, depending on whether or not the monitor had been turned off.
There was also a small wall used to track which stories were in development but after that there was no physical visibility of the status of anything.
The advantage of this approach was that there was no need to have the overhead of having to maintain a physical story wall and an electronic one.
The disadvantage was that it was quite difficult to see what people were working on and there was a very definite split between development and testing.
Mingle and similar tools are useful in distributed teams but they are information refrigerators in the sense that you have to put in some effort in order to get information from them – it’s not immediately available to you.
The physical story wall on the other hand is an information radiator which is what we want!
In the case that we get around that problem by using a projector to beam the web page onto a wall I still don’t think it’s as effective because it’s less human to deal with a projection than a real card wall.
I find it’s much more effective for people to be able to move cards around with their hands and then move people in between those cards depending on what pairings make sense at any given time.
The collaboration on my team seems to have increased quite dramatically since we made the card wall physical and I’d be surprised if the overhead of updating Mingle and the story wall was more than 5 minutes per day.
Coding: Context independent code
I’ve been flicking through Growing Object Oriented Software Guided By Tests again and in Chapter 6 on Object Oriented Style I came across the part of the chapter which talks about writing context independent code which reminded me of some code I’ve worked on recently.
The authors suggest the following:
A system is easier to change if its objects are context-independent; that is, if each object has no built-in knowledge about the system in which it executes
I was writing a bit of code in our ApplicationHelper which would only be used in a certain context within one of the views.
The view code was roughly like this:
<% unless current_user.blank? %> <% if show_something_for(current_user) %> <!-- some html --> <% else %> <!-- some other html --> <% end %> <% end %>
with the ‘show_something_for’ method defined like so:
module ApplicationHelper def show_something_for(user) user.has_foo? and user.has_bar? end end
Inside the ‘show_something_for’ method we’re working off the assumption that user will not be nil based on that fact that it’s being used inside a context where we’ve already checked that we do in fact have a user.
It’s not identical to the situation the authors are describing but there is an implicit assumption in this method which would mean that we couldn’t necessarily just go and use it anywhere else in the code base and assume that it’d work.
Having said that, the code is slightly simpler than if we had to assume that ‘user’ might be nil.
The situation in which we don’t have ‘current_user’ happens when no user has logged in so the method warden mixes into our ApplicationController returns nil.
I think it would be possible to make use of the null object pattern and store a guest user in the session but there are a fair few places in the code base that rely on the current implementation at the moment.
Ruby: Using alias with ‘indexers’
I’ve been browsing through some of the Rails routing code while following Jamis’ Buck’s blog post and I came across something I hadn’t seen before while inside the ‘NamedRouteCollection’ class.
The bit of code which initially confused me is in RouteSet.add_named_route:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | module ActionController module Routing class RouteSet def initialize ... self.named_routes = NamedRouteCollection.new end def add_named_route(name, path, options = {}) # TODO - is options EVER used? name = options[:name_prefix] + name.to_s if options[:name_prefix] named_routes[name.to_sym] = add_route(path, options) end end end end |
Reading the code on line 12 I was convinced that this code was being used to set a value into an array or hash so I was confused as to how the url/path helper methods which get added for named routes were being created since there didn’t seem to be any code which was calling the method in ‘NamedRouteCollection’ which would create them.
I eventually stumbled into the initializer code above which made me realise that ‘named_routes’ wasn’t actually a hash or array but an instance of ‘NamedRouteCollection’.
The methods ‘[]=’ and ‘[]‘ are aliases which call the ‘add’ and ‘get’ methods on ‘NamedRouteCollection’
module ActionController module Routing class RouteSet class NamedRouteCollection def add(name, route) routes[name.to_sym] = route define_named_route_methods(name, route) # creates the helper methods end def get(name) routes[name.to_sym] end alias []= add alias [] get end end end end
For me it seems like perhaps the creation of the helper methods could be the responsibility of another object although I can see why it’s been put in NamedRouteCollection since those helper methods are only created if you have a named route.
Either way it was pretty confusing for me initially that you could create this type of side effect from a method call that looked like it was just adding a key, value pair to an array.
Distributed Agile: Cultural Differences/Expectation disconnect
I came across an article written a few months ago titled ‘Outsourcing doesn’t work‘ which discussed some of the problems the author has experienced while working with teams offshore.
The article is provocatively titled but has some interesting observations which I thought I could contrast with my own after working offshore in Pune, India for a couple of months now.
The team I’m working on is distributed between Pune and Chicago so it’s not exactly the same situation as the author’s but the majority of the team are in a different country to the client.
The author focuses on two main issues:
Culture Differences
People in different cultures have a different idea about authority. In America, an entry level developer would be expected to answer “no” if a CEO asked him if he could finish a complex project by the end of the week. Under commit and over deliver is an established business mantra here. In many Indian or Asian cultures, the right thing to do is say yes since an authority figure asked you, although they would not be able to deliver.
I remember reading something similar about attitudes towards authority in Malcolm Gladwell’s Outliers with respect to the way that the first captain communicated with the captain when flying a plane.
Gladwell described several situations where accidents had happened on Asian airlines due to the first captain feeling unable to question the authority of the captain who was his/her superior.
I haven’t noticed that happening on the team I’ve been working on – sometimes we say we’ll be able to deliver something in a time period and get it wrong but that’s no different to any team I’ve worked on in Australia or the UK.
The main difference that I’ve seen which is potentially cultural is the rhythm of the day compared to what I’m used to.
On the other teams I’ve worked on we’ve typically worked from around 9am until 6pm with maybe an hour break during the day.
Here I find that people are typically in the office from around 9am until 8pm but the breaks scattered throughout the day probably total 2 or 3 hours.
I don’t know if it’s like that even in all the ThoughtWorks offices in India but that’s my observation from Pune at least.
Expectation Disconnect
It is generally accepted that the quantity of bugs (for requirements even specifically cited in documentation) is much higher for an outsourced project than a local one. Furthermore, the concept of well-designed, clean, efficient, maintainable code is often foreign (pardon my pun) with speaking with overseas developer. The only metric is whether or not it works, and it doesn’t even have to work.
As I mentioned in my first post about distributed agile a couple of months ago I’ve found that it’s much easier to build the wrong thing when working in a different time zone to the product owner but I wouldn’t say the ability to write maintainable code is much different.
Having said that, we have sometimes fallen into the trap of only focusing on completing stories and not paying enough attention to technical issues in the code.
I think this is partly due to the fact that since we’re in a different country the main output that people onshore can see is the stories that we complete in a given time period. That therefore becomes the focus.
We have started tracking any technical work we do which we weren’t doing before so hopefully that will help fix that problem
My current opinion on bugs is somewhat influenced by the 37 Signals book ‘Getting Real‘:
Just because you discover a bug in your product, doesn’t mean it’s time to panic. All software has bugs – it’s just a fact of life.
I don’t necessarily think that fixing ‘bugs’ is the most valuable use of time, particularly if those ‘bugs’ describe scenarios which are very unlikely to actually happen anyway.
When you’re onshore it’s much easier to point that type of thing out and then not work on it but offshore since you might only become aware of that ‘bug’ when the onshore team is asleep it’s more difficult.
Ruby: Hash default value
I’ve been pairing a fair bit with Ashwin this week and one thing he showed me which I hadn’t previously seen is the ability to set a default value for a hash which gets returned if we search for a key that doesn’t exist.
This is an idea that I originally came across while playing around with Clojure but with Clojure the default value was defined in the calling code rather than in the hash definition.
For example:
(def scores {:mark 10 :dave 20}) (get scores :tony :0)
For a similar piece of code in Ruby we could write the following:
scores = { :mark => 10, :dave => 20 } scores.default = 0
Now if we search for a key that doesn’t exist in the hash then we’ll get the value 0:
scores[:some_other_guy] 0
We’re using this in a couple of places in our code base already but it’ll be particularly useful for an upcoming piece of functionality where we want to vary the look and feel of some pages depending on certain criteria.
For the majority of users the look and feel will remain the same but for some of them it will vary slightly. Controlling that behaviour by using a hash with some default settings therefore seems like a reasonably good fit.
RSpec: Testing Rails routes
Something which I keep forgetting is how to write controller tests where I want to check whether an action correctly redirected to another action.
With most of the routes in our application we’ve created a ‘resourceful route’ where each action maps to a CRUD operation in the database.
We can do that with this type of code in routes.rb:
ActionController::Routing::Routes.draw do |map| map.resources :foos end
Several helper methods based on named rotes get created and included in our controllers when we do this and we have access to those inside our specs.
We can see the named routes by executing the following command from the terminal:
mneedham@markneedham.local ~/SandBox/rails_test$ rake routes CONTROLLER=foos
(in /Users/mneedham/SandBox)
foos GET /foos(.:format) {:action=>"index", :controller=>"foos"}
POST /foos(.:format) {:action=>"create", :controller=>"foos"}
new_foo GET /foos/new(.:format) {:action=>"new", :controller=>"foos"}
edit_foo GET /foos/:id/edit(.:format) {:action=>"edit", :controller=>"foos"}
foo GET /foos/:id(.:format) {:action=>"show", :controller=>"foos"}
PUT /foos/:id(.:format) {:action=>"update", :controller=>"foos"}
DELETE /foos/:id(.:format) {:action=>"destroy", :controller=>"foos"}The following helper methods would be created for this resource as per the documentation in resources.rb:
# Named Route Helpers
# ============ =====================================================
# foos foos_url, hash_for_foos_url,
# foos_path, hash_for_foos_path
#
# foo foo_url(id), hash_for_foo_url(id),
# foo_path(id), hash_for_foo_path(id)
#
# new_foo new_foo_url, hash_for_new_foo_url,
# new_foo_path, hash_for_new_foo_path
#
# edit_foo edit_foo_url(id), hash_for_edit_foo_url(id),
# edit_foo_path(id), hash_for_edit_foo_path(id)
Keeping this in mind means that if we do something like this inside our ‘create’ action:
class FoosController < ApplicationController def create ... redirect_to :action => :index end end
We’d be able to test that redirection with a spec along the following lines:
describe "POST create" do it "should redirect back to the index page" do post :create, :foo => { :bar => "value" } response.should redirect_to foos_path end end
We can also use any of those other helper methods inside our tests which seems obvious looking at it now but wasn’t for me until a colleague pointed it out.
It is pretty cool to be able to write specs like this and they seem much more readable than the equivalents you’d be able to test around ASP.NET MVC code in C#.
Agile: Constraints
I recently came across quite an interesting post written by Steve Garnett where he discusses the difference between constraints and impediments inside organisations.
He comes to the following conclusion:
For me, the difference between an impediment and a constraint is whether the individual, team, organisation, enterprise, or industry considers the obstacle as removable.
If whoever is working with the obstacle believes it can be removed then it is considered an impediment, if the same person doesn’t not believe it can be removed, or doesn’t wish to work towards it’s removal, it’s considered a constraint.
Over the last few years of working in various different organisations my experience is that there will always be some sort of obstacles and while I think Steve’s distinction is useful, it’s also helpful to consider whether we want to commit our time and energy to removing particular obstacles.
My former colleague Dan Bodart referred to this as ‘choosing your battles‘ and as well as saving energy and enthusiasm by choosing to live with some obstacles it also has the benefit that people who can help us remove obstacles will be more receptive to us if we’re selective in the battles we choose to fight.
The other thing that I’ve noticed is that people in the same team have different opinions on what they consider a constraint or impediment.
For example, I wrote last year about the benefit we can get from introducing new people with fresh perspective into a situation.
I found it interesting to notice the different perspectives of the new people compared to those of people who had been there a while.
Quite frequently something which the people who’d been there a while would consider to be a constraint would be seen as an impediment by the newer guys.
Another thing that we learnt from this situation is that sometimes waiting for a while can be a useful tactic before trying to remove an obstacle.
By this time we’ll have hopefully built a bit more credibility and trust within the system that we’re trying to change.
Ruby: Active Record – Using ‘exclusive_scope’ in IRB
Ashwin and I have been working recently on a bit of code to make it possible to ‘soft delete’ some objects in our system.
We’re doing this by creating an additional column in that table called ‘deleted_at_date’ which we populate if a record is ‘deleted’.
As we wanted the rest of the application to ignore ‘deleted’ records we added a default scope to it:
class Foo < ActiveRecord::Base default_scope :conditions => "deleted_at_date is null" end
This works fine but we wanted to be able to see the status of all the records in IRB and with the default scope ‘Foo.all’ no longer returns ‘soft deleted’ records.
Luckily Active Record provides a protected method called ‘with_exclusive_scope‘ which we can use to get around this:
Foo.send(:with_exclusive_scope) { Foo.find(:all) }
Since it’s a protected method we can only access it in IRB by using ‘send’ which is a bit hacky, something David Heinemeier Hansson would refer to as syntactic vinegar.
Interestingly our first attempt to use ‘with_exclusive_scope’ involved writing the above code like this…
Foo.send(:with_exclusive_scope) { find(:all) }
..which results in the following error because when the closure was created ‘self’ referred to the main object rather than to ‘Foo’:
NoMethodError: undefined method `find' for #<Object:0xb77cd94c>
from (irb):62
from /var/lib/gems/1.8/gems/activerecord-2.3.5/lib/active_record/base.rb:2143:in `with_scope'
from /var/lib/gems/1.8/gems/activerecord-2.3.5/lib/active_record/base.rb:2151:in `with_exclusive_scope'
from (irb):62:in `send'
from (irb):62
from :0Since the main object has no method called ‘find’ we get an exception thrown.
Agile: The curse of meetings
Something which can often happen with agile software development teams is that in the desire to take everyone’s opinion into account for every decision we end up having a lot of meetings.
Toni wrote about this a while ago and described a situation where he’d managed to get rid of a meeting and just have a discussion after the stand up with the necessary people.
While this is a good idea I still think there are occasions where it’s not necessary to discuss every problem down to the minute details with the whole team.
For example, this week we needed to make changes to some migration files to deal with the fact that we’d removed a gem which a migration previously relied on.
3 solutions were suggested and each had good and bad parts to it so any discussion amongst a group of people was likely to result in a split opinion on what the best approach to take was.
In that situation I think it makes much more sense for one pair to take the problem, work out what they think is the best solution and then just do it.
If it turns out that wasn’t the best solution then we can change it in the future.
It can be quite difficult to persuade people that it’s not necessary to have meetings because the meetings are usually are about things which affect the team so it seems to make sense to involve everyone.
On the other hand I like to think of what people could be doing instead of attending that meeting.
If what they could be doing would be more valuable then perhaps they shouldn’t be in that meeting.
Ruby: Getting the caller method with Kernel.caller
One of the things I’ve been finding when debugging Cucumber specs is that due to the number of levels of indirection present in those examples it becomes quite difficult to work out exactly how certain pieces of code got called.
In one cuke we were trying to work out how 4 objects of the same type were ending up in the database when it seemed like there should only be two.
Having failed to figure it out just by reading the code we resorted to putting calls to Kernel.caller inside the Factory Girl setup code so we could see how we’d ended up at that code:
p caller.inspect
It’s not a perfect solution – since the scenarios we write get parsed by Cucumber it’s hard to tell exactly which line the call stack started on – but it provided enough information for us to track down the steps which were calling the Factory Girl code in question.