I just read Situated Learning: Legitimate Peripheral Participation by Jean Lave and Etienne Wenger, which I put on my wish list since I am a total geek about theories of how people learn, particularly with regard to social learning. It’s a challenging read with lots of big words, but it short, fun and well-worth it. The approach to thinking about learning resonated with me. Naturally, I saw applications for human interface design (perhaps a subject for another post), but unexpectedly, I found that I recognized this concept of learning in my own experiences of open source development.

The stereotype of software engineering is that it is a solitary profession. The alpha geek hovers over his keyboard mind-melded to the machine applying his gifts for computation unhindered by his near autistic lack of social skills. While I’ve heard that such lone coders do exist, I more often see programmers interact in the virtual social setting of mailing list, forum or blog where the masters and the newbies interact with mutual respect and shared enthusiasm. It strikes me that open source projects provide a mechanism for what Lave and Wenger call “legitimate peripheral participation” which describes a specific and effective learning style.

Legitimate peripheral participation is a mouthful, but the authors make a good case for that as an effective description. They argue that teaching is not central to learning, but that we learn when we effectively (“legitimately”) participate. As a novice, peripheral participation is a way to participate before gaining complete skills and confidence, and sometimes the recognition of the community which is required for full participation. This learning is the journey from novice to master; however, they argue that “mastery resides not in the master but in the organization of the community of practice of which the master is part.” (p.94)

The book looks at five different systems of apprenticeship, including one that is ineffective, and reflects on successful learning techniques which do not always include teaching or even the intent of the apprentice to learn a specific curriculum:

… apprentices gradually assemble a general idea of what constitutes the practice of the community. This uneven sketch of the enterprise … might include who is involved; what they do; what everyday life is like; how masters talk, walk, work, and generally conduct their lives; how people who are not part of the community of practice interact with it; what other learners are doing; and what learners need to learn to become full practitioners. It includes an increasing understanding of how, when, and about what old-timers collaborate, collude, and collide, and what they enjoy, dislike, respect, and admire. (p. 95)

I have been experiencing this kind of legitimate peripheral participation lately on the RSpec mailing list. The specific content is less significant than the pattern of interaction that I have seen repeatedly in open source projects. In this case, I set out to learn Ruby on Rails, and decided to start using the cucumber behavior-driven development framework, which is an offshoot of RSpec. There on the RSpec list, live both Ruby novices and masters. One such master, Aslak Hellesøy, has created the cucumber framework. As a novice Ruby developer, I can see how he interacts with the other masters and with their work (incorporating other open source projects). Also, through my postings, I interact with other novices and we learn both from each others and from the very experienced members of the community.

It seems like the Lave and Wenger could be talking specifically about open source software projects when they say that “communities of practice are engaged in the generative process of producing their own future.” (p.57-58) Of particular note is the observation that “learning involves the construction of identities.” (p. 53) When we learn something new, particularly a new skill that enables us to act masterfully as a member of a community, part of our identity is defined by that skill. It reminds me of Derek Sivers who said that programming languages are like girlfriends: the new one is better because *you* are better. He observes that when you learn a new language, you learn a new way of thinking and new methodologies that then can be applied to any programming language. The community of practice stretches beyond the Rails community.

The book introduces the radical notion that little or no formal teaching needs to take place for learning to happen. In this kind of community, the typical teaching moment emerges from a specific challenge that a newbie encounters and in the telling of stories.

…researchers insist that there is very little observable teaching; the more basic phenomenon is learning. The practice of the community creates the potential “curriculum” in the broadest sense — that which may be learned by newcomers with legitimate peripheral access. Learning activity appears to have a characteristic pattern. There are strong goals for learning because learners, as peripheral participants, can develop a view of what the whole enterprise is about, and what there is to be learned. Learning itself is an improvised practice: A learning curriculum unfolds in opportunities for engagement in practice. (p.92-93)

When I set out to learn by developing my own tutorial, I was repeating a common practice in an open source programming community. Lave and Wegner describe the notion of a “constructively naive” perspective where “inexperience is an asset to be exploited… in the context of participation, when supported by experienced practitioners who both understand its limitations and value its role” (p. 117) The development of a tutorial is a recognized form of contribution in the open source world. It’s accurately described as peripheral participation because you are generally developing something very basic, which has little or no purpose other than to explore the language or framework; however, that participation is legitimized by comments from more experienced engineers, and, in this case, the author of the framework itself. Discussion on the list provides an opportunity for learning beyond the initial context of the tutorial and inspires story telling from from which novices learn coding patterns and methodologies, rather than merely syntax.

An interesting aspect of open source is that it creates natural communities of practice, enabling practitioners to easily immerse themselves in an effective learning environment. Software is a field which requires almost constant learning of new skills — the specific skills of new language syntax, installing and executing new tools along with new methodologies and programming “patterns.” I may be a novice at developing Ruby on Rails applications or drafting Cucumber “features”; however, I’m an experienced C and Javascript programmer. Spending time in a specific community and assigning myself an introductory project creates an effective apprenticeship, then later as a full participant, I continue to learn about that specific technology and be introduced to new tools and techniques merely by continuing to participate in the community.

Now that we have learned some basic Ruby syntax and gained some understanding about what the Rails generate scaffold script does, it is high time we started using a more modern approach to coding. In fact, if you recall at the end of day 2, I realized with horror that we had actually modified code and added features without developing the tests first. This defied everything I had ever heard about good coding practices from the Ruby crowd and I set off to mend my ways.

Rick Denatale describes the process of test-driven/behavior-driven development as:

  1. Write the test/spec
  2. Ensure that it FAILS
  3. Write the code to make it pass
  4. Goto step 1

After reading a bit about test- and behavior-driven development, I decided to use a relatively new framework called cucumber which uses natural language to describe features.

Today we will:

  1. Install cucumber
  2. Set up the application
  3. Describe a feature
  4. Execute the feature and Watch it fail
  5. Write the code to make it pass
  6. Review what we learned

 


 

h1 {font-size: 150%}
h1,h2 {font-style: bold}
img
{
border:2px solid silver;
margin:0px 0px 15px 20px;
}
blockquote, pre.code {
border: solid 1px #aaa;
padding: 6px;
background-color: #eee;
color: inherit;
overflow:auto;
margin: 10px 0px;
}

Install Cucumber

Based on these install instructions

sudo gem install rspec rspec-rails cucumber webrat

important: Cucumber 0.1.12 and up depends on Webrat 0.3.2.1 or higher, which as of this writing is not yet officially released to Rubyforge’s gem repository. In the meanwhile, install Bryan Helkamp’s snapshot gem:

gem sources -a http://gems.github.com
sudo gem install brynary-webrat

The plugins’ dependencies must be installed separately:

gem install term-ansicolor treetop diff-lcs nokogiri

 

Setup the Application

First we’ll create the Rails “to do list” application:

cd $webroot
rails -d mysql todolist
cd todolist
rake db:create:all
rake db:migrate

Now we’ll set up cucumber for the project

ruby script/generate cucumber
create  features/step_definitions
create  features/step_definitions/webrat_steps.rb
create  features/support
create  features/support/env.rb
exists  lib/tasks
create  lib/tasks/cucumber.rake
create  script/cucumber

Just to make sure that everything is installed correctly:

rake features

If that runs without errors you are ready to rock.

 

Describe a Feature

In the features directory that was auto-created for us with the cucumber script, we create a .feature file which starts with a description of the feature. The first section that describes the feature appears to be purely documentation; however the “scenario” sections will each become part of the executable feature definition. For starters we’ll do something simple.

features/tasklist.feature

Feature: Tasks
In order to keep track of tasks
People should be able to
Create a list of tasks

Scenario: List Tasks
Given that I have created a task "task 1"
When I go to the tasks page
Then I should see "task 1"

We know we haven’t written any executable steps, but we’ll execute it anyhow:

Note that one of the steps is already defined in webrat. Isn’t that cool? When we set up cucumber for the project, it automatically includes step_definitions/webrat_steps.rb which defines some common steps. As you get the hang of this, you reuse certain word patterns which map to specific tests. But we’re getting ahead of ourselves. We need to dive into the creation of “steps” which make up our executable spec. Cucumber gives a some handy snippets to get us started (in the output of “rake features” above). We’ll paste these into a new file that we’ll create in the “features/step_definitions” directory:

features/step_definitions/tasklist_steps.rb

Given /^that I have created a task "(.*)"$/ do |desc|
Task.create!(:description => desc)
end

When /^I go to the tasks page$/ do
visit "/tasks"
end

Note that I touched up the first step to include a regular expression. This means I could add Given that I have created a task "foo" to another scenario and it would match this step.

Short aside on task creation syntax

To create the task, I’m calling my Task model directly (since I’m new to Rails, I looked up the ActiveRecord::Base syntax in the Rails Framework API docs). In my first pass I wrote:

task = Task.new(:description => desc);
task.save

However, Aslak Hellesøy kindly pointed out that it would fail silently with that syntax, and instead I should call task.save! or the even simpler Task.create!(:description => desc). I had missed create! in the documentation, since it is part of ActiveRecord::Validations. The API doc is a little confusing on this point, but looking at the source shows that ActiveRecord::Validations is included as a module. Pat Maddox notes that he uses the bang version (.save!) in tests, and the non-bang version (.save) in production code since validation errors aren’t exceptional.

Back to Step 3

Looking in features/step_definitions/webrat_steps.rb, you can see the definition of our third step:

Then /^I should see "(.*)"$/ do |text|
response.body.should =~ /#{text}/m
end

Ok, now we have a simple spec. Is it time to write the code? No!

Execute the Feature and Watch it Fail


As expected, we see errors on our first step, since we have not yet written any code for the application.

Write the code to make it pass

Now, at last it is time to write code

$ ./script/generate scaffold Task description:string
$ rake db:migrate

Run the spec again..

It passes, yay!

What did we learn?

When we first set up our app, to setup cucumber:

ruby script/generate cucumber

To describe our feature, we create two files:

  • features/xxx.feature
  • features/step_definitions/xxx_steps.rb

To run the feature description:

rake features