Understanding how the Team WebAccess burndown chart works, Part 3

If you have been following my mini-series on understanding the TFS11 burndown chart, surely you know that in this blog post we are going to analyze how Team WebAccess calculates the ideal and actual burndown trend lines. Just as a quick recap, here’s what we’ve learned so far. In part one of the series, we have discovered how to construct a query expression which would retrieve the work items that can be used for calculating the burndown chart data for a specified iteration. Then, in part two, we’ve made good use of this query expression by using it to execute historical queries for each day in the iteration duration and to retrieve the remaining work for the specified iteration day.

This blog post is the third and final part, where we’ll see how the trend lines are calculated and why they are used. If you still haven’t read the first two posts, I’d really encourage you to go through them, as this blog post will make a lot more sense. You can find the posts here: part one, part two.

The sample data

Just to make everything a bit easier to understand and visualize, I’ve decided to use an imaginary iteration, which I’ve chosen to last between April 9th and April 29th 2012. Just as a note, I’m fully aware that people most often use two week iterations, but since this isn’t important for the purpose of our discussion, I’ve opted to use a longer iteration so we can work with more data points. Speaking of which, here are the imaginary values for the remaining work, which could have been retrieved by the code we’ve covered in parts one and two:

110, 105, 102, 92, 87, 87, 87, 65, 70, 70, 35,
55, 55, 55, 52, 52, 52, null, null, null, null

What the last four null values are saying is that we don’t have data for those iteration days, which should indicate that the iteration from the example is still in progress, and that we’ll imagine that today’s date is April 25th (since that’s the last day we retrieved the data for).

To visualize things even better, here’s a nice chart, which I’ve whipped up using Microsoft Excel:

The sample burndown chart

Now, there are a few interesting things in the sample data and the above chart. The first one is, like I’ve pointed out, that the iteration is still in progress and that we don’t have remaining work data for all the days in the iteration. What’s also interesting is that we have two places in the chart that the remaining work has actually increased, instead of decreasing or remaining the same. Most typical causes of something like this is if you add new tasks during the iteration or if some tasks were underestimated and a correction was necessary. I am pointing out these subtleties because they will impact the way we calculate the ideal and actual burndown trend lines, as you’ll see in a moment.

The ideal burndown trend line

So what should the ideal burndown line look like? Which two points define it? You might be thinking that the first point should be the remaining work value on the first day of the iteration and the last point should be the last day of the iteration, where the remaining work value should equal zero. And you would be perfectly right, if it weren’t for situations like the one I’ve described just now, where someone could add a new task to the iteration or increase the remaining work value of a task you have committed to implement. Because of this, the Team WebAccess burndown chart first calculates a correction value, by which it will offset the starting point of the ideal (and actual) burndown trend line to compensate for the aforementioned situations.

The way this correction value is calculated is by iterating over the entire sequence of remaining work values and aggregating values whenever the remaining work value for an iteration day is greater than the previous day. It might sound a bit complicated, but it’s really simple stuff. Let’s see how it’s implemented in C#. First, a nice helper class that holds all the information about a data-point in the burndown chart:

class DataPoint
{
    public double? Work { get; set; }
    public DateTime Date { get; set; }
    public double IdealTrend { get; set; }
    public double ActualTrend { get; set; }
}

And now for the correction value calculation:

static void CalculateTrendlines(DataPoint[] dataPoints)
{
    if (dataPoints.Where(dp => dp.Work.HasValue).Any())
    {
        int pointsWithValue = dataPoints.Where(dp => dp.Work.HasValue).Count();
        double correction = 0;

        // Iterate over data points that have a remaining work value set.
        for (int i = 1; i < pointsWithValue; i++)
        {
            // Read the remaining work for the current and the previous day.
            double currentWork = dataPoints[i].Work.Value;
            double previousWork = dataPoints[i - 1].Work.Value;

            // Aggregate the data if needed.
            if (currentWork > previousWork)
                correction += (currentWork - previousWork);
        }
    }
}

Like I’ve said, pretty simple stuff. We use a for loop to iterate over all the data points which have a remaining work value (that is, it’s not null) and whenever the current remaining work value is greater than the day before, we accumulate the difference. If we add the correction value to the remaining work value on the first iteration day, we then know both points required to draw the ideal burndown trend line:

// Calculate the remaining work value for the first day.
double firstValue = dataPoints[0].Work + correction;

// Get the iteration duration (in days).
int lastPointIndex = dataPoints.Count() - 1;
int iterationDays = (dataPoints[lastPointIndex].Date - dataPoints[0].Date).Days;

// The value by which the ideal burndown value should decrease each day.
double idealDelta = firstValue / iterationDays;

// Set the ideal trend value for the first day.
dataPoints[0].IdealTrend = firstValue;

// Set the ideal trend values for the rest of the iteration.
for (int i = 1; i <= lastPointIndex; i++)
    dataPoints[i].IdealTrend = dataPoints[i - 1].IdealTrend - idealDelta;

Once we’ve calculated the ideal trend line values, we can overlay that data over the sample burndown chart:

The burndown chart with the ideal burndown trend line

By looking at this chart, you can immediately see that the team was progressing just fine until about April 20th, after which the remaining work stopped decreasing and the team fell behind schedule. If we’d like to know how much we’re falling behind (or if we’re ahead of schedule), as well as where we’ll be work-wise at the end of the iteration, we need to have a look at the actual burndown trend line.

The actual burndown trend line

Similarly to the ideal burndown trend line, the actual burndown trend line will have the same starting point. That is, the starting point will again be the remaining work value on the first day, increased by the correction value. The end point will differ, however, and it’s value will be the remaining work value on the last day that we were able to retrieve data (that is, including today, if the iteration is still in progress). It’s simple to find the value of the end point:

// Calculate the remaining work for the last day we have data on.
double lastValue = dataPoints[pointsWithValue - 1].Work.Value;

// The value by which the actual burndown value should decrease each day.
double actualDelta = (firstValue - lastValue) / (pointsWithValue - 1);
	
// Set the actual trend value for the first day.
dataPoints[0].ActualTrend = firstValue;

// Set the ideal trend values for the rest of the iteration.
for (int i = 1; i <= lastPointIndex; i++)
    dataPoints[i].ActualTrend = dataPoints[i - 1].ActualTrend - actualDelta;

Now there’s nothing left to do but to overlay the actual burndown trend line over the previous burndown chart, which results in a chart similar to this one:

The burndown chart with the ideal and actual burndown trend lines

By looking at the chart, we can project that the team will have about 30 hours of remaining work left by the end of the iteration. You can also see that the actual burndown trend line just barely touches the burndown chart on the last day that we’ve retrieved data for (that is, today, April 25th).

Source code listing

Here’s the source code listing for the entire method that calculates both the actual and ideal burndown trend lines:

static void CalculateTrendlines(List<DataPoint> dataPoints)
{
    if (dataPoints.Any() && dataPoints.Where(dp => dp.Effort.HasValue).Any())
    {        
        int pointsWithValue = dataPoints.Where(dp => dp.Effort.HasValue).Count();
        double firstValue = dataPoints[0].Effort.Value;

        // Calculate the correction value and use it to
        // update the starting value for the trend lines.
        for (int i = 1; i < pointsWithValue; i++)
        {
            double currentEffort = dataPoints[i].Effort.Value;
            double previousEffort = dataPoints[i - 1].Effort.Value;

            if (currentEffort > previousEffort)
                firstValue += (currentEffort - previousEffort);
        }
        
        // Calculate the iteration duration (in days).
        int lastPointIndex = dataPoints.Count() - 1;
        int days = (dataPoints[lastPointIndex].Date - dataPoints[0].Date).Days;

        // Calculate the delta for the ideal burndown trend line.
        double idealDelta = firstValue / days;

        // Calculate the delta for the actual burndown trend line.
        double lastValue = dataPoints[pointsWithValue - 1].Effort.Value;
        double actualDelta = (firstValue - lastValue) / (pointsWithValue - 1);

        // Set the values for the first day of the iteration.
        dataPoints[0].ActualTrend = firstValue;
        dataPoints[0].IdealTrend = firstValue;        
        
        // Set the trend line data for the rest of the iteration days.
        for (int i = 1; i <= lastPointIndex; i++)
        {
            dataPoints[i].IdealTrend = dataPoints[i - 1].IdealTrend - idealDelta;
            dataPoints[i].ActualTrend = dataPoints[i - 1].ActualTrend - actualDelta;
        }
    }
}

Conclusion

This is the end of the blog post series on understanding how the Team WebAccess burndown chart works under the hood. I hope that it’s been interesting to read as it was interesting for me to research and reverse-engineer! I you happen to have any feedback or ideas on how to improve the burndown chart to make it more useful, I’d be very interested to hear what you have to say. See you soon!

Be Sociable, Share!
  • Liat

    Very good blog. Thanks for the detailed inforation.
    I was wondering if there is a way to know if a project is “On Track” – if you know the planed end date for this porject and you see that the actual trend’s end date is bigger than the planed one it means that the project isn’t “On Track” – is there a way to get the actual trend’s end date using the TfsTeamProjectCollection?

    • http://blog.johnsworkshop.net/ Ivan Popek

      Hi Liat,

      I’m happy to hear that you’ve found the blog post useful. As far as I know, there is no TFS API that you could use to determine when the work on an iteration would actually be done. My guess is that this “feature” does not exist because the idea would be to simply move any unfinished work into the next iteration once the current iteration ends. If you were calculating the burndown chart programmatically yourself, like I did in this blog post series, it would be easy to use the information about the ideal trendline “slope” to calculate the exact date when all work would be done.

      • Liat

        Thank you very much for your answer. I’ll implement your suggestion.

  • Liat

    Hi again,
    I understood the logic for the correction and it’s defiantly make sense,
    but it looks like we don’t have this correction at our TFS Burndown charts
    – please see the attached image as example to one of our TFS Burndown charts.
    There is no correction there, in fact the actual trend has infinite completion date (it’s only grow and never gets to zero) – very strange.
    Do you have idea why we don’t see the correction? Do we need to configure it somewhere?
    Thanks,
    Liat.

    • http://blog.johnsworkshop.net/ Ivan Popek

      Hi Liat,

      It is not strange that the actual trend line has an infinite completion date. Since the amount of remaining work actually increases over time in your case, this means that the work would never be done at this rate. Once work starts getting done and the remaining work decreases, you should see the actual trend line fall off.

      I am not really sure about the missing correction offset for the chart in your screenshot. It is possible that the “Burndown and Burn Rate” report uses slightly different logic than the chart used in Team WebAccess. If you would like to learn how to modify the TFS report, you can check out the TFS Guide at http://tfsguide.codeplex.com/ and have a look at Part VII, which covers TFS reporting.

  • Alin

    Hi Liat,

    Is there a way to customize these charts on the web access UI?

  • Raj

    Hi,
    Is there way to get Burn-up report similar to Burn-down reports.
    Regards,
    Raj