This One Weird Trick Will Make Your Acceptance Criteria More Understandable

The way we think, and the way we use language, have a direct relationship with each other. And a subtle change to the way that we write can change the way we think.

Consider for a moment, one of my favourite topics: goal setting. Let’s take the perennial favourite new year’s resolution:

I want to lose weight.

vs.

I will weigh 100kg by the end of this year.

Does one of these seem more real?

The same technique applies when we’re thinking about, and communicating about the work we’re doing in an Agile environment.

Acceptance Criteria tell us when to stop. While I might sound pedantic when I say this, there’s a specific language that I like to use when writing Acceptance Criteria. In short, Acceptance Criteria describe the actual desired state, and not the desire to have some future state.

As a developer or tester, I want to read the criteria and quickly identify whether my solution has accomplished the goals as stated.

So what’s my clickbaity One Simple Trick?

Use the present tense when writing acceptance criteria.

When describing the desired outcome, put your mind into that future state. Write down what the desired state is, and not what it should be.

Why? Using the present tense is a cognitive trick, which changes the way we think about the problem. As a product owner, I can visualize the end state, and easily see gaps in my thinking about the problem. As a developer, I can determine very quickly whether the current state matches the desired state, without parsing out the concept of time.

Here are some examples that I consider good and bad:

Bad:

  • The user should be able to see their account balances at the top of the screen
  • The system should display the most recent account balances
  • I should write an article about Acceptance Criteria

Good:

  • Account balances are listed at the top of the screen
  • Account balances update when the page is reloaded
  • I have posted an article about Acceptance Criterla

Going slow

It’s really easy to get out there and give it everything you have.  It’s even rewarded in our culture.  When’s the last time you heard someone say, “Go out there and give it 20%!”?

Fact is, just like you can’t get your car un-stuck from a snowbank by flooring the pedal, you can’t expect success by giving your full effort.  That’s a certain recipe for burnout.  Mind you, if I’m being chased by a ravenous bear, I might go all out until I pass out from exhaustion or until the bear gets me.  But that’s not what I’m on about.  I’m talking about long-term success.

I’ve signed up for a half-marathon in May.  And for the next few months, I’m subscribing to the 80/20 principle when it comes to my training.  This week begins a 15-week structured training plan: 80% of my workouts will be done at low-intensity, and 20% at moderate-to-high intensity.  I’ll be using heart rate as my primary metric to help manage my effort.

Going slow is hard!

Measuring wheel speed

Each wheel on the robot is paired with a slotted disc, which passes through an optical sensor.  There are 20 slots on each disc, which translates to 20 “on” pulses per rotation.  However, it’s easier for me to count both the “on” and “off” pulse edges, so I’m dealing with 40 pulse edges per each wheel rotation.  I’m going to call these ticks as that’s a lot easier to say.
DSC_9248
But ultimately, I don’t care about ticks.  I do care about speed, though.  So I’m going to start there, and figure out what I need in order to calculate that.

Simply stated:

speed=frac{distance}{time}

or:

speed=frac{1 tick}{timeSinceLastTick}

or, if I throw some averaging in there:

speed=frac{numTicks}{elapsedTime}

I can already see that there’s going to be a problem when the speed approaches zero, as there are no ticks to measure the timings.  So I’m going to start by measuring both the number of ticks that have occurred, and the amount of time that’s passed.  If too much time has passed, I’ll consider the speed to be zero.

// Store up to 10 "tick" (or "no-tick") events
const uint8_t NUM_SAMPLES = 10;

// Keep track of how many of the "tick" or "no-tick" events have happened.
uint8_t _tickCount[NUM_SAMPLES];
volatile uint8_t _totalTicks = 0;

// Keep track of the event timestamps
uint32_t _timings[NUM_SAMPLES];

// Some pointers to the arrays above.
volatile uint8_t _oldestIndex = 0;
volatile uint8_t _newestIndex = NUM_SAMPLES - 1;

When a tick occurs, the system calls an interrupt:

ISR(PCINT1_vect) {
    uint8_t newState = PINC & (_BV(PINC2) | _BV(PINC3));
    uint64_t nowStamp = micros(); // timestamp in µs
    uint8_t changes = newState ^ _encoderState;
    _encoderState = newState;

    if (changes & (_BV(PINC2))) {
        _totalTicks = _totalTicks - _tickCount[_oldestIndex] + 1;
        _tickCount[_oldestIndex] = 1;
        _timings[_oldestIndex] = nowStamp;

        // Move the pointers along, overflowing back to zero if needed.
        _newestIndex = _oldestIndex;
        _oldestIndex++;
        if (_oldestIndex >= NUM_SAMPLES) {
            _oldestIndex = 0;
        }
    }
}

If a tick didn’t happen, we inject a zero into the mix:

// Called when... nothing happened!
void nothingHappened() {
    cli();
    // Overwrite the "oldest" item in the averaging loop, and adjust pointers.
    _totalTicks = _totalTicks - _tickCount[_oldestIndex] + 0;
    _tickCount[_oldestIndex] = 0; // nothing happened!
    _timings[_oldestIndex] = micros();

    _newestIndex = _oldestIndex;
    _oldestIndex++;
    if (_oldestIndexB >= NUM_SAMPLES) {
        _oldestIndexB = 0;
    }
    sei();
}

And finally, in order to calculate the current speed:

double getSpeed() {
    cli(); // Make sure the interrupt doesn't fire while we're in here.
    uint8_t totalTicks = _totalTicks;
    uint64_t oldestTime = _timings[_oldestIndex];
    uint64_t newestTime = _timings[_newestIndex];
    sei(); // Set the interrupts free!

    // To get the time difference, we can't simply subtract,
    // because micros() overflows every 70 minutes or so.
    // Implementation of getTimeDiff is left as an exercise
    // to the reader.
    uint64_t timeDiff = getTimeDiff(oldestTime, newestTime);

    if (totalTicks == 0 || timeDiff == 0 || timeDiff > 10000000) {
        return 0.0;
    } else {
        return (float)((totalTicks - 1) * 1000000) / (float)(timeDiff);
    }
}

There’s a little more to it, but you’ve got the idea.  It’s just a bunch of code that watches how far we’ve gone, another bunch that watches the clock, and at the end of the day, it’s just simple physics:

speed=frac{distance}{time}

Building the Mini Weather Station v2

Last night, I gathered all the parts for the Weather Station and did some assembly.  Installing a few dozen parts by hand, on a 12.5cm² PCB is an intricate process.

I start out with a huge printout of the PCB layout, an unpopulated PCB, and this excellent anti-static parts tray from iFixit.  All the big and identifiable parts go here.  When I’m feeling more energetic, I sometimes print out a specially-formatted parts list that goes under the tray, but I didn’t do that this time.  Just out of frame, there’s a stack of bags with hundreds of tiny surface-mount capacitors, resistors and inductors.  Since they’re not easily-identifiable, I keep them in labelled bags and deal with one at a time.

IMG_3829.JPG

Next, I align the solder paste stencil over the bare PCB and squeegee the paste onto the board.

Before:

IMG_3830.JPG

After:

IMG_3831.JPG

That paste is made out of microscopic balls of lead-free solder, held together with a sticky material called flux.  As you can see in the photo above, it’s not a perfect application of paste, but the reflow process is somewhat forgiving: the surface tension of molten solder helps to settle the parts into better alignment.

About a half-hour with curved ESD-safe tweezers, and the board is fully-populated.  At this point though, all the parts are simply resting atop the sticky paste.

IMG_3834.JPG

Into the oven!

This is a modified toaster oven, that I use for the sole purpose of baking electronics.  There’s my new board, ready to get baked!

IMG_3835.JPG

I really did have far too much fun building this toaster… I ripped out the existing thermostat and timer, and installed my own completely custom control panel and temperature control circuitry.  And it does a pretty decent job of following the time/temperature curve that I programmed into it!

IMG_3841.JPG

A few minutes later, and after it’s all cooled down, I’ve got a completed board!

IMG_3846.JPG

Seawheezed

Wow, sure has been quiet in here.  See, I’ve been building robots (to take over the world), and spending too much time being ill, lately.

Robots are fun; being sick isn’t.  I had a really stubborn cough that just wouldn’t go away.  Still have a bit of it.  It comes and goes.  I’d like for it to go away, really.

This cough started around the beginning of July, and caused me to miss a lot of training, and even a few days’ work.  But of course, I had gone through the trouble of signing up for Seawheeze, and I wasn’t about to give up a ridiculously heavy carrot medal just for a cough.

My respiratory system is feeling much better than it was a month ago, when I could barely walk a few blocks without going into a coughing fit.  And I know that my physical form can take a half marathon — or at least, I’ve successfully run them in the past.

I’m not going to talk about my performance, but rather how this race became a step in my recovery from injury to health.  Yesterday’s race was about a few “moments” that really stuck out to me.

  1. Spin class on the Dunsmuir viaduct.  Maybe they shouldn’t tear down that thing after all.  Turn it into a gym!
  2. Running off the Burrard St. Bridge onto Cornwall: Wall-to-wall people, all running, all being amazing.
  3. Band on a barge, making up running-based lyrics to well-known songs.
  4. Legs hurt, can barely walk home.  Can’t wait to do it again.

Mini Weather Station v2

Last winter, I built a device that I called “Mini Weather Station.”  It is a small plastic enclosure which contains a thermometer, barometer and hygrometer.  And of course, a microcontroller, realtime clock and a 2.4GHz radio module.

IMG_3827.JPG

It worked reasonably well for a few months, even though it had a few minor design flaws.

IMG_3828.JPG

Yeah, that little piece of red wire is a sign of one of those minor design flaws.  The other problem isn’t as obvious from the photo.  I had originally intended the microcontroller to run at 1MHz on its internal oscillator.  But when I did, the barometer stopped playing nice with the microcontroller.  While I am able to run everything at 8MHz for a little while, the µc starts to become unreliable as the battery depletes.

Last time, I thought the battery was dead.  So I replaced it with a fresh one.  But, no matter how fresh the battery, it’s no longer transmitting.  The culprit seems to be some corrosion on a few of the radio module’s solder joints.

While I’m disappointed that the board has failed so soon, it’s given me an opportunity to address the design problems.

EAGLE screen shot

Here, I have a new board design with space reserved for a 4MHz ceramic resonator, a new expanded debugging header, and a completely redesigned board layout to better suit the orientation of the finished PCB in the plastic enclosure.

This still won’t solve the corrosion problem, though.  I found something called a “conformal coating”: a nasty chemical which claims to protect against moisture, corrosion, fungus, thermal shock, and static discharges.  I haven’t bothered with it before, mostly because I didn’t know that it was even a thing.  But even though it’s starting to cost me far too much money, this whole fake engineering thing is about trying new things and learning, with eye protection and safety gloves, of course.

With any luck, this iteration will prove to be far more reliable than the last one!

Triathlete, Tinkerer