Saturday, March 19, 2016

Foretelling the future: Estimatig Software Project Lifespans

In my professional career as  Technologist I have evolved through many stages. Why it seems like just yesterday I was scanning for broken HREFs. Now, I am in charge of task management for one of the most talented development teams I have ever gotten the privilege of coding with. Like every major paradigm shift though, this one has come with a whole new set of challenges and skills. One of the most important ones is accurately estimating project timelines. As a manager, I wanted an easy way to come up with realistic project. This is the result of that research.

The method I use combines the developer's assessment of a projects length with a little math magic to provide an expected amount of time, a best case amount of time, a worst case amount of time, and two ranges of time the project is likely to fall through. I will cover each piece in more detail in a minute. This method also takes into account the "Cone of Uncertainty" principle. I have broken my projects up into five phases: Inception, Requirements, Architecture Design, and Development. As your project progresses towards completion, the amount of uncertainty about how long something might take lessens as well. It Sounds like a lot, but the math (and programming) was relatively simple to understand.
Te = (To + (4*Tm)+Tp) / 6
 S = (Tp - To) / 6
R0 = Te +/- S
R1 = Te +/- (2*S)

Now let me cover each piece of the formula a bit more:
Tm = The estimate provided by the developer. Taken as the most likely time a project will take.
To = This is the Optimistic Time. It represents best case scenario or least time it could take. Derived from Tm using the values from the cone of uncertainty

Tp = The Pessimistic case or worst case is the longest amount of time a project can take. Derived from Tm using the values from the cone of uncertainty

S = Sigma. The deviation for the range of values. A low deviation means a high degree of stability in the estimate
R0 = The smaller range of time. There is a 68.3% chance the project's time will fall between the up and lower bound of this range.
R1 = The bigger range of time. There is a 95.5% chance the project's time will fall between the up and lower bound of this range.

You may notice only one of those variables, Tm,  is actually supplied to the code. The rest of the values are derived from that. In my version of this algorithm I solve all 4 equations for all 5 phases of a project. I can then select the output for the proper phase as the estimate I provide to my client.

Now what would a blog entry from me be without some code? Of course, I do not want to have to hand calculate all of those variables every time I need to estimate a project (it happens frequently now). So I wrote a simple Python script to handle all the grunt work. Check it out:

#!/usr/bin/env python2
#encoding: UTF-8
import math
from optparse import OptionParser
__author__= "Daniel"
usage = '''
This program is designed to help estimate ranges for projects with regard to the current phase of the project.
It is based of the "Cone of Uncertainty" principle taught in Software Management. The result is a set of values
representing different scenarios for the project.

4.00|*
2.50|      *
1.75|            *
1.25|                    * 
0.00|-Time----------------------*-
0.90|                    *
0.75|            *
0.50|      *
0.25|*
'''
parser = OptionParser()
parser.add_option("-e", "--estimate", dest="estNum", default=None,
                  help="The numeric estimate for the project provided by the developer")
parser.add_option("-u", "--unit", dest="estUnit", default="h",
                  help="The numeric estimate for the project provided by the developer")
(options, args) = parser.parse_args()

if options.estNum is None:
    print "Must include an estimate from the dev (-e)"
    exit(1)
   

uncertainty_index = {"inception":[0.25,4],
                     "requirements":[0.4,2.5],
                     "architecture":[0.6,1.75],
                     "development":[0.75,1.5],
                     "testing":[0.9,1.25]}
dev_estimate = float(options.estNum)
estimate_unit = options.estUnit
pess_time = 0
likely_time = dev_estimate
optimistic_time = 0
sigma = 0
print "The Dev Estimates: %d" % likely_time
for k in uncertainty_index.keys():
    mods = uncertainty_index[k]
    optimistic_time = math.ceil(float(likely_time*mods[0]))
    #optimistic_time = float(likely_time*mods[0])
    pess_time = math.ceil(float(likely_time*mods[1]))
    est_time = math.ceil(float((optimistic_time+(4*likely_time)+pess_time)/6))
    sigma = float((pess_time-optimistic_time)/6)
    print "During the %s phase:" % k
    tight_range = [math.ceil(est_time-sigma),math.ceil(est_time+sigma)]
    loose_range = [math.ceil(est_time-(sigma*2)),math.ceil(est_time+(sigma*2))]
    print "\tThe Optimistic estimate is %2.2f %s" % (optimistic_time,estimate_unit)
    print "\tThe Pessimistic estimate is %2.2f %s" % (pess_time,estimate_unit)
    print "\tThe Adjusted Estimate is %2.2f" % est_time
    print "\tThe Sigma is %2.2f" % sigma
    print "\tThe tight range is %2.2f +/- %2.2f" % (est_time,sigma)
    print "\tThe loose range is %2.2f +/- %2.2f" % (est_time,sigma*2)
    print "\tThe project is 68.3 percent Likely to be between %2.2f and %2.2f %s" % (tight_range[0],tight_range[1],estimate_unit)
    print "\tThe project is 95.5 percent Likely to be between %2.2f and %2.2f %s" % (loose_range[0],loose_range[1],estimate_unit)
    print "\n"


No comments:

Post a Comment