How (not) to use Story Points

Abstract (tl;dr)

Story points should never be used to represent hours, but simply relative size of effort to complete a story. Over time, the team will tend to complete a consistent range of story points each sprint. Trying to tie story points to duration breaks the model and leads to inaccurate forecasting. Points should not be used to compare teams, nor should they be used to compare bugs.

Introduction

In Agile projects, each work packet is called a Story. Each story has a point value assigned to it.

I prefer to use the Fibonacci scale for story points.

1, 2, 3, 5, 8, 13

Each number is the sum of the previous two numbers (3 = 2 +1; 8 = 5 + 3; etc).

But what do these points mean? We will get to that in a minute, but first let’s examine how difficult it is to estimate sizes.

Glass of Water

A glass of ice water with straw and a lemon slice.
A Glass of Water

How many ounces of water is in the glass? If you’re like most people, that is not an easy thing to guess.

On the other hand, if I compare it to the glass below, I might say is has roughly twice as much water, and I would be mostly correct.

Glass half-full of water
Another Glass of Water

This is the concept of story points.

1 is the baseline amount of work.

2 is twice as much effort

3 is three times as much, and so on.

Either 8 or 13 are “too big to do”—otherwise known as EPICs—and are slated to being broken up into smaller stories later.

The reason this is done is because it is easier for humans to judge a relative size than an absolute size.

Smallest amount of work

The effort of the smallest amount of work is considered a “1”. For web projects, this is often the effort required to change some element’s CSS style (color, font, size, etc).

Every other story is compared to this task.

Count the points

During the course of the sprint, the team completes the stories. And the end of the sprint, all completed story points are summed and that is the number of points completed for that sprint. After 3-4 sprints are completed, the average number of completed points for the prior three sprints is a good indicator of the number of points the team will complete in the next sprint.

Sprint 1Sprint 2Sprint 3Average
14181415 (rounded down)

As you can see, points cannot be hours because the number of points varies, based upon many factors:

  • Team members’ skills
  • Team leader’s leadership
  • Distractions/work environment
  • Complexity of work
  • Tools/equipment quality

Points are not fungible

fungible (fŭnˈjə-bəl)

adj. Interchangeable.

Points are not fungible, that is, they are tied to the team. One can’t judge one team’s performance against another’s by counting points, because of the factors that cause variability in points. One team might complete 15 points in a sprint, while another might complete 40. The first team is not worse than the second team; points mean different things for the two teams.

Bugs don’t Point

Lady standing in front of a huge waterfall
Huge Waterfall

You can’t point bugs. Well, you can, but you’re making a huge (huuuuuge) mistake. When pointing stories, one needs to explicitly lay out the tasks required to complete the work. Then, that work is compared to the Smallest Amount of Work and given a point.

Bug HAVE NO defined tasks required to complete the work because no one knows what is causing the bug. What are the exact steps required to fix it? There aren’t any; one simply works the problem until it is fixed.

For example, let’s take the “bug” of me losing my car keys. How late will I be? If I estimate the tasks to find my keys, it will be something like:

I will look:

  1. In my backpack
  2. On the table
  3. In my pants
  4. On the kitchen counter
  5. On the bathroom counter
  6. On the nightstand next to my bed
  7. Under the nightstand next to my bed

Given all that, when will I find my keys? After #1? After #8? Later?

Edit (30 Jan 2026) – Formatting, images

Misunderstanding Cookies

In a recent column, Tracey Capen posits the reason so many sites are displaying the “This site uses cookies. Click ‘okay’ to continue” banner is because of Advertising—specifically because many people use ad-blockers so the advertisers want to still be able to track you.

 

Cookies in the EU

He almost accidentally mentions the actual reason for these banners: EU Regulations on privacy. Specifically, the regs state that websites must notify visitors before the site places cookies on the visitor’s browser, AND give them an option to opt out before that happens.

English: Tor Logo
(credit: Wikipedia)

So why do we in the non-EU part of the world see these banners? Because it is simply easier to display the banner to everyone, rather than by attempting to determine if the visitor is in the EU and displaying the banner IFF they are.

There are a myriad of ways to mask one’s location, such as using the TOR network, so the risks of failing to show the banner (and getting fined) versus just displaying it to everyone is a no-brainer.

But what about Advertisements?

Ads—and their ad-delivery network—use cookies in a variety of ways. Advertisers are charged by the networks for impressions (number of people to whom the network delivers the ad or CPM) and for the number of people who click on the ad (clickthroughs). The latter is more valuable to the advertiser and thus the networks charge more.

The ads aren’t directly loaded on the page without the network because there needs to be a system that counts the number of impressions, rotates multiple ads in the page location, and stops showing an ad when the ad’s CPM is exhausted. If an advertiser pays for 5,000 CPM, once 5,000,000 visitors visit the page, the next visitor will not see that ad.

When an adblocker runs it does at least one of two things: it blocks the ad code or (more likely) it blocks the ad delivery network code. What this means is the visitor’s browser doesn’t even request the code from the server; NOTHING from that server is requested. No code means the ad network can’t run and deliver the ads. Also, without a request, there is no cookie.

Even if somehow the site displayed the banner (“please accept our marketing/advertising cookie” Capen imagines them saying), the cookie can’t be added because there is no cookie. There is no ad network that loads. There is no ad to display.

This site uses Akismet to reduce spam. Learn how your comment data is processed.

If I’m going to hire you, I don’t want to read your resume

Well, I take it back. I want to remember your name, and I’m terrible at names. So it helps if I have your name in front of me.

High-quality neon rocket sign for business branding and decor. Perfect for attracting attention and enhancing visual appeal.
Dead-Tree Resume

“But,” you ask, “How will you know if the candidate has the skills and experience you need?”

Good question. Let’s look at the purpose of an interview.

The Two Questions

An interview must answer two questions:

  1. Can the candidate do the job now and in the future?
  2. Will the candidate be a good fit for the team?

Neither of these questions are answered by the candidate’s resume.

Wait a minute

Unless the candidate worked for a company doing the exact work, for the exact clients you have, their experience will not tell you if they can do the current job at your company.

Put it this way: would it make sense to compare your company to another in a different industry, with different customers and a different product, and then make a judgement about performance based upon that comparison?

Company Industry # Customers # Employees Product
MyCorp Pharmaceutical 3 7 Blue Pills
Acme, Inc Energy 150 70 Batteries
Initech Accounting Software 3,200 1,500 TPS Reports

We will investigate the 2 questions in a future post.

I don’t want to read your resume

Well, I take it back. I want to remember your name, and I’m terrible at names. So it helps if I have your name in front of me.

High-quality neon rocket sign for business branding and decor. Perfect for attracting attention and enhancing visual appeal.
Dead-Tree Resume

“But,” you ask, “How will you know if the candidate has the skills and experience you need?”

Good question. Let’s look at the purpose of an interview.

The Two Questions

An interview must answer two questions:

  1. Can the candidate do the job now and in the future?
  2. Will the candidate be a good fit for the team?

Neither of these questions are answered by the candidate’s resume.

Wait a minute

Unless the candidate worked for a company doing the exact work, for the exact clients you have, their experience will not tell you if they can do the current job at your company.

Put it this way: would it make sense to compare your company to another in a different industry, with different customers and a different product, and then make a judgement about performance based upon that comparison?

Company Industry # Customers # Employees Product
MyCorp Pharmaceutical 3 7 Blue Pills
Acme, Inc Energy 150 70 Batteries
Initech Accounting Software 3,200 1,500 TPS Reports

We will investigate the 2 questions in a future post.

Adding defensive sanity checks

I recently needed to make a set of several favicons, so I went to the web to see if anyone had a script I could borrow steal.

Sure enough, I found one written by Joshua McGee: “Create a favicon with ImageMagick” (not reproduced here for copyright reasons).

It was a simple enough script, just a series of escaped commands. I noticed, however, that it assumed a few things:

  • An image file was specified on the command line,
  • The image existed, and
  • Imagemagik was installed.

In other words, the script was not developed defensively. This makes sense: it was just a bang-out.

The script had no inline documentation, and if a favicon file that already existed in the current directory would be silently overwritten—not good.

I’m clumsy: I delete and overwrite files all the time, so I could use a little help. Maybe I can tidy up the script?

Ensure command-line parameter

Whenever your script requires a command-line parameter, display a help if it is not provided or is the wrong type. Why? Because in 6 months you will forget how to use it.

if [[ -z "$1" ]]; then
		echo -e "\nUsage:\n\t$0 image.png\n\n\tWhere 'image.png' is ..."
		exit 0
fi

Easy! The -z means “if empty”. And $1 contains the first parameter. When we display the error message, we use $0, which contains the script’s filename and path. So if I run my script like this “/foo/bar/somescript”, that is what $0 will display.

Ensure Source Image Exists

# Check to is if source file exists
if [[ ! -e $1 ]]; then
		echo -e "\nFile '$1' not found."
		exit 0
fi

I added the comment because I can never remember what “-e” means. When scanning the source, I can quickly tell what this section does without reading much further into the code.

Ask the user before overwriting

# Warn if favicon.ico already exists in current directory
if [[ -e favicon.ico ]]; then
		echo -e "Warning:\n\tfavicon.ico exists in current directory."
		choice "Overwrite? [y/N]: "
		if [ "$CHOICE" = "y" ]; then
				echo "Overwriting favicon.ico"
				rm favicon.ico
		else
				echo "Exiting"
				exit 0
		fi
fi

Another defensive-coding best-practice. Before doing anything destructive that isn’t the primary purpose of the script, let the user decide if they want to proceed. By “primary purpose” I mean that the only reason someone would run the script was to destroy something.

What’s worse is destroying something silently, which is why I let the user know when we overwrite favicon. Keeping the user informed needs to be balanced with flooding the user with too many messages.

Note that the “choice” statement above isn’t a build-in command, but a very handy one I got from O’Reilly’s “bash Cookbook”.

Is Imagemagick installed?

Actually, I don’t care because I use it so much that I always install it on whatever system I’m using. An approach I might take would be

type mogrify

And compare the result.
A good discussion of self-documenting code vs. inline documentation may be found in chapter 11 of: McConnell, Steve (2004). Code Complete (2nd ed.). Pearson Education. ISBN 9780735636972.

The importance of turning on debugging

Debugging a php script with emacs in geben mode.
Debugging a php script with emacs in geben mode. (Photo credit: Wikipedia)

I was recently testing some wordpress plugin code for an upgrade. As part of my testing, I turn on debugging to see if any errors or warnings show up.

Imagine my surprise when several errors appeared, many of them deprecation warnings. After troubleshooting to determine the source of the errors, I discovered it was coming from the theme I was using.

Without turning on debugging, the site appears to behave normally, which is problematic. One of my objections to many languages and frameworks is that they hide problems from the developer. In many cases, for example, deprecation warnings don’t mean much—except they’re a ticking time bomb. At some point in the future, the code is going to break. Better to fix it now while it is easier to do, than scramble to diagnose and fix lots of code just after an upgrade.

In the case of WordPress (or PHP in general), it would be helpful if the admin area showed everything – bugs and warnings. For novice admins, of course, this would be a support nightmare, as they would have little clue what was going on. The upside is (hopefully) sloppy coders would fix their stuff promptly. Nothing will get a plugin/theme pulled faster than getting an error right after installing it.

Standalone CRUD

CRUD: Create, Read, Update, Delete; actions for managing data usually stored in a database.

Data model diagram picture of an EMPLOYEE data...
Data model (credit: Wikipedia)

A system I maintain has a very unusual quirk: when adding a new element (“blub”) to a list of elements (“blubs”), the system crashes with a generic error.

What this tells me is there is an unmet dependency, probably a join to another database table. I suspect the original developer (OD) created all the blubs manually, then later added in CRUD screens to manage them. For some reason OD never tested creating a new blub.

For any list of things, unless they are constants that will never be CRUDded (okay, reading is okay), the app should be able to Create/Update/Delete them without any manual steps.

Why? Because someday, some schlub who maintains your code is going to have to CRUD. And they will be screwed.

Development Languages are not Just Syntax

I recall a conversation I had with a coworker years ago. This fellow mentioned he thought differences between languages (e.g., Perl and PHP) were simple syntax.

example of Python language
example of Python language (Photo credit: Wikipedia)

This is true, sort of. The nuts and bolts for each language (displaying a variable’s value and the like) are same in concept and similar in construct.

<?php =someVar ?>

or

<% puts someVar %>

You get the idea.

The difference (and where the coworker was wrong), is the concept of how the data flows through the system and which pattern(s) are encouraged/discouraged.

Some languages encourage MVC, whereas others encourage spaghetti and still others encourage a hybrid (or something else entirely, like modules).

What is a language best at?

Going beyond this, what is the language best at doing? Is it installed in places you want to host your code? Do you have data structures/stores that fit better with one language than another?

Picking the best tool for the job is important in delivering a quality product. Choosing which language to use isn’t trivial and can make it easier—or harder—to deliver the solution.

Goals

Goals differ from intentions or ideas in several key aspects.

Every goal:

  • Has a deadline
    • A goal is achieved at some point in the future. By having a deadline, the goal is focused, even if the deadline is not specific: “next week”, “next year”, etc.
    • A goal without a deadline is an intention.
  • Is measurable
    • One or more conditions that indicate when the goal has been reached, such as “built my house” (the house is ready for occupancy); “completing a course” (I passed the final / learned the material); “saved up to buy a car” (I have enough money to buy the car)
  • Has an owner
    • The person responsible for moving work forward to achieve the goal; may not be the same person as the one who does the work.
  • Must be reviewed on a regular basis
    • Conditions and people change, so it is important to review every goal to determine if it needs to be adjusted to fit reality.
    • Sometimes goals may no longer be necessary so continuing to pursue them is time better spent in other ways.
    • Some questions to ask about every goal:
    • is it necessary/desired?
    • is work on track to finish by the deadline?
    • is the deadline still applicable? and
    • is it still achievable?