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?

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.

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.

Tpyos

I have a problem with spelling.

It’s a bit like anti-dyslexia. I’ve seen friends stare at a page of text, totally unable to see the glaring errors in front of them. I don’t have that. I get the opposite.

When I look at a page of text, errors jump out at me. They’re distracting. I’m not trying to proofread, not trying to find problems. It’s them. They find me.

Of course, I’m as vulnerable as anyone else when it comes to text I’ve written. It’s hard to proofread your own stuff.

But it’s bad enough that I find it a problem to read on the Kindle. As soon as I see an error, I’m pulled out of the book. The carefully constructed fantasy world is jerked out from under me. I want to feed back to the publisher, so I think about how I could do that. I can’t.

I’ve taken to just highlighting the word. Then I move on. For the rest of the chapter, I’m not reading, I’m just checking words for errors. The next chapter, I can start reading again. But I’m not back into the fantasy world for another three chapters or so.

So I bought this book recently, written by an ex-colleague. It’s good – it’s really good. I want to write back and say I love how he sets the scene, how he builds his characters through their actions rather than simply describing what they’re wearing. How he doesn’t fall for the new-author fate of rewriting the first three chapters straight from a thesaurus, just to appear intelligent; but at the same time, I love the simple choice of words. “Raucous chowder” is a wonderful way to describe Fisherman’s Wharf.

I’d say this, and more, but I wouldn’t be able to say it without adding the soul-destroying “but”. He doesn’t want to hear the “but”. Nobody cares about the “but”.

But there’s a rogue “if” instead of “it” in paragraph eight. And it’s killing me.

And nobody needs to know that.

So maybe I’ll just be quiet.

Favourites

Matt Knox asked me this over breakfast the other day,
“What’s your favourite thing about being a dad?”

My brain spun. I didn’t have an answer. I think I fluffed a reply with something like “it’s not something you have favourites for, it’s something you live for.”

Yeah, that’s a bit lame. But it’s a tough question. I could’ve easily worked out a top-ten list of the things I didn’t like doing. Like emptying a potty. Ever tried that? Not pleasant. Not fun at all.* But favourite? No, too hard.

Anyway, I thought about it later.

What’s best about being a dad is being married.

Yes, of course, there’s all sorts of heart-warming moments with the baby. The arrival. The first smile. In fact, every smile. Talking, walking, crawling, holding. Just being able to hold and cuddle. Seeing parts of you. It’s a reason to live.

All that is granted.

But I couldn’t do this alone. I couldn’t manage. I’m shattered just feeding at nights and doing two playgrounds in a weekend.

Knowing that I have a soulmate to entrust with the little one’s upbringing is everything.

And actually, getting married is excellent preparation for having a baby. The stress, the organisation, the planning; coordinating relatives; coordinating jobs. It you can get through that together, you’re well prepared for the shock of having a baby.

Mind you though, what I miss most is: being married.

I miss whiling away the weekends doing nothing. I miss snuggling up on the sofa watching TV late into the night. I miss the lazy Sunday mornings in bed.

For now, for us, those times are gone. We’re in bed by ten at the latest, too tired to talk much. We don’t watch TV for fear of waking the sleeping one. Besides, it’s too much noise for us.

I miss my wife. I love my wife. I depend on my wife. Every day.


* Top ten worst things:
- emptying the potty
- lack of sleep
- dealing with escaped poo
- waking baby
- lack of sleep
- fear of the future
- lack of sleep
- lack of social life
- lack of sleep
- lack of sleep

Icebreaker

This was my first ToastMasters speech at Twitter. I ran over time a bit – I took 20 seconds over my limit of 7 minutes, and would’ve liked to have gone on a bit longer. I ad-libbed a fair bit. It was good fun. Not sure what to talk about for the next one.

Hello.

This is my icebreaker, so I get the chance to introduce myself.

My name is Kenneth Kufluk. I am one hundred and five years old, and Dick Costolo hasn’t spoken to me in six months.

But let me start with the name…

My surname is Kufluk, something I’ve always had trouble with. It’s unusual, even in Poland, where it came from. At school I’d get endless taunts of “toughluck kufluk”, and all sorts of mispronounciations.
One person says “Kufluk”, the next says “Kuflux”, the next says “Kooflux”.
For five years of high school, I had half the school laughing at me for being called “Cuthbert”.
But it’s Kufluk. It’s always been Kufluk.

Or so I thought.
Last year I published by Grandparent’s memoirs, and right there, buried in his stories of soviet invasion, cattle trucks and labour camps, and all the horrors of those times, he says that it was originally pronounced “Kooflook”. So now we know. I still prefer Kufluk, but I’m more relaxed about it now.

There’s nothing relaxed about my first name. It’s Kenneth. Nobody calls me Ken. Or at least, not twice.

I’ve been Kenneth my whole life, from when I was born until my 105th birthday this year.

Now I know what you’re thinking. 105? Is that his real hair? What’s his secret?
Well, yes, the hair is real. But there’s a story.

About six years ago I was a different man. Young, full of energy.
I’d been round the world twice.
I’d played in unicycle hockey world championships in japan, china and switzerland.
I’d sprint any distance.
I’d be out every night at the bar, picking up … well, picking up pints of beer, if I’m honest.
I’d actually been described as “a coiled spring”.

And then one summer, I found myself waking up at night with pain in my legs. I’d just walk it off and go back to bed. I assumed it was too much running.
But then it got worse.
Before long, a three block walk to the shops felt like miles. I’d come home and lie face-down on the bed, not asleep, but just exhausted.
No more running.
I’d find myself stopping on the way home from work, unable to go any further, but unable to think of any other way to get home.
This was chronic fatigue, something my “pull yourself together” upbringing had never really believed in.

Life got really hard.
I cancelled my first marathon, gave up on buying a house, and postponed my part-time degree.
But I still went to work.

And that was difficult too – not for exhaustion, but the constant questions “what did you do last night”, “how was your weekend”. I was face-down in bed at home, thanks for asking.

Little by little, month by month, I got better.
I learned to push through it.
Sometimes it got worse again, but overall I improved.

By the following year, I could get around.
I found that running was somehow easier than walking, and much easier than standing around.
So I entered the marathon again.

It was 26 miles of hard work. But I used what I’d learned – that you can push through exhaustion.
And I finished.
And I bought that house, finished that degree, and even proposed to my girlfriend.
But somewhere I’d still lost about a third of my life.

Five years on, and I’m cured. I still don’t like standing up for long stretches, or walking a long way. It’s like someone has turned up the gravity.
Some days are better than others. But I’m ok.
I’d say it’s made me feel about five years older.

Which, ok, only makes me about 40.
The real reason I feel 105 is because I have a six month old baby, which means I haven’t slept properly for, oh, about a year.

And why hasn’t Dick spoken to me?
Well, maybe it’s because I named my baby [REACTED].

Or maybe it’s because we’ve never actually really met.
One of those. I’ll never be sure.

The whole shebang

My colleagues have been amicably bickering, online and offline, about the use of hashbangs.

Ben Cherry explains that HashBangs are necessary, though not pretty, temporary workarounds to the lack of pushState support, which is part of the HTML5 spec.
http://www.adequatelygood.com/2011/2/Thoughts-on-the-Hashbang

Dan Webb explains that this temporary workaround goes against the fundamental rule that “cool URIs don’t change”, and that they are bad for the web in general.
http://danwebb.net/2011/5/28/it-is-about-the-hashbangs

When we discuss this sort of thing, we’re talking about the web in general, not about any Twitter pages, URLs, sites or strategy. Our personal sites are for personal opinions. Continue reading