ThoughtWorks Studios

ThoughtWorks' Agile Project Management application

ThoughtBloggers

Abdul Salam (feed)
Agile Hong Kong (feed)
Akshay Dhavle (feed)
Akshay Rawat (feed)
Alex Hung (feed)
Alistair Jones (feed)
Amit Rathore (feed)
Anand Iyengar (feed)
Andrew Palmer (feed)
Andy Marks (feed)
Antonio Terreno (feed)
Avishek Sen Gupta (feed)
Barrow Kwan (feed)
Ben Hogan (feed)
Benjie Davis (feed)
Bernardo Silva (feed)
Bhavin Javia (feed)
Bill Six (feed)
Brandon Hastings Byars (feed)
Brian Guthrie (feed)
Carlos Villela (feed)
Cenk Civici (feed)
Chad Wathington (feed)
Chirdeep Shetty (feed)
Chris Leishman (feed)
Chris Stevenson (feed)
Christopher Johnston (feed)
Christopher Read (feed)
Cliff Morehead (feed)
Damana Madden (feed)
Dan Abel (feed)
Dan Bodart (feed)
Dan North (feed)
Dan Wieringa (feed)
Daniel Aragao (feed)
Daniel Manges (feed)
Danilo Sato (feed)
Daragh Farrell (feed)
Darren Smith (feed)
David Kemp (feed)
David Leigh-Fellows (feed)
Dennis Byrne (feed)
Elizabeth Keogh (feed)
Eric Liu (feed)
Erik Doernenburg (feed)
Evan Bottcher (feed)
Farooq Ali (feed)
Felix Leipold (feed)
Francisco Trindade (feed)
George Malamidis (feed)
Graham Brooks (feed)
Grzegorz Gigon (feed)
Hakan Raberg (feed)
Halvard Skogsrud (feed)
Hsue-Shen Tham (feed)
Ian Cartwright (feed)
Ian Robinson (feed)
Igor Stoyanov (feed)
Jake Scruggs (feed)
James Crisp (feed)
James Webster (feed)
Jason Yip (feed)
Jay Fields (feed)
Jeff Rogers (feed)
Jeff Santini (feed)
Jeff Xiong (feed)
Jen Mozen (feed)
Jez Humble (feed)
Jiangmei kang (feed)
Jie Xia (Jessie Xia) (feed)
Jim Arnold (feed)
Jim Webber (feed)
Jinzhou Chen (feed)
Joe Homs (feed)
Joe Poon (feed)
John Hume (feed)
John Johnston (feed)
Jon Pither (feed)
Jonathan Andrew Wolter (feed)
Jonny Leroy (feed)
Josh Cronemeyer (feed)
Josh Evnin (feed)
Joshua Graham (feed)
Julias Shaw (feed)
Ketan Padegaonkar (feed)
Kiran Bellubbi (feed)
Kraig Parkinson (feed)
Kris Kemper (feed)
Kristan Vingrys (feed)
Kurtis Seebaldt (feed)
Li Xiao (feed)
Liang Huang (feed)
Luke Barrett (feed)
Marc McNeill (feed)
Marco Abis (feed)
Mark Burnett (feed)
Martin Fowler (feed)
Matthew Buckland (feed)
Matthew Dunn (feed)
Megan Folsom (feed)
Michael Klynstra (feed)
Michael Schubert (feed)
Michael Ward (feed)
Mike Mason (feed)
Mo Li (feed)
Narla Keshav Ram (feed)
Neal Ford (feed)
Nicholas Bailey (feed)
Nicholas Carroll (feed)
Nick Drew (feed)
Niket Kumar Bhumihar (feed)
Nivetha Padmanaban (feed)
Ola Bini (feed)
Patric Fornasier (feed)
Patrick Farley (feed)
Patrick Kua (feed)
Paul Gross (feed)
Paul Hammant (feed)
Paul Ingles (feed)
Perryn Fowler (feed)
Peter F Ryan (feed)
Peter Gillard-Moss (feed)
Philip Calcado (feed)
Philippe Hanrigou (feed)
Pramod Sadalage (feed)
Prasanna Pendse (feed)
Prashant Gandhi (feed)
Premanand Chandrasekaran (feed)
Qihui Qin (feed)
Ram - Sriram Narayanan (feed)
Ranjan D Sakalley (feed)
Renee Ovcina (feed)
Reshmi Buthello (feed)
Richard Durnall (feed)
Ricky Lui (feed)
Rohan Kini (feed)
Rohith Rajagopal (feed)
Ross Niemi (feed)
Ross Pettit (feed)
Ryan Kinderman (feed)
Saager Suhas Mhatre (feed)
Sachin Dharmapurikar (feed)
Sam Newman (feed)
Santhosh Sagar Reddy (feed)
Shane Harvie (feed)
Shaun Jayaraj (feed)
Sidu Ponnappa Kariappa Chonira (feed)
Simon Brunning (feed)
Siva Jagadeesan (feed)
Srihari Srinivasan (feed)
Sriram Narayan (feed)
Srushti Ambekallu (feed)
Stacy Curl (Digital Compulsion) (feed)
Stephen Chu (feed)
Stuart Caborn (feed)
Sudhindra Rao (feed)
Sumeet Moghe (feed)
Suresh Harikrishnan (feed)
Suzi Edwards (feed)
Szczepan Faber (feed)
Tarek Abdelmaguid (feed)
Thamarai Poomalai Selvan (feed)
Thomas Czarniecki (feed)
ThoughtWorkers on Open Source (feed)
ThoughtWorks Studios (feed)
Tim Goodwin (feed)
Tomas Varsavsky (feed)
Troy Gould (feed)
Vincent Xu (feed)
Vivek Prahlad (feed)
Vivek Singh (feed)
Vivek Vaid (feed)
Wen Tao (feed)
William Hegarty (feed)
Xiaoming Wang (feed)
Xin Huang (feed)
Xuemin Guan (feed)
Ye Zheng (feed)
Yogi Kulkarni (feed)
__ThoughtBlogs-Admin (feed)

While taking computer science classes in college, I was taught to distrust a user’s input in all cases. The theory goes that a user’s data can’t be trusted because it could be malicious or just a simple mistake that causes your program to have an error with input it didn’t expect. So you protect your system from incorrect user input and sanitize it. It always felt like one of our dirtier secrets to me, however I fully advocate the practice in code.

My problem comes when the people building software turn from distrusting their user’s input to having a level of disdain for the users themselves. It starts innocently enough with the engineering principles I described above, but can sometimes turn into small things like, “Our users won’t understand that,” and starts to slip into things like, “Our users are dumb, so we won’t do that.” If you’ve started to hate your users, you’ve gone too far. If something is too complex for your users to understand, it’s your job as the developer/engineer/analyst/etc. to make it so that they can understand it. If you think of your users like idiots, your system will reflect that and they will notice.

If your job is to design software for people to use (which, is pretty much all software), make sure you work with the user, instead of against their best interests.

Steve rightfully asked for the source of the bad feelings we had about about adding a sequential return value feature to Mockito. The issue is that a Mockito stub is stateless, i.e. the value returned depended solely on the parameters being passed in when calling the stub. Statelessness is generally a nice property. ...
Martin wrote a blog a long time before: http://www.martinfowler.com/bliki/AnemicDomainModel.html. It was about domain model without rich behavior (anemic). Today, I am going to analyze why we have this problem, and try to give a elegant solution.



Let's give a example first. This is a task management system. Two entities in the domain, Employee, Task. So we can write the relationship as following codes:





public class Employee {

private Set<Task> tasks = new HashSet<Task>();

}



public class Task {

private String name;

private Employee owner;

private Date startTime;

private Date endTime;

}




It is a very typical parent/child relationship. Now, I want to add a behavior to my domain model. The behavior is: get all the processing task owned by a specified employee. If we ignore the existence of database, very naturally, this behavior belongs to Employee entity.





public class Employee {

private Set<Task> tasks = new HashSet<Task>();

public Set<Task> getProcessingTask() {

...

}

}




But if we do care the database. This design is not acceptable. Where can I get all my tasks? Are you going to load all my tasks when building the employee object? If we only have five tasks, that is OK. But if we have 5000 tasks, that probably is not acceptable. So, before the age of hibernate, we wrote:





public class TaskDAO {

public Set<Task> getProcessingTasks(Employee employee) {

...//sql

}

}




hmmm, wait a moment... Is DAO part of domain model. Yeah... you can. Just rename it to TaskRepository, then it is part of your domain model. Really? I don't believe it. DAO is not part of your domain model. Instead, it stole the logic from domain. It is the reason why our domain model is anemic. Because the getProcessingTasks was part of Employee, but now belongs to a DAO. Can hibernate solve the problem?





@Entity

public class Employee {

@OneToMany

private Set<Task> tasks = new HashSet<Task>();

public Set<Task> getProcessingTasks() {

...

}

}




yes! Hibernate rocks!

Have we succeed? No, not yet. Hibernate can make the tasks lazy-loaded. But you only have two options. Load, or not. If you are iterating tasks inside the impl of getProcessingTasks, you still end up as loading all the tasks from the database.



To solve this problem, many people tried many different ways. The goal was "injecting something" into domain, then domain can execute query itself. The attempts including using hibernate interceptor, static code instrument, aspectj... Spring gave a answer to this:





@Entity

@Configurable

public class Employee {

private TaskDao dao;

public Set<Task> getProcessingTask() {

return dao.getProcessingTask(this);

}

public void setTaskDao(TaskDao dao) {

this.dao = dao;

}

}




The @Configurable annotation was introduced to inject DAO into domain model. Now, the domain can do what it supposed to do. Really? domain model depending on DAO made lots of people unhappy. The argued, the cyclic dependencies between DAO layer and Domain layer. The argued, domain should not be "bound" with database or any container. I personally think, it is not that a big issue... I think RoR Active Record is bounding the domain model with database, people still love it. Anyway, I started again, and looking for a more elegant solution.



Finally, I found, what if I wrote this:





public class Employee {

private RichSet<Task> tasks = new DefaultRichSet<Task>();

public RichSet<Task> getProcessingTasks() {

return tasks.find("startTime").le(new Date()).find("endTime").isNull();

}

...

}




RichSet is a Set with extra capabilities (query, sum...)





public interface RichSet<T> extends Set<T> {

Finder<RichSet<T>> find(String expression);

int sum(String expression);

}




DefaultRichSet is pure in memory implementation of those operations by iterating the set. So you can new a Employee in your unit test, and test the getProcessingTasks right way. No need to worry about database or dependency injection. Do you feel better?



But, where is the database? Er... This is complicated, you know. The first thing I need to do is mapping the entity in Hibernate. Er... hibernate do not like it. Hibernate expect a Set, not RichSet. I think I need to write more things to make hibernate happy:





<hibernate-mapping default-access="field" package="net.sf.ferrum.example.domain">

<class name="Employee">

<tuplizer entity-mode="pojo" class="net.sf.ferrum.RichEntityTuplizer"/>

<id name="id">

<generator class="native"/>

</id>

<set name="tasks" cascade="all" inverse="true" lazy="true">

<key/>

<one-to-many class="Task" />

</set>

</class>

</hibernate-mapping>




What is tuplizer? It is used by hibernate to replace your set with hibernate enhanced set. So, I wrote my own tuplizer, and replace your set with my enhanced set.





public class RichEntityTuplizer extends PojoEntityTuplizer {

public RichEntityTuplizer(EntityMetamodel entityMetamodel, PersistentClass mappedEntity) {

super(entityMetamodel, mappedEntity);

}



protected Setter buildPropertySetter(final Property mappedProperty, PersistentClass mappedEntity) {

final Setter setter = super.buildPropertySetter(mappedProperty, mappedEntity);

if (!(mappedProperty.getValue() instanceof org.hibernate.mapping.Set)) {

return setter;

}

return new Setter() {

public void set(Object target, Object value, SessionFactoryImplementor factory) throws HibernateException {

Object wrappedValue = value;

if (value instanceof Set) {

HibernateRepository repository = new HibernateRepository();

repository.setSessionFactory(factory);

wrappedValue = new HibernateRichSet((Set) value, repository, getCriteria(mappedProperty, target));

}

setter.set(target, wrappedValue, factory);

}



public String getMethodName() {

return setter.getMethodName();

}



public Method getMethod() {

return setter.getMethod();

}

};

}

}




In short, the code means:





employee.tasks = new HibernateRichSet<Task>(...)





This version of RichSet is much smarter. It will translate your find statements from





tasks.find("startTime").le(new Date()).find("endTime").isNull();





--->





DetachedCriteria.forClass(..).add(...).add(...)





Now, in the domain, you can query against your collection without worrying about how the query will be done. Domain is still pure, no dependency on DAO. Domain is still all InMemory, no need to start up your container, your database to test domain logic.

Hot on the heels of the “Will Agile work for you?” presentation is our second code jam. In this session, Tom and I will dust off the projector and give a short demonstration on pairing with particular focus given to Test Driven Development. We’ll also try working under time constrained mini-iterations. Duly, we’ll supply the problem specification on the night so you don’t have any preconceptions. This session will be good for developers of all levels. If you are a seasoned TDDer come along and we’ll prepare something of a more challenging nature.

When: 7pm until whenever, Monday, 19th of May

Where: Gecko Lounge

Address: LG/F, Ezra Lane Lower Hollywood Road, Hong Kong

Directions: Gecko is on Ezra Lane which runs between Pottinger St and the Escalator. Ezra lane is below Hollywood Road. If you enter off Pottinger Street you will find the lane is just opposite Soda Bar. If you enter the lane from the escalator you will find it opposite the Hot Dog shop. If you have any problems finding it call the Gecko Lounge on: 2537 4680

Map:

Map of Gecko

On Github, in order to have your commits associated with your github user account, the email address you use when pushing code to the origin must match an email address associated with your github account. This is something I discovered after noticing that my commits were showing up as grey and not blue on the activity bar.

Setting your git email account is really easy. There are two different ways of doing it. First, you can directly edit your .git/config file and add a section like this:

[user]
	email = you@example.com
	name = Your Name

The second method is by using the following command from the root folder of your project:

git config user.name "Your Name"
git config user.email "you@example.com"

Many people can drink and write; there is, in fact, a grand tradition of engaging in both simultaneously. I am not one of those people1, and have spent much of the last year in this new city, job, and social circle engaged in much of the former and not enough of the latter. Even that cannot suffice to explain my extended absence from blogging; indeed, I must ultimately confess to having surrendered to the shallow charms of sloth. No more! Everything is now deployed, configured, and arranged in more or less the fashion that I desire. I have met the enemy, and he is me, and he is vanquished. Allow me to take a moment and put in an associate cheer for mod_rails.

I’ve also taken the opportunity to finally design my own custom theme. Many good and widely admired technical bloggers use stock or mostly-stock layouts. But it’s my little corner of the internet, and I can stumble blindly around Photoshop as well or better than the next poor sap when the need arises. Folly follows futility, however, with the knowledge that a large proportion of readers will simply consume this content through syndication and never stop to admire the elegant curve of my header, or its tacky nod to Web circa-2.0 fashion. In fact I haven’t even bothered testing it in IE6, and almost certainly never will; I am profoundly, almost fanatically indifferent to legacy browsers, even though their users are, being somewhat behind the technical curve, proportionately far less likely to consume this content through a feed reader and must thus endure my less-than-finely-tuned CSS.

I nonetheless submit that I am part of the solution, not the problem.

Frankly I haven’t tested it in IE7 either. I tried Firefox 3 and Safari 3 and then I got bored and went out for coffee. Either way, expect future blog entries to be more technical and perhaps less baldly narcissistic.

1 I’m young yet, though, and everybody needs a dream.

I think I first heard the phrase “the first bite is with the eye” from a TV chef, but it applies equally to the software creation process as it does to cookery.

A user’s interaction with a piece of software or web site is as much emotional as it is functional. Compare the soft, warm, fuzzy feeling you get when first interacting with a product from 37 Signals, say, to the stomach churning reaction you get when booting up Lotus Notes, for example.

This immediate emotional response will pervade the whole of a user’s long-term impression of a product, imbuing their relationship with whatever feeling was conjured up in those preliminary interactions. They say that in most job interviews the interviewer makes up their mind within the first 5 minutes. The same is equally true for software.

That’s the reason that I’m often accused of prematurely optimizing the UI. I’ve been round the block enough times to have been bitten by the following type of interaction:

Me: “This is just a wire-frame, we’re only trying to prove out the functional flows of the application. Don’t worry about the layout or the look and feel - that will be dealt with later.”

Client: “Ooh, I don’t like that font … and I think that’s our old logo. Maybe we should give it a drop-shadow or something. I’m disappointed, I expected more from you guys …”

After a host of similar experiences I now make sure that I give whatever product I’m working on an appropriate level of design and polish for the current stage, while still trying to evoke positive emotional responses. In my mind it’s always worth spending an hour or two tweaking a CSS file to tighten the layout, soften the lines and add a little sparkle - a little :hover goes a long way. Then even if the application isn’t functionally complete at least it looks professional and imbues the client with a sense of confidence and pleasure. This will put them into a much more positive state of mind, which can only be a good thing for the next stages of the project.

Call me shallow if you want, but when I arrive at a (studiously) un-styled site, like our guru Martin Fowler’s, I have a negative knee-jerk reaction making me resist reading the wise words he’s actually written. When, however, I land on a site where they care about the first bite I feel compelled to carry on reading all day.

If someone in a meeting has a bit of chopped herb stuck in their teeth it’s really hard to focus on what they’re saying - you get distracted and have a negative emotional response. In the same way drizzling some coulis and sprinkling a bit of chopped herb onto a plate before serving can provoke a positive emotional response which fools the diner into enjoying their food more.

I tend to write titles using additions and equals... Hmm... Guess I like simple math... Who knows? Anyway, I want to stress out today how cool I find Monorail right now! I always thought it was a cool framework, but after I wired my controllers through...(read more)
Thinking about simple rules in order of priority...



1. Available
- Availability is more important than anything else. Do not introduce things that reduce the overall reliability of the enterprise. This should lead to things like decentralised graceful degradation.

2. Easily verified - How are you going to test it? How are you going to test it when it's running (aka monitoring)?

3. Single Source of Information - Don't present contradicting data to customers.

4. Easy to Describe - The enterprise architecture should reflect the enterprise's structure. See Conway's Law. This works both ways. Change the architecture or change the organisation.

5. Least Number of Systems - Less stuff to deal with, monitor, and fail.



See also Design Beyond Human Abilities.

To say that I am seriously tired of JavaFX at this point, would be a gross understatement. So let's not even go there.



CommunityOne was a nice event. I like the feeling of it more and more, and the new open spaces approach seemed to be really successful. The alternative languages presentations were well attended and good. I recommend anyone in the Bay Area, or anyone attending JavaOne, to make the effort to go to CommunityOne next year. It's definitely worth it.



Tuesday was, for undisclosed reasons, a day where I didn't attend so many presentations. In fact, I missed both the keynote and Tom&Charlies JRuby talk. Bad on me. I did manage to go to both the technical session and the BOF about upcoming language features in Java 7. Let me say immediately that I don't want pluggable type systems as a part of Java. Yes, they are useful in certain settings, but they don't fit Java. Not at all. I'm all for having it possible to have annotations in more places, but not for type systems.



Wednesday was "my" day of the conference. Started out early with the Script Bowl, where Groovy, JRuby, Jython and Scala faced off in three different challenges. I was one of the three judges. It was a actually a great fun. Some people have gotten annoyed at it, but I feel that they are taking it too seriously. This format was a good way of introducing the audience to the capabilities of four languages that are sometimes very much alike and sometimes very different.



It's also interesting to note that Charles was the only one of the panel who solved his challenges by asking the community for help with it. In fact, Charles didn't do any of them himself, while the other three did their code in isolation. My opinion is that this shows a difference in attitude between the communities, and it's very interesting.



After that I took it easy for some time, and then it was time for my JRuby on Rails presentation. It went fairly well and was also well attended. I did a serious mistake in my database configuration, but Tom helped me out and the rest of the demonstrations was good.



I did a book signing session after that and then headed back to the office for a while before coming back and holding my JRuby at ThoughtWorks BOF which was also nice.



Today I'm going to take it easy and relax. I'll go to Nick's session in an hour and then I'll go to Josh and Brian's BOF tonight. After that we host a party which I'm going to attend for a while. It's going to be a nice day.

ECOWS 2008: The 6th European Conference on Web Services, November 12-14, 2008 in Dublin, Ireland

http://www.computing.dcu.ie/ecows08

The European Conference on Web Services (ECOWS) is the premier conference for both researchers and practitioners to exchange the latest advances in the state of the art and practices of Web Services. The main objectives of this conference are to facilitate the exchange between researchers and practitioners and to foster future collaborations in Europe and beyond.

The success encountered by the Web has shown that tightly coupled software systems are only good for niche markets, whereas loosely coupled software systems can be more flexible, more adaptive and often more appropriate in practice. Loose coupling makes it easier for a given system to interact with other systems, possibly legacy systems that share very little with it.

Web services are at the crossing of distributed computing and loosely coupled systems. When applications adopt service-oriented architectures, they can evolve during their lifespan more easily and better adapt to changing or unpredictable environments. When properly implemented, services can be discovered and invoked dynamically using non-proprietary mechanisms, while each service can still be implemented in a black-box manner. This is important from a business perspective since each service can be implemented using any technology, independently of the others. What matters is that everybody agrees on the integration technology, and there is a consensus about this in today's middleware market: customers want to use Web technologies. Despite these promises, however, service integrators, developers, and providers need to create methods tools

and techniques to support cost-effective development and the use of dependable services and service-oriented applications.

Topics of Interest

The ECOWS 2008 program committee seeks high quality papers related to all aspects of Web Services, which constitute the current main

technology available for implementing service-oriented architectures and computing. Topics of interest to the Research Track include, but are not limited to, the following:

  • Life-Cycle of Web Services Implementations
  • Dynamic Web Services
  • Semantic Web Services
  • Economics and Web Services
  • Quality Requirements for Web Services
  • Web Services for Grids
  • Web Services in a Service-Oriented Environment
  • Web Services and Mobility
  • Frameworks for Building Web Service-Based Applications
  • Formal Methods for Web Services

Research Paper Submission Guidelines

We solicit papers with a maximum of 10 pages, containing new material. Submissions must be in English, must be original, and must not have been submitted for publication elsewhere. There will be an award for the best paper and best student paper. Submissions should be made through the website.

As in the previous years, we plan to publish the ECOWS 2008 Proceedings with IEEE Computer Society Press. Technical papers must be conform to the IEEE paper guidelines which will be made available at the ECOWS 2008 web page. Submit your paper in PDF or Postscript format via the electronic submission system which will be made available via the ECOWS 2008 web site.

Deadlines

Abstract Submission May 18, 2008

Electronic Paper Submission May 25, 2008

Acceptance Notification July 7, 2008

Camera Ready Version August 4, 2008

One of the things that constantly surprises me about facilitating retrospectives is about the energy that a well run session can result in. For most heartbeat retrospectives, I feel it’s not normally that useful to write up a comprehensive report, as the team should feel ownership of the action items.

An important aspect to the role of the facilitator, is to do as much as they can to sustain the energy of the group and to tap into everyone’s capacity for embracing and dealing with change. Helping people contribute their story to the retrospective helps. Letting people tell their story in full helps. Facilitating difficult conversations towards a non destructive outcome helps. Moving the team towards specific, tangible actions or concrete lessons learns helps.

After the retrospective, I’ve always wondered what responsibility the facilitator has for ensuring change. My conclusion is that, in reality if they are truly independent, it’s none. Of course, the facilitator may care (and I can assure you I do) about following through on the change, yet all the systemic forces that push for and against change tend to be out of the influence of a truly independent facilitator.

In short, retrospectives are agents for change, yet ultimately it comes down to the empowered team to make sure the changes really happen. My advice to managers is to give teams responsibility and, with that, the decision making authority, to help them make the changes they need to.

Dirty object functionality has been in edge rails for a month now, allowing you to see what changes have been made to an object prior to saving it. Here's an example from Ryan's edge rails blog -
article = Article.find(:first)

article.changed? #=> false



# Track changes to individual attributes with

# attr_name_changed? accessor

article.title #=> "Title"

article.title = "New Title"

article.title_changed? #=> true



# Access previous value with attr_name_was accessor

article.title_was #=> "Title"



# See both previous and current value with attr_name_change accessor

article.title_change #=> ["Title", "New Title"]
Dirty objects should be available with the next release of Rails. Unfortunately, you need it in your Rails 2.0 project right away and can't wait. Well, you're in luck; adding this functionality to a Rails project turns out to be dead simple thanks to Dirty being a nicely decoupled module.

Here's what you need to do:
  • Download the source for dirty.rb from http://dev.rubyonrails.org/browser/trunk/activerecord/lib/active_record/dirty.rb?rev=9127. I'm specifically picking revision 9127 because later versions incorporate partial updates (also new in edge rails), which complicates matters.
  • Once you've got the file create a directory called active_record under RAILS_ROOT/config/initializers and copy dirty.rb into it. Thus, if your project is under /home/work/myproj, the path to dirty.rb should be /home/work/myproj/config/initializers/dirty.rb.
  • Reopen active_record and mixin Dirty. Do this by creating a file called active_record.rb under RAILS_ROOT/config/initializers and putting the following bit of code in it:
    require "#{File.dirname(__FILE__)}/active_record/dirty"



    ActiveRecord::Base.class_eval do

    include ActiveRecord::Dirty

    end


There, you're all set. You can verify that Dirty actually works by adding the following spec to your suite.
require File.dirname(__FILE__) + '/../spec_helper'



class TestModel < ActiveRecord::Base

end



describe 'Models with Dirty enabled' do

before(:all) do

connection = ActiveRecord::Base.connection

begin

connection.create_table(:test_models) do |t|

t.column :name, :string

t.column :age, :integer

end

rescue Exception => e

puts "Error #{e} when creating test table"

end

end



it "should mark new (unsaved) objects as changed" do

TestModel.new(:name => 'Ooga', :age => 15).should be_changed

end



it "should mark all fields of a new (unsaved) object as changed" do

t = TestModel.new(:name => 'Ooga', :age => 15)

t.name_changed?.should be_true

t.age_changed?.should be_true

end



it "should mark newly created objects as unchanged" do

TestModel.create(:name => 'Ooga', :age => 15).should_not be_changed

end



it "should consider objects retrieved from the database to be unchanged" do

TestModel.create(:name => 'Ooga', :age => 15)

TestModel.find(:first).should_not be_changed

end



it "should know when an object is dirty" do

t = TestModel.create(:name => 'Ooga', :age => 15)

t.age = 5

t.should be_changed

end



it "should know when a field is dirty" do

t = TestModel.create(:name => 'Ooga', :age => 15)

t.age = 5

t.age_changed?.should be_true

end



it "should mark an object as clean after a successful save" do

t = TestModel.create(:name => 'Ooga', :age => 15)

t.age = 5

t.save.should be_true

t.should_not be_changed

end



after(:all) do

drop_tables(ActiveRecord::Base.connection, :test_models)

end

end


Note that Rails automatically loads files under config/initializers - you can put these files elsewhere in your project, but make sure you tell Rails to pick them up and that the paths are alright. It is also possible that Dirty Objects may break some plugins, acts_as_audited being a case in point.

Dan Manges has a nice write-up on why the factory pattern is better than Rails fixtures: Rails: Fixin’ Fixtures with Factory. The factory is used to create valid objects for testing with default values for all of the fields. These objects can be used in tests without cluttering the test with attributes we do not care about.

On my current project, we like the factory pattern but favor a slightly better syntax. We wanted to replace:


Factory.create_paperboy

with


Paperboy.build!

We use the build method (for lack of a better name) to create test data. We are calling a class method on Paperboy in order to create an instance, which seems more consistent with object creation in ruby than calling a method on a Factory class:


Paperboy.new
Paperboy.create
Paperboy.build!

In addition to build! (which is like create!), we added a build method which creates the object without saving it. We put our code in a file called factory.rb (which we require in spec_helper.rb) that looks like:


module Factory

  def self.included(base)
    base.extend(self)
  end

  def build(params = {})
    raise "There are no default params for #{self.name}" unless self.respond_to?(self.name.underscore)
    new(self.send(self.name.underscore).merge(params))
  end

  def build!(params = {})
    obj = build(params)
    obj.save!
    obj
  end

  def customer
    {
      :first_name => "Joe",
      :last_name  => "Guy",
      :paperboy   => Paperboy.build,
    }
  end

  def newspaper
    {
      :customer => Customer.build,
      :headline => "Read all about it!",
      :paperboy => Paperboy.build,
    }
  end

  def paperboy
    {
      :first_name     => "Paper",
      :last_name      => "Boy",
      :delivery_route => "Main St Route" 
    }
  end  
end

ActiveRecord::Base.class_eval do
  include Factory
end

The build method uses the class name to find the default params, which are defined as a method per class. Then, it merges any user supplied params and creates the object. Now, when we see test code that looks like:


Paperboy.new :first_name => "some", :last_name => "person" 

we can replace it with:


Paperboy.build

or, if one of the fields is important for the test:


Paperboy.build :first_name => "joe" 

It is easy to swap the word “new” or “create” for “build” or “build!” and then delete the params that we do not care about.

Alan McClellan must be one of the calmest persons I've met. Conversations with him are thought provoking. He asks you a question and ask you answer that question, you are forced to think and rethink. I'm certain now that this in itself compells technical people to give him good responses.



Michelle Olson, meanwhile, will be moving on to take over other responsibilities.



The Docs group at opensolaris.org has done great work so far, and I'm excited to help out fixing things that need fixing and writing things that need writing :)

When using RubyGems from the command line, I almost always type sudo gem i synthesis as opposed to sudo gem install rails, the emphasis targeted at using “i” instead of “install”, of course. The gem executable happily understands what command it is being asked to execute when provided with the first few letters of the command, as long as those letters are not ambiguous, i.e. don’t clash with the names of other commands. So even though sudo gem u foo complains that Ambiguous command u matches [uninstall, unpack, update], sudo gem uni foo will uninstall the specified gem.

Here’s how this is implemented in RubyGems.

def find_command(cmd_name)
  possibilities = find_command_possibilities(cmd_name)
  if possibilities.size > 1
    raise "Ambiguous command #{cmd_name} matches [#{possibilities.join(', ')}]"
  end
  if possibilities.size < 1
    raise "Unknown command #{cmd_name}"
  end

  self[possibilities.first]
end

def find_command_possibilities(cmd_name)
  len = cmd_name.length
  self.command_names.select { |n| cmd_name == n[0,len] }
end

In the same vein, although not strictly a command abbreviation, Danilo pointed out git understands abbreviated revision hashes, so it’s possible to use something like git diff d0a..HEAD even with the hash’s complete representation being d0aa7dd4aa9a95090df1e0b9d0f426d5a5bd56ae.

Less typing is almost always a good option to have. The easy to implement Unambiguous command abbreviation trick adds a subtle usability improvement to command line interfaces and holds a nice treat to the utility’s power users.

Some (actually quite a long) time ago, Sczcepan asked if private methods are an anti-pattern and I've been promising him ever since to post a reply. The reasoning behind his statement was derived from the fact that TDD purists often maintain that untestable code is very likely bad code. Private methods are hard to test and it therefore seems to follow naturally that they, too, are a sign of bad code. While I'm sure that this is true in some cases, I think there are a lot of good reasons to have private methods or classes.



Implementation vs. Behavior



Many developers focus too much on implementation details when they write tests. Ask yourself what you really care about a class: implementation details or behavior? It turns out that in most cases, one is only interested in the latter. So when writing tests, we should focus more on the public API of a class and not about its internal structure.



Modularization vs. Encapsulation



In summary, I agree with Sczcepan when he says that there is probably a lot of code out there in which private methods could be factored out into public classes or methods which would, in turn, increase code modularity. Often, however, encapsulation and a lean public API might be favored over modularization. More importantly, modularization might not always be needed, and therefore complicate and bloat the codebase unnecessarily. If modularization is still required at a later stage, internal code can be refactored and made public without breaking existing code.



Are You Over-Mocking?



Stubbing and mocking techniques and tools, especially Mockito, are great and make perfect sense in many situations (e.g. faking a network connection, a database, a service etc.). Breaking up a public method that consists of n private methods into n public methods of n new classes only so that we can test that the methods are indeed invoked, however, introduces unwanted complexity and completely breaks encapsulation principles, without yielding any real benefits.



Don't Defend Yourself!



Private methods and classes are also a good thing because they allow to code non-defensively, which will make code simpler. As soon as logic is offered to the world as a public method, one needs to ensure that the implementation also caters for unexpected scenarios. If the implementation is private, on the other hand, it is easy to determine that a certain variable will never be null, for example, and can thus be safely ignored.



Public APIs are Contracts



The API of a class (i.e. it's non-private methods) represents a contract with whoever is going to use it. Sure, as long as the class is used only within a single source repository this might not be a big issues, because powerful refactoring tools make it easy to change the contracts. But what happens if code is being used externally - maybe because other people find it useful, too? It won't be possible to change public method signatures that easily anymore, because other code will have been built on top of them.



Reduce Public API complexity



Private methods and classes can greatly reduce the complexity of a public API. Compare the Java API and its source code, for example. You'll realize that a lot of complexity is encapsulated in private methods and classes, which is a good thing, because it keeps the public APIs complexity low(er(-ish)).



To sum up...



Although these are very much thoughts-in-progress, I think that in summary:

  • We focus too much on implementation details and not enough on externally observable behavior of a class when writing tests
  • We often create code that is unnecessarily modular, because we are test-obsessed
  • We often mock too much (probably as a consequence or mutual reaction of the above two points)
  • We often create APIs that expose too much and therefore break encapsulation
It's been only 2 days after I first implemented my new exception throwing mechanism , and I already feel an enormous difference. It's not just having the exceptions with better messages per se, it's actually having to think why I'm throwing that exception...(read more)

Before joining my current project I spent about 4 months working with Ruby every day, the first time I’d done so for a few years. It was a glorious time: uncluttered syntax, closures, internal iterators, and with open classes, the ability to extend the ‘core’ at will.

Today I’m working with C# and .NET, and I’ve noticed that those 4 months with Ruby have changed the way I’ve been writing code. Most noticeably, I’m using anonymous delegates a lot more. But that’s not all.

I’ve found myself aching to use the List<T>’s ForEach method; I’m now wired to use Ruby’s internal iterators where instead of

List<Person> people = FindAllPeople();
foreach (Person person in people) {
  ...
}

I can instead do

List<Person> people = FindAllPeople();
people.ForEach(delegate(Person person) {
  Console.WriteLine(person.Name);
});

But, frequently I’m put off by the surrounding guff that’s needed to express the same and have almost always gone back to the more traditional external iterator-based approach. It’s simply too high-a-price to pay.

One of the largest smells I’ve noticed recently (to my mind) appears driven out of not having open classes and external iterators. If they were there, I’m sure people would use them. The result: all across the codebase, whenever you need to convert from type to another you’ll see

List<Person> people = FindAllPeople();
List<String> firstNames = new List<String>();

foreach (Person person in people) {
  firstNames.Add(person.Name);
}

This smells to me. But it really, really smells from having used Ruby where I would previously have written something as succinctly as this:

find_all_people.collect {|person| person.name}

(I’m sure other languages could do equally good things- but I’m familiar with Ruby, before the Pythonists pounce :p)

Well, turns out that you can get nearly there with C# 2.0 and .NET 2.0 with the almost certainly underused ConvertAll method (also part of List<T>).

List<Person> people = FindAllPeople();
List<String> names = people.ConvertAll(delegate(Person person) {
  return person.Name;
});

There’s still a fair bit of accidental complexity remaining- lot’s of delegate and type declarations.

C# 3.0 introduced lambda expressions and we can use that to bubble our soup down to a nice intentional broth even more. We can get rid of the delegate bumpf and let the compiler infer the type (we are still statically typed after all)

List<Person> people = FindAllPeople();
List<String> names = people.ConvertAll(person => person.Name);

Next step, we can also infer the types for our local variables:

var people = FindAllPeople();
var names = people.ConvertAll(person => person.Name);

Pretty nice. Most of the code is focused on the task at hand, and on expressing the necessary complexity (what it means to convert people to names). Guess learning a new language each year has it’s benefits.

I’ve got another bit of Ruby influenced C# refactoring to cover (a somewhat declarative way of removing switch statements), hopefully I’ll get that posted tomorrow!

I got a referral to my blog from a Google query a few weeks ago which made me chuckle. Then a couple of days ago I got a few more and I felt I just had to figure out what was so wrong about Guerrilla SOA (having invented the term and all that, I feel quite paternal!). Well Google doesn't see anything wrong with it, in fact Google suggests that really Guerrilla SOA (is) working.

guerrilla-soa-working

Lovely!

Looks like my feedback had been taken into consideration after posts from me and Sidu. A cleartrip.com representative has told me that cleartrip.com is willing to bare half the cancellation charges. This saves me 4000/-. I will post more details about this after my vacation.



Tomorrow I'm leaving for my vacation to Ladakh. Unfortunately I had to cut short my vacation because my plans went haywire after this "cancellation episode".



I'm biking between Leh - Kargil



My Itinerary



Bangalore - Delhi Flight

Delhi - Leh Flight

Leh - Kargil Thunderbird

Leh - Delhi Flight

Delhi - Hyd Flight



More details and pics when I survive :-)

Good to see Steve blogging again.

Quoting Cleartrip's about section, "Cleartrip is dedicated to making travel simple". So simple that you can easily buy tickets worth thousands of rupees. And then lose thousands of rupees by clicking the 'Cancel Booking' link by accident.

ClearTrip is so easy to use that it apparently doesn't believe in confirmation dialogues - your booking will simply be canceled. Cool, what? The good Mr. Narla just lost Rs. 8000/- this way. That's what I call usabilty in action.



Update

Looks like cleartrip.com isn't bad at all once you get past the call centre. Narla contacted them via the feedback section of their website and this elicited a very quick response. They've been exceptionally helpful and have offered to refund Rs. 4000, half the cancellation charge due. This is pretty decent of them, keeping in mind the fact that cleartrip.com cannot recover the cost (it's already been canceled at the airline). Good stuff!

We had a weird requirement on our project recently..

Find all the Rows in All the tables that do not comply with the Constraints that we have in development but not in QA environments

Best way to do this we thought was to write a SQL statement against the table for each column that was going to have a Foreign Key constrained column and find out what data was not right or did not match the constraint. For example: If we have a INVOICE table that has a ITEMID on it. I want to find all the rows in the INVOICE table that have a ITEMID that does not exist in the ITEM table. Writing this SQL for our 400+ tables database was going to be huge task.

Oracles (or for that matter any databases metadata) metadata to the rescue and we ended up writing a SQL that would generate our above SQL.

here is the SQL that generated the above SQL

SELECT 'SELECT '''||table_name||'-'||column_name||''', count(*) FROM '|| 
table_name|| ' WHERE not exists (select 1 from '|| remote_table ||' where '||
remote_table||'.'||remote_column||' = '||table_name||'.'||column_name||') AND '
||table_name||'.'||column_name||' IS NOT NULL
UNION ALL' 
FROM (
   SELECT a.table_name,
      column_name,
      ( SELECT table_name FROM user_constraints 
      		WHERE constraint_name = a.R_CONSTRAINT_NAME) remote_table, 
      ( SELECT column_name FROM user_cons_columns 
      		WHERE constraint_name = a.R_CONSTRAINT_NAME) remote_column 
   FROM user_constraints a, user_cons_columns b 
   WHERE a.constraint_name = b.constraint_name 
   AND a.constraint_type = 'R' )

This SQL generates SQL that when run will give us data about tables that do not match our constraints requirements. If you have a CUSTOMER table which has CUSTOMERTYPEID and STATUSID on it, then the SQL generated would be.

SELECT 'CUSTOMER-CUSTOMERTYPEID', COUNT(*) FROM CUSTOMER  WHERE NOT EXISTS 
      (SELECT 1 FROM CUSTOMERTYPE WHERE 
             CUSTOMERTYPE.CUSTOMERTYPEID = CUSTOMER.CUSTOMERTYPEID) 
  AND CUSTOMER.CUSTOMERTYPEID IS NOT NULL
UNION ALL
SELECT 'CUSTOMER-STATUSID', COUNT(*) FROM CUSTOMER WHERE NOT EXISTS 
       (SELECT 1 FROM STATUS WHERE 
             STATUS.STATUSID = CUSTOMER.STATUSID) 
  AND CUSTOMER.STATUSID IS NOT NULL

Once the above SQL is run, the results will show us data that does not match the constraints we want to introduce into the QA environments.

Hmmm, as pinpointed by my good friend Claudio Figueiredo , the compiler isn't very thrilled with my last syntax : 1: public string MyCode(){ 2: try { 3: return "some value" ; 4: } 5: catch (System.Exception ex){ 6: Throw 7: .WithMessage( "something" )...(read more)
Johanna Rothman over at Hiring Technical People has recently blogged a colleague's comments about the benefits of befriending those of the Recruiting persuasion. Although being aimed more at those recruiters working in Agencies I'm not 100% sure I agree with them all. 1. Some of the best jobs / candidates are rarely advertised This is largely true. When I used to work in an agency often we

In Jakob Neilsen’s How Little Do Users Read? he sites an ACM study that has found that people typically only read about 20% of content on a page on average, with a max of around 28%.

This just confirms my suspicion that developers fully don’t read the stories that I write for them, even though they are highly focused and relevant to what they are looking for. Instead of reading, they skim and look at the screenshots I provide.

What can we take away from this? Treat stories as a conversation point, rather than a full design spec. If your devs have to read thousands of words in your stories, they are too big. Talk to your team and make sure people understand what you are trying to do.

At 130 words above, most people have read only 26 words in this article, meaning they’ve barely read the first paragraph. Scary.

An index page comes with Rails scaffolding. It is used to show a list of the "thing" you are CRUD-ing on. However, all too often we are tasked to show some pages that we aren't doing any CRUD operations on. Just a few examples:

  • Dashboard page after user logs in


  • The "Forgot your password?" page


  • Tabs or subtabs of multiple lists


  • List page showing multiple lists of different entities


A normal dashboard page isn't something you would normally CRUD on. It is more like a place holder page that a user sees the first thing after s/he logs in, showing many items of all sorts valuable to a user. In many Rails app, depending on what those items are, the dashboard page will end up being rendered by one of the items' index action. Worse yet, next time when you actually need that index page by that model, you have to call it something else, maybe 'list', and then play with the routes to get it wired up correctly.



class CustomersController < ApplicationController



# The dashboard page

def index

@customers = Customer.find :all

@products = Product.find :all

@tasks_of_the_day = Task.find :all

end



# A list of customers

def list

@customers = Customer.find :all

end



end



ActionController::Routing::Routes.draw do |map|



map.resources :customers, :products, :tasks

map.with_options :controller => 'customer' do |r|

r.dashboard '/customers/list', :action => 'list'

end



end




Here's a suggestion: how about put it in views/dashboards/show.html.erb, and, while you are at it, give it a DashboardsController? Then, put it in your routes.rb as a (singular) map.resource :dashboard, like such:



class DashboardsController < ApplicationController



def show

@customers = Customer.find :all

@products = Product.find :all

@tasks_of_the_day = Task.find :all

end



end



ActionController::Routing::Routes.draw do |map|



map.resources :customers, :products, tasks

map.resource :dashboard



end




By rendering the dashboard page in a completely different controller, you now have a very readable GET dashboard_path named route (GET: http://localhost:3000/dashboards), and you will not contaminate the index action of your other models' controllers with instance variables of all kinds. You also have a more readable routes.rb file.



One of the examples above for non-RESTful pages is the "Forgot My Password" page. Can you think of a good way to do it the Rails REST-ful way? Please go to Seeing Rails Resources Clearly to share some of your thoughts.

Update: So it turns out iPhones *do* have a way to turn it off but its just that I at least never read the manual (because you don’t need to) and didn’t discover it. My main point is still interesting I think, that you don’t need it enough to want to find it. As usual I will leave my mistake here for the word to read. :-)

Apple get usability. By now there must be very few people who don’t realise that. But in case there are some out there, lets just look at a couple of examples that struck me recently.

I own an Apple iPhone. I bought it in San Francisco, unlocked it myself, and have been using it constantly for the last month. It is without question the best phone I have ever used. Not perfect, for all the (3G etc.) reasons that you can read about on the blogsphere, but it is pretty damn close, and certainly closer than any other I have owned, including my abortive attempts to get to like “smart phones” in the past (Palm, Nokia etc).

But it struck me after a month of using the thing that there is a glaring missing feature. In fact it is so obvious I’m astounded that I haven’t noticed it before. There is no ‘off’ button.

There is a button on the top right that you press to lock it - the screen turns off but little more.

There is also a ‘be quiet’ button on the left side, above the volume control. Hit it, and the phone gives a little shake and then remains silent. This is equivalent to choosing the ‘Silent’ profile on a Nokia or other phone.

The most amazing thing about the lack of an ‘off’ switch is that you never notice it. Hey, it took me a month. And when do notice, and start to think about it, you realise that you never actually needed an off button anyway. The only reason that I used it on my old phone was because I was going into a meeting, and I wanted to make sure I wasn’t disturbed.

But this shows the reason that Apple doesn’t need it. With my old Nokia, ‘Silent’ was a profile, and I had to trust that the people who created that profile did the right thing, and turned all the sounds and notifications off. That they didn’t think “You know, this feature that I’m working on is really *really* important — no-one would *ever* turn it off — so I’ll give an ever so subtle beep even in Silent mode”. I don’t trust anyone to do that. I don’t trust *myself* to do that if I’m developing software.

So what Apple does is say: “No, this isn’t an option, its not a profile that you can customise. No questions asked — hit this button and I won’t disturb you. Not at all. Never”. It is still a trust issue. But I trust that button. I trust it because it doesn’t feel like a software option — the button implies a hardware solution — a built in switch that kills the sound. (Of course I understand that in reality it is software, but trust is conveyed by the visceral physical click of that button).

Apart from wanting to make it silent, why would you ever turn off your mobile? These days turning off a mobile is like locking yourself in a dark room and calling out to your friends that you aren’t coming out. Life — or at least social life — stops [1]. So the Apple iPhone has no off button.

Another shorter example. I am writing this sitting outside the Apple store in NY. You know the one, the glass box on 5th Avenue. Every couple of minutes someone takes a photo of it. It is beautiful, ad the plaza that surrounds it is comfortable and popular and full of people sitting eating lunch. A shop that gives something back. Nice.

Inside the store, it is very busy, very crowded. Apple stores have the highest per foot profitability of any store here. But it is still cool and comfortable and a pleasant place to be. But it is quite noisy. So how do you sell iPods, which are ‘all about the music’ as Steve Jobs likes to say in a noisy environment?

I don’t know how other companies would do it, but what Apple do is load all the iPods on display with Bose sound cancelling headphones. These are the $250+ headphones that you see long distance travellers (like me) using on long haul flights to cut down the airplane noise and get some sleep [2]. Despite the noise in the store you really can hear the music, and the chaos of the store disappears when you put them on.

This is the attention to the user experience that has made Apple what it is. It is pleasant to visit an Apple store. It is pleasant to buy an Apple product. It is pleasant to use an Apple product. And it is why the Apple iPhone doesn’t have an off button.

[1] While writing this it struck me that the same is true of my laptop. I never switch it off, I just close the lid. And that makes me wonder if we will someday see a laptop from Apple that has no off switch?

[2] They are using the AC3 ones that sit on the ear, and I guess that is because a used over the ear headphone might feel unsanitary, whereas the on the ear design leads to less actual contact. They are not the battery operated ones though, I don’t know what exact model they are.

http://www.guardian.co.uk/travel/iraq



In my training career, one of the wishes I've seen a lot of professionals express is; "I want the training to be as close to reality as possible". While I appreciate the intent, its often important to understand that there are various reasons why a training environment should not model reality a 100%. Here's why I think so:



a) A "real" environment generally has a mix of experienced and inexperienced individuals. Trying to mimic that environment with a group of inexperienced folks does NOT model reality.



Note: A simulation is meant to be a ground to fail fast and learn from mistakes. The trainer brings in his/ her experience into such an environment through briefs/ debriefs so that students can learn by way of a semi-controlled activity.



b) A "real" environment gives each participant at least some time to prepare/ plan for the activities that follow. When students have no idea of what's coming up, they have no way to either plan or prepare.



Note: To run an effective simulation, the trainer should be willing to do the background work for the students (preparation for client meetings, deciding agenda, collecting material, etc)



c) A "real" environment could at times involve a mix of roles which could potentially be absent in the classroom. In absence of these roles, students could miss out on perspectives they'd usually have during the real situation.



Note: To ensure that students dont miss out on different perspectives, the facilitator should plan the debriefing sessions to point out the areas that students didnt think about. This will help them relate back to the mistakes they made and reinforce their learning.



Things to remember about Simulations:

  • Please, please, please have a plan ON PAPER. Try to have a logical sequence to the activities that you wish to conduct, with enough time for briefing and debriefing.



  • Make yourself available at all times to provide guidance and to facilitate the process.



  • If you have volunteers helping you out in the simulation, make them understand their role and give them enough information to do it exactly the way you'd imagine.



  • Be clear about the learnings you wish to drive out as part of each activity and rehearse how you'd bring these out during the debrief.



  • Please, for heaven's sake, dont take "reality" to the extreme. Always remember, if your students were ready for reality, they wouldn't be in your classroom!



One of the challenges of facilitating retrospectives in a training environment is that it inadvertently becomes the trainers' prerogative to act on action items. Its useful to remember that retrospectives are for the team and the trainer is just another member in the team. It therefore is important that all learners contribute to improving the learning experience.



Often it so happens that there's just a handful of people keep volunteering to drive action items. One of the tools I find useful to drive out action items is the "Who", "What", "When". I just draw up a flipchart with those headings and try to break up each action item into those fields. As a result, if someone has way too many action items against his/ her name, it shows up. Unrealistic deadlines are immediately visible too. I find this as a useful way to split responsibility and take action items to completion.

Indiana Jones and the Temple of Doom, wherein a man has his heart ripped out of his chest, a