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.

Apple ideas

Here are my predictions for Apple products this year.

No, hang on. Not predictions. More like hopes and desires. Wishes.

1) Embeddable products.

I have just not bought a Honda CRV. I’ve been driving one as a zipcar, and I love how it drives. We need a family car, so it’s an obvious choice.

And then I saw the screen on the website.

Screen Shot 2013-02-16 at 19.13.10

No way. It’s 2013. I’m not using a computer system from 1980. I’m just not.

You should be able to slide in an iPad mini, and have it control all the car’s features. This shouldn’t be exclusive to luxury cars. This should be across all cars, all brands, all prices.

The same is true for aeroplanes. We took a Virgin America flight a few months ago. One of the seats was empty (which was awesome), but why? The entertainment didn’t work. If they’d used embedded iPads, they could have just switched it out. And we’d have apps. And the games wouldn’t be shit. And they’d be interactive adverts for Apple products, with a 100% captive audience.

2) Touchscreen laptops

Ok, I get it. A touchpad works better on a laptop. But, having used a Surface for a bit, I’d still say a touchscreen is useful.

It’s useful when pairing to be able to say “that word there”, or “push this button”.

It’s useful to scroll, sometimes, when reading. Your right thumb just flicks the content down. Feels natural.

And they seem to be both ubiquitous and inexpensive. So why not?

3) A radical design change for iOS

I have an original iPod Touch. The iPhone wasn’t released in the UK for about a year after the US, but the Touch was. I’m still blown away by the completely unnecessary brilliance of the device. It’s so slim, so solid. The screen is amazing. The touchscreen was unlike anything anyone had seen before. It had an accelerometer, even though there was NO USE FOR IT until the App Store arrived. It still works.

But it looks exactly the same as the new ones. I know great design is forever, but is forever really longer than five years?

4) iTunes

Just break the videos out of iTunes. Call it iVideo or iView or something. It doesn’t make sense where it is.

While we’re there, let me opt into iCloud fully. I *do not want* to download everything. Ok? Ok.

5) TV

Jus do something amazing. Typing using a remote control is madness. Give me Kinect.

Don’t give me Siri. I don’t want to shout at my TV. Seriously, the tech pundits are idiots. Don’t go there. Let it go. Put it in a watch, where I don’t have to care about it.

6) Buy Sonos

Simply to acknowledge that their system is awesome in a way that Apple showed us was possible. And it needs iTunes.

That’s it. I’m happy with the products as they stand. They don’t need another iPad/iPhone/iPod idea. They make money. We love the stuff. Keep it going.

Oh, and I’d love a phone the size of an iPod nano. Now that would be amazing.

2012: This year I have…

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

This year we have:

  • Survived.

Last year I said I’d take it easy this year. That’s not so simple with a baby. But I think we’re slowly getting on top of things.

I have also worked solidly on twitter.com, making it faster and more robust. I’m giving the New Hire Orientation talk for frontend. I’ve got the hang of how things work. I interviewed eighty people. We moved offices. We got a roof garden and a proper kitchen. Twitter is all grown up.

We went to Texas, our first real trip with the baby. And we made it.

We had a weekend in Monterey.

Next year, I plan to buy a car. A new car. My first new car.

And I’d like to go home to England for a few weeks. See some old friends and new family.

Imagine

I’m English.

England is vast. Despite holding about fifty million people, most of the land is still dedicated to farming, and enjoys large exports to the rest of the world.

We complain about the variable weather that always comes with island life, but it gives us acres of fertile soils. From the air, the land looks terraformed: neat rectangles of colour.

Fifty million people is also a huge populace, mostly living in cities. The cities are vast and sprawling, with thousands of years of history behind them. Your local church can be a thousand years old as easily as a hundred. You can travel from town to town, city to city, with barely a break in housing.

An hour’s drive is a good trip. In ten hours, you can cross the country, even the long way. Rail travel is good, expensive and sometimes unreliable, but generally good. Motorway travel is excellent. Speeds are fast, accidents are infrequent.

Wherever you travel, you are surrounded by people. At most, you could be half an hour from people, a few miles from people.

I live in America.

American cities feel like English cities. There’s that mix of all-comers, the blend of cultures thrown together. Buildings, housing, stacked closely together. People rubbing shoulders on the bus.

Further out, to the suburbs, there is still very little difference. The houses are generally a little bigger. They’re less likely to fit the upstairs/downstairs template of English life. They’ll have proper garages. They’re made of wood. The urban sprawl surrounds the cities, giving families some breathing space and distance from the bustling city nightlife.

Further out still, there is more difference.

Because America is absolutely, staggeringly, incomprehensibly, mindbendingly vast.

Looking out of the window at the Rockies as you travel here, you can see the breathtaking landscape still raw and scarred, like the surface of Mars. It’s enormous, it’s beautiful, and it’s empty.

Travelling from city to city, it’s easy to forget this. Close the window, close your eyes, and move seamlessly from one identikit airport to the next.

But out there, people live. They live off the land, they live different lives. Farm fields roll over the horizon, without limit. Farmers have planes.

How far can you be from other people? How far can you be from help? How far from the nearest mobile phone tower?

If your family lives on a farm, miles from anywhere, how can you feel safe? Who will be the next person to drive by? If trouble came knocking, how long before anyone came to ask if you are ok?

England has a wonderful position of security.

America is different.

I don’t support the gun lobby. I believe that fewer guns mean fewer deaths. But I’m disappointed in polarity of argument, and the lack of imagination in recent commentary. One should try to see both sides of the picture to find where to draw the line.

I’m not making a point one way or the other. I’m saying: think.
Use your imagination.