How to walk a marathon

I ran the San Francisco marathon two weeks ago today. Previously (back in 2008), I ran London. San Francisco is hillier than London, and it doesn’t take much of a hill to tire you out. After getting close to the coast at 15 miles or so, I took a few walking breaks.

And, to be perfectly honest, I had a lot of company. Not even from just the full marathoners, but from the halfers too – and they’d only been at it for a couple of miles!

When you get to the top of the hills at around the 20 mile mark, you’d think it’s easy. The truth is that your legs muscles are just blown. You’ve been running for 3 or 4 hours at that point, and you can’t even walk down the hills. You just limp along with a grimace.

At 20 miles, I quit. I sat down by the side of the road. I said I’d had enough.

But then I thought, what now? I could call a taxi and get to the end, but really it’s only five miles. I could just walk the rest. so I did. And apart from some muscle pains for the rest of the day (and week), and some foot pain ever since – it was fine. I wrapped up in just under 6 hours.

If you look around online you’ll get lots of really great help on how to run a marathon. This will be from those California fitness freaks. They’ll be all eating healthy and training and yoga and sensible and stuff. But there’s not much out there for the slackers. Where is the guide for the fat, the lazy, the unwilling, or merely too busy? Here are my tips:

  • It’s a good idea to do some actual training. I can run 2 miles to the Bart station. That’s my training. Twice a week for four months. I’d call it the minimum.
  • Don’t eat before you run. You’ll get cramps. Eat after.
  • You should do one long run as practice. I did Bay to Breakers, which is an easy 7 miles. Don’t walk this one.
  • If you’re getting blisters after half an hour running, you need new shoes. That’s why you need the long training run. Get new shoes and wear them in. If you can also wear them at work, then you don’t need to carry so much on your commute.
  • If you’re tired or hurting, don’t stress – just skip a training session. You don’t need an injury. You don’t need to run until you hate it. Just enjoy it.
  • Don’t turn up early for the race. Why get tired standing around? Be there 10 minutes before your start time – no more.
  • Run light – there’ll be plenty of water stops with electrolyte. Maybe even food. Carry as little as possible.
  • Don’t go mad with the water. Don’t go mad for the gels and electrolytes. Take a little here and there. Take your favourite candy for the 18 mile mark.
  • Run with a friend who matches your pace. Seriously more fun. You can people-watch together, admire the views of SF and complain about how bad the gels taste.
  • If you don’t have a friend, get your playlist ready. Have some variety – after 3 hours you’ll be stressed out by everything and need some chill.
  • Know your route – you’ll be able to look forward to the flat bits and know where you can pee.
  • Run London if you can – there are crowds all the way. It’s flat.
  • Walk if you must, but try to avoid it. Get into a rhythm, or follow someone at your pace. There will seriously be lots of walkers. You’re not alone.
  • Wear a visor or a cap. While the pros will finish before breakfast, you’ll be out running at noon. Sunburn is a problem, and so is the sunlight.
  • Don’t try and hop up and down curbs while you run – your legs will suddenly stop working sooner than you think and you’ll fall.
  • You’ll be running for 5 or 6 hours. That’s not a marathon, that’s an ultramarathon. You’re awesome and let nobody tell you any different. All you need to do now is finish.
  • Final note: don’t die. Take a phone for emergencies.

Publishing

I plan to publish a book for children.

It shall have a dust jacket, so that the books look crinkled and worn after a single use.

It shall have numerous title pages. It’s not enough to say the title on the cover alone. I would rather the title were repeated on the first page and second page too.

It shall have razor sharp corners, suitable for carving your name into diamonds, or for gouging eyes.

It shall have pages which are cut straight, such that grownups and children alike cannot turn a single page at a time.

It shall have popup controls that will be broken before it leaves the factory.

It shall have text that varies in both rhythm and rhyme. The plot will be absent of reason.

It shall be a classic.

2014: This year I have …

My annual End of Year update. See previous years: 2013, 2012, 2011, 2010, 2009 and 2008.

Last year, I made a long list of things I wouldn’t do this year. I only broke my promise on one thing: I bought a new car. Well, not a car – a truck. A big, beautiful, red, four-wheel drive, leather trimmed, all-american truck.

I was happy with that.

My son is now going to school, and talking like anything. It’s amazing to hear him put sentences together, have conversations, defend his turf, argue his position. Age three is great fun. And I think it will only get better next year.

He wears his Buzz Lightyear suit every single day.

My dad came to visit for the first time! We took him to Yosemite.

We drove up to Tahoe, and were surprised by 6 inches of snow overnight.

I finish the year at work with more than twenty reports. I’m now fully a manager instead of a coder – I’ve got five really strong teams of people I trust, and that’s excellent. I’m really enjoying learning to be a better manager.

Not that I don’t indulge in a little coding too. This year I made Twitter span three columns.

Next year, I plan to take a good holiday or two. I’d like to get more exercise. Maybe start running again. Maybe something different. And we’ll try and persuade more people to visit us.

Maybe we’ll get a cat.

Flight Mixins

We started building Flight.js at Twitter back in 2011. The brainchild of Dan Webb, it’s a component-based framework based on his previous frameworks polished with what we’d learned from working with Twitter’s existing codebase. One feature of the framework we wanted to include from the outset was Angus Croll‘s mixin model. His blog post on the subject is beautifully readable, and he’s also given a presentation on Flight’s mixin model at BrazilJS. It’s well worth reading both the blog and slide deck now, if you haven’t already.

Two and a half years later, with the whole of twitter.com now running on Flight, how do we feel about Flight and mixins? Are they working for us?

Rather than using the classical talking animals to describe our components, I’ll try to use real-life twitter components, though I’ll be dramatically simplifying for readability.

module.exports = defineComponent(customTimeline,
  withBaseTimeline,
  withOldItems,
  withMinMaxPagination,
  withNewItems,
  withTimestampUpdating,
  withTweetActions,
  withTravelingPtw,
  withItemActions,
  withUserActions,
  withGallery,
  withViewerFollowState);
function customTimeline() {
  this.defaultAttrs({
    itemType: 'tweet'
  });
}

Here’s an example of the code running our new Custom Timelines. As you can see, all the functionality is provided by our mixins. The code for our timeline is powerful, using delegated event handlers to pick up and handle most of the events sourced by the tweets themselves.

Precedence

We’ve always described mixins as “specialisations”. The idea is that you’d write a module, and then extend it with extra functionality.

For example,

module.exports = defineComponent(newTimeline);

We could then add functionality like infinite scroll:

module.exports = defineComponent(newTimeline,
  withInfiniteScroll);

But if we break apart this model a little further, then things get complicated. The mixin is able to wrap and override code in our newTimeline model.

function newTimeline() {
  this.updateTimeline = function() {
    // something
  };
}
function withInfiniteScroll() {
  this.around('updateTimeline', function(originalFunc) {
    // something
    originalFunc();
  };
}

However, this doesn’t really make sense. If we follow this model, then we need to define the core methods of timeline in every single component that wants to use withInfiniteScroll. We also have no way to extend or override the behaviour of the mixin without using another mixin on top.

A far better model would be this:

module.exports = defineComponent(withInfiniteScroll,
  newTimeline);

Putting the mixins at the start of the component definition will allow us to override any behaviour we want. This is much more like mixins used by other languages (Ruby or Scala used here at Twitter, for example).

If we apply this new model back to our original component code, we get this:

module.exports = defineComponent(withBaseTimeline,
  withOldItems,
  withMinMaxPagination,
  withNewItems,
  withTimestampUpdating,
  withTweetActions,
  withTravelingPtw,
  withItemActions,
  withUserActions,
  withGallery,
  withViewerFollowState,
  customTimeline);

This is a simple change, but gives far more power to the customTimeline. Flight gives us this flexibility because it treats all the arguments to defineComponent as mixins. If you’re using Flight.js in the wild, I would strongly recommend making this change.

Inheritance

One thing that’s very clear from our Custom Timeline example is that our mixins have become reasonably verbose. If we wanted to create another similar Timeline component, we’d have to duplicate that whole definition.

module.exports = defineComponent(withBaseTimeline,
  withOldItems,
  withMinMaxPagination,
  withNewItems,
  withTimestampUpdating,
  withTweetActions,
  withTravelingPtw,
  withItemActions,
  withUserActions,
  withGallery,
  withViewerFollowState,
  newTimeline);
function newTimeline() {
  this.defaultAttrs({
    itemType: 'tweet'
  });
}

Something we’ve done in a few cases is create mixin collections:

var compose = require('flight/lib/compose');
module.exports = customTimelineMixins;
function customTimelineMixins() {
  compose.mixin(this, [
    withBaseTimeline,
    withOldItems,
    withMinMaxPagination,
    withNewItems,
    withTimestampUpdating,
    withTweetActions,
    withTravelingPtw,
    withItemActions,
    withUserActions,
    withGallery,
    withViewerFollowState
  ]);
}

This will work, but I dislike having to reach into the framework and apply the mixins myself. This is also taking us back to the idea of inheritance:

module.exports = defineComponent(customTimelineMixins,
  customTimeline);
module.exports = defineComponent(customTimelineMixins,
  newTimeline);

Ultimately, I think there is some value in this combination of mixins and inheritance. In wider discussions with the team at Twitter, we took this further. Ideally, we’d like to be able to treat a defined component just as another mixin.

module.exports = defineComponent(customTimelineComponent,
  newTimeline);

This isn’t possible in the framework today, but conceptually there’s no reason why we couldn’t treat every component as a collection of mixins, and defer the creation of the component itself until it is actually used for instantiation and attachment. I’m hoping something like this will make Flight v2.

Modularity

One of the huge benefits of Flight is the enforcement of modularity. Each component is written and tested as a unit, and the only communication layer is through events.

However, large components are more complex to test, and when there are lots of mixins, it can be really hard to trace through the code, especially when advice is used liberally.

If you’ve got a lot of mixins, as we clearly do for our timelines, then you might be better breaking the component apart, into multiple components.

Our current timeline component is attached like this:

CustomTimeline('#timeline', options);

But there’s no reason why we couldn’t have done something like this:

CustomTimeline.attachTo('#timeline', options);
TimelinePagination.attachTo('#timeline', options);
TimestampUpdating.attachTo('#timeline', options);
TimelineActions.attachTo('#timeline', options);

Of course, we could attach those components within CustomTimeline:

function customTimelineMixins() {
  this.before('teardown', function() {
    this.trigger('teardownTimelineSubcomponents');
  });
  this.after('initialise', function() {
    TimelinePagination.attachTo(this.$node, options);
    TimestampUpdating.attachTo(this.$node, options);
    TimelineActions.attachTo(this.$node, options);
  });
}

However, as you can see, we have to be clever about handling our own teardowns here, which may not be worth the hassle.

Attributes

If you’ve used mixins at all, you’ve likely encountered the default attribute conflict. If an attribute in declared in a mixin, it cannot be redeclared in your component, or even in another mixin. Our aim was to prevent unintentional ‘clobbering’ of methods and properties, where two mixins use the same property for different purposes, producing unexpected and hard-to-debug conflicts.

However, I’ve often wanted to avoid this guard, to give more specific default attributes for my mixins in my component definition. To do so, we can define a new method, “overrideDefaultAttrs”:

this.overrideDefaultAttrs = function(defaults) {
  utils.push(this.defaults, defaults, false) || (this.defaults = defaults);
};

This will behave exactly the same as the defaultAttrs method, but will pass “false” to utils.push, which will allow us to overwrite the attributes when conflicts arise.

It’s tempting to redefine defaultAttrs itself, but I think it’s better not to redefine the framework for future compatibility, and the guard may prove useful in other places. The method is also blocked from redefinition when being mixed in.

To add the method above, one could either add it to the global component base (be sure to do so before any components are defined):

define(['./base', './utils'], function(base, utils) {
  this.overrideDefaultAttrs = function(defaults) {
    utils.push(this.defaults, defaults, false) || (this.defaults = defaults);
  };
});

Or simply create a mixin to be applied to each component as you need it.

We haven’t yet tried this in-house, but I think it would solve some readability and precedence issues.

Summary

Mixins have proved to be a vital part of Flight and twitter.com. Key learnings:

  • move your mixins to the start of the component definition
  • consider using overrideDefaultAttrs in component definitions
  • only use mixins where appropriate; use different components if you can
  • keep an eye on Flight for new features

2013: This year I have …

My annual End of Year update. See previous years: 2012201120102009 and 2008.

This year, we survived.

Yes, I know I said that last year. But it’s also how we feel. Exhausted.

Things are getting better. Our son is growing, learning new words every day. We might not have the sleeping situation sorted, but it’s still a fantastic feeling to have a family.

This year, we have also: bought a new car (my first new car), been to England for three weeks to see friends and family, been to Poland to see grandparents, moved out of San Francisco to a proper house in Walnut Creek with a garden, and endured the subsequent hundred-degree heat of the summer and the shivering freeze of winter.

At work, we’ve IPO’ed. Achievement unlocked. I’ve also switched from being a day-to-day coder to being an Engineering Manager, responsible for my team of six and twitter.com, the website. I’m still coding too. Well, deleting code. This year I deleted #NewTwitter.

We also got our Green Cards this year, so we can stay as long as we need.

At New Year’s, people make all sorts of resolutions and then fail to achieve them. This year, I’m going to take it easy on myself by listing antiresolutions. These are things that I would like to do, but not this year.

  • Run another marathon.
  • Do more programming after work.
  • Write a book.
  • Learn to play the piano. (long-standing antiresolution)
  • Buy another car.
  • Fly to England for a holiday.
  • Learn Polish.
  • Study for another degree.
  • Move house.

Wow, that feels better. What aren’t you doing this year?

Flying with atoddler

Catherine Webb recently pointed me to her outstanding 2008 post “Baby Holiday: SWAT Team special“. I felt it needed a little updating for the modern era. Here are our experiences from the summer. Not with a baby, but with a toddler.

If your toddler is under two, the airlines will try to force you to have the child on your lap the whole flight. Because your bollocks haven’t been kicked enough. However, you can call them and book a real seat. Well worth the extra cost.

If you read up on things, you’ll discover that you can bring your car seat. Be careful though – Virgin won’t let you bring a US car seat on the plane. They will let you check it and can give you a rickety leather seat of their own (reluctantly).

The reason why you want to bring the seat is so that you can have a belt he can’t escape from.

You’ll want to bring loads of baby stuff like food but remember the bastards at the security checks will have no sympathy for you. Low volumes of liquids in plastic bags only.

Once on board the plane, you may find the entertainment system doesn’t work in any of your seats. For eleven hours. Remember this is something of a nuisance for the cabin crew, who will become very unfriendly about it.

In the event of a trouser accident, the cabin crew will change the baby for you. Just press the button to ask them round and hand him over. It’s part of the cost of the flight, so insist if necessary.

Let me know how that goes.

Don’t expect toddler food for your toddler. Expect crap like crisps and sugar.

Don’t worry about the people in front, especially if they recline into your face the whole flight. Just smash and wallop their head. It’s payback time, and you have an excuse.

You can check a stroller at the door of the plane. But do remember to get it at the door when you get off, otherwise you can’t get back for it. It may also be at a different door, conveniently. The cabin crew will be oblivious to all of this so don’t worry them with it. They are probably very tired.

The simple solution to most woes is the iPad. Don’t leave home without it charged and loaded. Bring a few if you can.

Enjoy your flight. Chances are it will be mindnumbingly tedious. But, on the bright side, you’ll have a jetlagged toddler at the other end. Good luck with that.

Stories from New Zealand

I once went to New Zealand.

 

I was backpacking around the world, on my own, and landed in Aukland. I didn’t expect much – New Zealand is small, perhaps not geographically, but in terms of population. There’s only about 4 million of them.

There was actually a huge new modern hostel for backpackers there, called Auckland Central Backpackers. It was easily the nicest hostel I stayed in. Underneath was a club/bar sort of thing.

One night I was down drinking a few pints with a small crowd of fellow travellers. Some guys just finished up a karaoke song on stage, and passed us by. We laughed. Not actually because of them. We had complained about the noise at the start maybe, but you know, we’d moved on. We were laughing about something completely innocuous. However, one of those karaoke guys took offence.

At closing time, we left and found our karaoke friends again, who attacked us. The guy in front of me simply folded up after one punch. The other got into a protracted fistfight. After the bouncers had chased them off, we found him on the ground, his face smashed up horribly. Turned out they’d used knuckledusters.

 

I thought it was time to move on, and on a whim, went to a bike shop. On impulse, I bought a bike, a helmet, a speedometer, and a book of cycle trips.

The next day I decided to try out the new wheels. I set out for Auckland Harbour Bridge. It’s big. It’s famous. They do bungee jumps from it, so I knew there’d be a safe walkway.

I head for the bridge. As I take the last turn, it says “Motorway starts”.

I don’t turn back here, because the traffic is too busy. Besides, do they really have motorways here? It’s a tiny place.

And the sign is green, not blue. Blue means motorway. At least in the UK.

And anyway, I can always hop off onto the walkway if things get tricky.

Forty-five minutes later, I reach the other side and pull over in front of the waiting policeman, as the police car escorting me for the last third cruises off.

“What was that in aid of?” he says.
“Why, what do you mean?” I ask, all innocent.

It all got sorted out in the end, but it turns out that, yes, they have motorways, yes, the bridge is one, and no, they do not have a walkway of any kind.

 

At this point, I decide the leave the city. I decide to cycle the Northland for ten days. It’s all planned out in my book. Point to point. Hostel to hostel.

The first day went without a hitch. I got a train to the first hostel and cycled a few miles. Next day, I was doing fifty miles.

I woke early, filled my water bottle and headed out. It was a glorious cycle. I loved the exercise, since so much of travelling involves sitting around, or walking and gawping. Proper cycling is good for me.

After a few hours, I realised I was in trouble.

The bike was fine. It was me. I’d run out of water.

I wasn’t thirsty, but my legs had stopped working. I started walking the bike up the hills, and then just walking all the way. I was getting worried.

What do I do? Flag down a passing car? But there weren’t any! Do I look for streams? Can I suck water out of trees somehow?

Eventually I found a farmhouse, And, rather red-faced, I asked for some water. They obliged The next day I prepared better.

 

On the sixth day, I cycled across the top of some hills. It was really hard work, cycling at forty-five degrees into the cross-winds laced with rain. The only traffic were huge double logging trucks, always going too fast, too close. It was horrible.

Eventually I got to my stop, and got some bad news.

Firstly they were closed. Secondly, they were now a luxury retreat, not a hostel. Rooms cost $250, and that’s if they were open, which they were not. They said I should stay at the camp down the hill for $10, and called for me. But I had no tent or bed, so they wouldn’t let me camp.

I was in a pickle, and there was no way I was going back the way I’d come.

Luckily they caved, and let me stay. For the discounted rate of $200. And I have to say, it was pure luxury: sipping brandy from a large glass in front of the fire luxury. I slept well that night.

 

But not so well the next night.

I found the next hostel. But the sign had been painted over. This place had lost it’s International Youth Hostel mark. Hmm. And everywhere else was booked up. Hm. So I went in.

The manager emerged in a cloud of smoke. And not cigarette smoke. The more exotic kind.

I found the place inhabited by the homeless. By drunks. It was not pleasant. I got a single room anyway, and stressed about the twin beds in there. They wouldn’t sneak anyone else in would they? The room had a bad smell.

At about two am, I woke to find one of the oldest smelliest drunks, just lying next to my bed by the window. Watching me. He stank. I froze.

All night I froze, I wondered what to do. I fumed. I tried not the breathe. I was outraged. How could they do this?

As early as I could, I decided to just get out. Not stay for breakfast, for shower, nothing. Just get out, get away from this guy.

And that’s when the curtain moved, and he turned out to be just a shadow.

 

I’m sure there was more to New Zealand. Hobbits and scenery and beaches and huge trees.

But these are the stories I remember.

Orange robots

Bought a new Orange Pay-as-you-go SIM.
Stuck it in the phone.
Tried to add credit.

ME > 450 (send)

“Welcome to Orange. Press 1 if you’d like to top up.”

ME > 1

“If you’d like to top up with a credit or debit card, press 1, …”

ME > 1

“If you’re topping up with a voucher, press 1 now to return to the start. Otherwise, hold on.”

“Ok, let’s top you up. What’s your postcode. Just say the post code.”

ME > “E17 7QW”

“Ok. And the house number?”

ME > “47”

“Ok. I’ve got 47 Penumbra Avenue, London, E17 7QW. Is that right?”

ME > “Yes.”

“Ok. Now, what is your last name? Just say it out loud into the phone.”

(stunned)
ME > “Kufluk”

“CUTNER. Is that right?”

ME > “Well, no, it’s not likely to be right is it – you’re just a computer”

“Ok, let’s try again. What’s your last name?”

ME > “KUFLUK”

“CLARK. Is that right?”

ME > “NO!”

“Ok, well, I can’t figure this out, so I’m going to have to pass you to customer services.”

“Sorry, I can’t ocnnect you to customer services because you don’t have any credit. Calls cost 25p per minute.”

“Would you like to top up now?”
“If you’d like to top up with a credit or debit card, press 1, …”

ME > ARGGGG!

I hang up.

When I called back later, I admitted to being Mr Elliot Clark. I confirmed that this was indeed the name on my credit card. I successfully added 20 pounds to my account.

To avoid confusion in future, I will be responding solely to this moniker. I would like to thank Orange for simplifying my affairs in this regard.

A day in the life of atest failure

I love my job. Fact.

But some days are easier than others. If you’ve been reading my tweets this week, you’ll have sensed a certain frustration.

Don’t worry. None of this is your fault.

But here’s what happened.

Over the weekend it had occurred to me, while I was asleep at about 6am – seriously, this happens – it occurred to me that I’d forgotten to submit my latest test fixes before I merged my branch. This would screw up the branch for the weekend, for the whole team, and delay the deploy on Monday. Bad news.

As soon as I woke up, I popped open the laptop, preemptively apologised to all and sundry – as is the English way – and proceeded the fix the problem. It turned out my fixes only fixed half the problem. And it’s the weekend.

So, I cheated. I knew this code was in a hidden section, and wouldn’t be live when deployed. I could just comment out the tests, ensuring the branch would pass even if the code didn’t work properly.

First thing Monday morning, I returned to the problem. I looked back over the logs to find the faulty test and dutifully kicked off a test run.

And this was the start of my problem.

It passed.

I was a little surprised. There were failures last week, I could see there were failures. Maybe somebody else fixed it. I check the logs. Nope. I run the tests again.

It passed.

I should mention that running the tests isn’t instantaneous. It’s a complex test suite we’re running here. We’re currently migrating our server code away from Ruby (we’re always migrating to faster systems). In the interim, we’re running tests against both systems. The various compilation and startup sequences, combined with the real browser used, and the queues of other developers waiting on the system, gives us an average runtime of about 15 minutes.

It’s optimised to run a suite of tests, rather than individual tests, so both cases take around the same amount of time to run. In fact, we have some excellently comprehensive test suites with thousands of tests, and they can all be run in about 15 mins. Running single tests is still slow, because the task can’t be distributed.

From this point on, I’ll indicate the test timings like this: (15).

I can of course run a test locally. Again, this can take time. Usually less time – let’s say 5 minutes or so. It takes less time because I already have the servers running locally.

So my tests are passing. Usually, a cause for joy. In this case, a worry. Why did they fail last week? I dig out the code from last week, and run the tests (15).

It fails, as expected.

Well, that’s something. I diff the code. And here I win the Goof of the Week award. Prize chump right here. The tests are passing because I commented them out.

Duh.

I go to lunch and try to apologise to my own brain cells for letting them down.

I kick off a real test run (15). It fails. Good. But weird – I swear these were working once. I test the Ruby code instead (15). It passes.

Interesting. So I’ve found a difference. This is weird because I haven’t actually changed any of the server code. It shouldn’t be possible to be different here.

What’s worse is that we can’t easily test the new code locally. It takes longer. I run them (10). They pass. I run the Ruby tests (5). They pass. I run the new tests again, to be sure it’s not random. Pass (10). Pass (10).

I spike the code full of debugger statements. Run the tests (15). More debugging (15). Add screenshots (15). Everything is the same between systems, except that the new tests are failing.

” is not true”. Thanks, error messages.

Hmm. I look back at the screenshots. While the systems show the same screens, there’s something I didn’t expect. When you’re a new user on Twitter, we show a little helper module at the top of the page. I’d forgotten we did that. It also loads asynchronously.

Aha. Here it is. The new code runs so much faster that this module loads earlier. This explains the difference. I go home, happy that I’ve established the problem.

Next day, I make allowances for the helper module. I pull out the debugging code, happily run the tests (15) one last time, and prep the branch for shipping.

It fails.

Bollocks. I unprep the branch and put the debugging code back in. I run the tests (15).

It fails. Same debugging result as before.

That’s weird. I was so convinced the module was the problem. I hack the code and remove the module entirely. Another run (15).

It passes.

Ok, so it’s the module. No question. I just got the allowances wrong. I tweak them, and run (15). Fail. I add debugging (15). Nothing. And again (15). No.

This is crazypants. I am so not happy.

I try to reproduce locally. Passes (10) every (10) time (10). No amount of debugging and breakpoints will help if I can’t reproduce the error.

I look back to the screenshots. Aha. No images in the module.

I knew the error was caused by an bad height calculation. And the missing images are (whips out an onscreen ruler), yes, the images are 137px – exactly the difference.

Eureka.

Our images don’t load on the testing machines because they’re insulated against the outside network. That makes sense. Locally, we can access the images, which must be why they pass. It’s perfect.

Furthermore, our testing machines are using Firefox, which won’t render broken images, while I’m using Chrome locally – which does. This would account for the height difference.

I curse and laugh, since this quirk of Firefox has bitten me many times in the distant past.

To fix the test, I simply break all the images – to be consistent. I prep the branch for shipping, throw it out to the test cluster one last time, and go and get a fifteenth cup of coffee.

It fails (15).

What. The. Fuck.
What. Thefuck.
Whatthefuck.
Fuck.

How can this be? I had this nailed.

Worse than that, the tests is now passing occasionally. When I run the tests on the test cluster, I run them about sixty times in parallel. This ensures we don’t get “flaky” tests, which sometimes pass and sometimes don’t. Flaky tests are notoriously disruptive and hard to fix.

Sometimes, all you can do is try it yourself. I open a browser. I downgrade it to the exact same version as our test cluster. I’ve done this before, but I’m doing it again. I run the tests (10). I click around. I fiddle with the code. I break the images, to match. Refresh, refresh, refresh.

Hey, what was that?

I refresh again. And again. There it is.

Firefox won’t render a broken image – it hides it away completely. I knew that. But what I’m seeing here is a flash of broken image. There’s a flash of a broken image in this helper module, before Firefox hides it away.

If my code is making calculations just at that moment, it’ll get a different result than, say, half a second later.

I don’t get my hopes up again. I tweak the test. There’s actually a little css property:
-moz-force-broken-image-icon:1;
This will prevent Firefox from hiding the broken images. I run the tests. All the tests (15).

Pass.

I prep. I ship. I smile.
I move on to the next thing.

(btw, if testing tools is your kind of thing, and you want to help me make things faster, we’re hiring and I will buy you cookies)

Waiting for Bill

I throw another log onto the fire. I’m not sure we really need it – it’ll be warm enough all night long. Out here in Palm Springs, it’s only bearable when the sun sets anyway. The fire simply gives us something to watch while we wait.

Bill Gates is due for dinner. Most of the other guests have already arrived, so he should be next. Or the one after.

I poke the fire. Sparks fly. It’s been a few good hours now, and I still haven’t decided.

Do I shake his hand, or kick him in the fork?

This was about ten years ago. I’d never suggest or condone violence in any form these days. Hell, even then I wouldn’t really. So let’s moderate the language.

Do I shake his hand heartily, welcome him like an old friend, thank him for all he’s done for my career, and propose my next big idea? Or do I ignore him, snub him, turn the other cheek?

I should clarify my position. I don’t have dinner with billionaires. That doesn’t happen. I’m really not that kind of guy.

I’m here on holiday. Vacation, if you will. I’m visiting an old friend from London, who is originally from Perth, who is currently working as a hot air balloon pilot in Palm Desert. And in the evenings, he works as a parking valet at The Reserve, an exclusive golf club in Palm Springs. And, since I’ve got nothing else to do, I’m here to help.

I say “parking valet” to emphasise that I’m using the American meaning of “valet”. Someone who parks your car. Not the English meaning, which you might be familiar with from Downton Abbey, of someone who dresses you in the morning.

To make this slightly more bizarre, I can’t actually drive. No license. Never even sat behind the wheel. I’m opening the car door, welcoming them, opening the restaurant door, that kind of thing. Oh, and I get to drive the golf cart, ferrying my fellow valets back from the car park. This is a nice place – you expect your valet to be fast. This is a classy place – even the valets have a little nook with a fireplace.

When I arrived, they told me Bill Gates was booked for dinner. Yes, that Bill Gates. That one.

Obviously I was somewhat in awe. He was, I believe, the richest man in the world at the time. And I’d been working with his software for years. My job, my life, was founded upon it.

And yet, that brought so much trauma. I was out here for a much-needed break. I’d been fighting poorly implemented APIs, buggy applications, incomprehensible error codes, absent or irrelevant documentation, and that blasted comedy paperclip. In no small way did I hate this software as much as I loved it. I should snub this man for all the suffering he put me through.

But what a wasted opportunity. I should be pitching an idea. I’ve got maybe 20 seconds with the most powerful man in technology. I’ve got to plant a thought in his head, one that will distract him over dinner, so that he’ll emerge with a warm smile and an offer of partnership.

All I needed now was the idea.

Ideas are funny things. After a few pints, or several sleepless hours, your mind is full of amazing, wonderful, life-changing, world-changing, revolutionary ideas. When you’re put on the spot, nothing.

Fast forward to the future, and hindsight brings the answers. I could’ve pitched Twitter. Facebook. Myspace. The cloud. Groupon. Well, maybe not Groupon. Instagram. The iPhone. I could’ve begged him to continue development of IE.

If you were put on the spot, right here, right now – could you pitch? What’s your idea? Are you ready?

I wasn’t ready, but that was ok. Bill never showed up.

The other guys were fine with it. Apparently he’s not a big tipper.

Since then of course, he’s donated most of his wealth to charity. Seems like a generous tip to me. I’d heartily shake his hand. Or fist bump. Or whatever he’d prefer.

And he tweets. I build the software he uses every day. That’s pretty awesome.

Maybe it’s a good thing he didn’t show.