All posts by Darryl

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
Advertisements

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!

The Multisport Event Formerly Known As Subaru Victoria

Success in triathlon is all about successfully carrying out a set of well-orchestrated tasks, in a specific sequence.  Whether you’re one of the professional elite, or whether you’re an enthusiastic age-grouper, it’s all the same.  Flawless execution of a race plan is what we strive for.

Of course, flawless execution is only half the battle.  Triathlon is as much about going through the physical actions, as it is about the mental discipline and preparation.  Leading up to the race, I spent a fair amount of time, creating a race plan.  Well, I can’t really call it a plan, because it It became more of an over-analytical checklist than anything, and landed as something of a race visualization guide.  I wrote my checklist, choosing words which created a mental and emotional picture of each step in the process.

So, while my splits (except perhaps for my T1 and T2 times) don’t come close to world-class, my performance in Victoria was a near-flawless execution of my race plan.  It was a successful race, in my books.  By the numbers:

500m Swim 10:04
T1 1:13
20km Bike 42:59
T2 1:18
5km Run 31:44
Final Clock 1:27:17

Package Pick-Up

I don’t think I’d normally include a comment about the package pick-up as part of a race report, but in this case, I must.  I felt that the organizers were more interested in making sure we all knew that the race was Ironman™-branded, than actually giving us useful information.  Especially for those of us racing the short course events.  The process of finding my race number, and making sure all the forms were filled out, was very disorganized.  Upon arriving at the athlete registration tent, I was told, “Go there first, and then come back.”  There was a small tent, crammed full of athletes trying to figure out their race numbers and which forms they needed to fill out.  It was as much a melée as a mass swim start.  Nevertheless, we were all able to figure it out.  But it wasn’t a great first introduction to the race experience, and in all honesty, I wasn’t mentally prepared to deal with it.  Fortunately (and I’m getting ahead of myself), the night’s sleep would help.

Bike Check

Once I had my race package, I went back to the car for my bike.  This is where the execution of my race plan begins.  This is the last chance I’ll have to make sure everything is in good shape before leaving my bike in the transition area overnight.

  1. Race number goes on the bike.
  2. Check that wheels are installed with quick-release levers at an appropriate tension.
  3. Check that brakes are well-aligned, and I didn’t forget to tighten the little screws on the brake pads.
  4. Get air in the tires.
  5. Take a quick spin to make sure everything is still in sound mechanical order.
  6. Make sure the gears shift smoothly and correctly.
  7. Choose an easy or appropriate gear for the start of the bike course (in this case, a short flat and a slight uphill grade).

Bike Drop-Off

After a successful test ride, I took my bike into the transition area.  I’m familiar with transition areas, and how little space is normally available.  I know to only bring the absolute necessities into transition.  But, when I found my race number on the rack, I wasn’t quite prepared for this.  What I found, was a clearance of at best 15cm to the bikes on either side of mine.  Not to mention, those stupid short racks that simply don’t work with my 58cm tri bike frame.  Fortunately, I hadn’t removed my rear bottle cages (as I have sometimes done in the past for a sprint distance), so I racked my bike off the rear of my saddle, so I’d be able to simply lift it out of the rack in T1.

I left transition, after trying to make sure that the adjacent bikes wouldn’t rub their icky metallic grossness on my carbon.  But I couldn’t quite shake the worry about being able to get my bike out and back in to transition without trouble.

Hotel Check-In and Dinner

Once the logistics were taken care of at the race site, it was off to the hotel to relax and get some food in.  We went to the hotel restaurant.  At this point, it’s worth noting that the adage, Never try anything new on race day, should be extended out to at least one day before the race.  This is all I will say about my pre-race dinner.

And then, after a suitable amount of time spent doing as little as possible, it was bedtime.  I set my alarm for some ungodly hour, and turned out the lights.

Race Morning

Setting up transition always seems to take me longer than it should.  But I think I’m getting the hang of it.  When I got into transition, everything was like clockwork.  I set down my backpack, and  went through it like a checklist, starting with the bike.

  1. Aero bottle on handlebars.
  2. Elbow pads on aero bars.
  3. Bike shoes un-velcroed and clipped into pedals, with elastic bands.
  4. Spare tubes and repair kit in one of the rear bottle cages.
  5. CO2 and inflator attached to rear of bottle cages.
  6. Gel taped to top tube.
  7. Helmet ratchet loosened.
  8. Helmet placed beside front wheel.
  9. Sunglasses, with arms out, inside helmet.
  10. Garmin mounted to bike, in Auto Multisport mode.

Then the run:

  1. Race belt in hat
  2. Hat behind bike helmet
  3. Bodyglide rubbed on shoes (inside edge of heel)
  4. Shoes on top of hat

And finally, the swim:

  1. Anti-fog applied to goggles
  2. Swim cap and goggles in back pocket of tri top
  3. Post-swim gel in back pocket of tri top

Once all my stuff was in the right place, I visualized all of the critical stages of the race.  I wasn’t really going through a checklist at this point; rather, drawing on my past race experiences.  And yes, everything was where it needed to be for my race plan.  My race day went perfectly.  While I didn’t win (nor even come close), I executed on my plan almost flawlessly.