Friday 31 October 2008

Finding our target velocity without black magic

To be in the moment (and I always like to be), here is a post for Halloween that removes the black magic from the estimation process and determines initial velocity.

Photo by euart used under the flickr Creative Commons license

Starting a new project and being around from the beginning is always an eye opener. If you are involved in estimation then it is also like signing a contract. Breach of promise to reach the guessed random number can be punished with overtime and a sense of failure. Heaven forbid a late project especially if it is fixed price.

I've started a new project and we are working out how long it will take and what resources are required. For the first time, estimation seemed more deliberate and less like guesstimation.

This is how we did it...

What you need to start estimating:
  • The latest draft of the master story list - a list of the all the stories known so far, written like "AS a person using the system, I WANT TO do something functional SO THAT business value is achieved";
  • As many members of the delivery team as possible - including engineers, testers (QA), business analysts (BA) and even business;
  • Agree on the length of an iteration;
  • An estimation deck - usually consisting of cards representing the Fibonacci sequence between 1 to 13. If you don't have this then use your fingers with 10 fingers = 13;
  • A willingness to estimate in points and not days;
  • A big table for everyone to sit around and to spread the cards on;
  • Snacks - this can take a while.
Next you get the team to understand what you are building, both high-level technically and what the business wants:
  • Sit everyone down at the table;
  • Have the BA or Project Manager (PM) read through every story;
  • Allow questions to understand what the story means;
  • Rewrite or breakdown stories in to small functioning parts;
  • Write out all stories on 4x6 cards.
Now you start throwing numbers around:
  • Re-read the stories;
  • Get everyone involved in delivery to do min-likely-max estimates and write them on the back of the cards- minimum points if all goes blissfully well, likely points in a realistic situation and maximum points if all hell breaks loose;
  • For each story work out volatility - low, medium or high;
  • For each story work out completeness - complete, incomplete or unknown;
  • For each story work out complexity - simple, medium or complex;
  • Engineers group the stories in to what they think they could complete in one iteration. Use about 5 groups - dependencies and iteration order are not important. Just look at the size of the stories and group an iterations worth;
  • PM secretly adds up the total likely estimated points from the groups and works out average iteration velocity;
  • PM lays out different groups of stories that would fit in to the new predicted velocity - note this is not the teams actual velocity because that isn't known until they actually start banking points;
  • The engineers looks at the groups and decide if they are too big, just right or too small - it's a little like Goldilocks;
  • Rinse and repeat - this can be re-estimated and sorted again to double check numbers.

Monday 27 October 2008

Don't Embrace Mediocrity

used under the flick creative commons license by caravinagre

I wrote a post a while ago that somehow got on to a few of the IT aggregators and resulted in an influx of readers, many of whom were interested in what was a popular topic at the time. That attention brings out the good, the bad and the ugly on the Interblag.

A friend of mine who regularly blogs about food on our work blog feeder has also recently been attacked by the freak-geek police and told her blog doesn't belong as it isn't technical. Amusingly enough, it wasn't even a work colleague who said this.

After spending an hour listening to Edward de Bono talk about why arguing a point doesn't always produce the best outcome, I'm starting to see that point of view reinforced online. It was probably always there but it seems relevant to me now. So let's discuss it and also annoy the people who think that only code snippets belong in blog posts.

Are any new ideas being added to the blogosphere? Is blogging dead? If it is and there are no new ideas then what or who killed it?

It seems that if you are established, usually not through rising on the Internet but through a more traditional (and rightfully respected) publishing background, then your opinion is rarely knocked online. Social proof or tangible proof. If you are more like the bloggers I see out there with new ideas and different ways of thinking and challenging the established norm, then your words will be hammered. Interestingly enough, it is a small enough group who attacks so it doesn't really matter most of the time. It does however feel like that constant beating at the edges of the blogosphere, is dulling the interesting sparky parts.

It even has the potential to silence any new comers who think they might have something to say and haven't quite found their voice. Instead those who are happy with the way the Internet appears to be to them want to keep it that way. Maintain the average. Silence the creativity. It's an obscure notion to tell someone else not to speak so I will not tell them not to speak as they so happily feel the right to do to others.

Instead, I encourage any of you out there with something to say to stand up and say it. Ignore the push toward mediocrity. I paraphrase the wise and powerful Madonna in saying: Express what you've got, baby ready or not. Express yourself!

This one is better: ruby-net-ldap

After posting a quick how-to about Ruby-LDAP, I received a couple of very helpful comments that pointed me towards ruby-net-ldap. This is a pure Ruby LDAP library that is stable and has good documentation to help you along. It is the best Ruby LDAP gem out there and I've been through almost all of them to get to this point.

Here is a simple search for an organizational unit with the name "marketing"...


require 'rubygems'
require 'net/ldap'

def ldap_search

 ldap = Net::LDAP.new
 ldap.host = "localhost"
 ldap.port = "389"
 ldap.auth "cn=Directory Manager", "password"

 filter = Net::LDAP::Filter.eq( "ou", "marketing" )
 attrs = [ "ou" , "objectClass"]

 ldap.search( :base => "dc=mycompany, dc=com", :attributes => attrs, :filter =>
 filter, :return_result => true ) do |entry|
   puts entry.dn
 end

end


Here is the code to add an organizational unit under the base node...


require 'rubygems'
require 'net/ldap'

def ldap_search

 ldap = Net::LDAP.new
 ldap.host = "localhost"
 ldap.port = "389"
 ldap.auth "cn=Directory Manager", "password"

 dn = "ou=marketing, dc=mycompany, dc=com"
 attr = {
   :ou => "marketing",
   :objectclass =>"organizationalUnit"
 }
 ldap.add( :dn => dn, :attributes => attr )

end



Check out the rest of the documentation for pretty good examples. This is the library I recommend. In my situation, I'm using ruby-net-ldap to import data in to, manipulate and query data in an OpenDS LDAP server.

Sunday 5 October 2008

Ruby-LDAP

I said it would never happen but here is another Ruby blogpost for the ThoughtWorks pool. In my defense, there really wasn't much help out there on this topic so my pair and I decided this had to be posted. His post on what we tried in order to make Ruby talk to LDAP is over there.

The Bits
OpenDS is Sun's LDAP server. We chose this as our LDAP server because it is the one most likely to sit behind the OpenSSO Single Sign-On implementation we are populating, querying and binding to. It is written purely in Java and provides a bunch of useful shell scripts and a Java API to do the things we want to do. This will allow us to test our Ruby-LDAP implementation independently of our solution.

OpenDS has a nice installer with a friendly setup wizard. If you want to script the install then run this from the root of the installation directory:


>$ './setup --cli --no-prompt -p 1389 -D "cn=Directory Manager" -w "password" -b dc=example,dc=com -a --doNotStart'


Ruby-LDAP was the friendliest and most sensible choice because it was the most pure Ruby choice which allowed us to do things the Ruby way. RJB let us use the OpenDS libraries but was too much like writing Java in Ruby and involved running another JVM on the production server it is destined for. If we were to go that far then we'd have opted for JRuby to do the bridging. After ActiveLDAP flat out refused to acknowledge OpenDS and would only play well with OpenLDAP, that was written off. Ruby-LDAP is a written in C so it isn't a nice gem install but instead takes a bit of making and shaking to get going.

Here is how to install it from the command line once it is downloaded and unzipped:

>$ ruby
ruby-ldap-0.9.7/extconf.rb "."
>$ make
>$ make install

The Required Basic LDAP Knowledge
LDAP is like a very simple and slightly stupid database for storing Identity information. It does not have transactions. You can't query it with SQL. It is a tree. It does have a schema.

Understand your LDAP schema. You can customise it. It is made up of nodes that are defined by object classes and attributes. These include but are not restricted to: Organization; OrganizationalUnit; and Person.

Distinguished Name (DN) appears everywhere when you are dealing with LDAP. It is the unique name given to a node in the LDAP tree and describes the exact path from the node to the root. A DN starts at the node and walks up the tree. It looks like: uid=damana,ou=australia,dc=mycompany,dc=com

This is an LDAP glossary that was useful in deciphering the LDAP maze and nomenclature.

Adding an Organizational Unit
To add an organizational unit:


#/usr/bin/ruby -w

require 'ldap'

connection = LDAP::Conn.new('localhost', 1389)
connection.bind('cn=Directory Manager','password')

record = [
LDAP.mod(LDAP::LDAP_MOD_ADD,'objectclass',['organizationalUnit']),
LDAP.mod(LDAP::LDAP_MOD_ADD,'ou',['australia
']),
]

connection.add("ou=branches, dc=
mycompany, dc=com", record)

connection.unbind


Adding a User
To add a user to an organizational unit:


#/usr/bin/ruby -w

require 'ldap'

connection = LDAP::Conn.new('localhost', 1389)
connection.bind('cn=Directory Manager','password')

record = [
LDAP.mod(LDAP::LDAP_MOD_ADD,'objectclass',['person']),
LDAP.mod(LDAP::LDAP_MOD_ADD,'cn',['Damana Madden']),
LDAP.mod(LDAP::LDAP_MOD_ADD,'cn',['dmadden']),
LDAP.mod(LDAP::LDAP_MOD_ADD,'sn',['Damana Madden']),
]

connection.add("cn=Damana Madden ou=australia, dc=mycompany, dc=com", record)

connection.unbind


Deleting a User
To delete a user:


#/usr/bin/ruby -w

require 'ldap'

connection = LDAP::Conn.new('localhost', 1389)
connection.bind('cn=Directory Manager','password')

connection.delete("cn=Damana Madden ou=australia, dc=mycompany, dc=com")

connection.unbind

Acknowledge Me

Apple started a user experience trend many iOSes ago when it accepted Settings changes and did not ask for confirmation. Once the chang...