Flight is the Right Choice for Your Existing Website

Andrew Krespanis
Tweet

At the beginning of 2014 I’d never even considered using Twitter’s Flight JavaScript component framework, yet here I am unequivocally stating that it’s the right choice for your existing website, which I probably know nothing about. I’d better explain myself while you ready your pitchforks for the comments section.

Let Me Paint You a Picture

Flight is what Twitter is made with. Flight doesn’t get much press because its specialty isn’t fancy single page app demos with data binding, but for real world web apps built on primarily server-side codebases. Its design is entirely, and solely, components and events. There are no instance variables. There is no magic. Data components fire events to broadcast data, while UI components listen to data events and in turn fire their own UI events.

Flight components are exceptionally decoupled, in no way “take over” your page (unlike Angular’s ng-app), and are by their decoupled nature very easy to test, to migrate to/from, and to refactor. The cognitive load of inheriting maintenance of existing Flight components or refactoring existing components is dramatically lower than what is possible with Backbone or Angular and you don’t end up leaking or duplicating domain logic into your app like you do with Backbone or any JS framework which includes models.

Why Flight?

Your team has been working on a website for a few years. It’s primarily driven by a server-side technology — Ruby, PHP, Python, .Net — and that’s where you’ve solved the real domain-specific problems. Many of these solutions are the reason for your site’s success. Along with these server-driven features, you’ve continually added more JavaScript enhancements for improved interaction, snappier interfaces, that sort of thing. Maybe it started as jQuery spaghetti gluing together other people’s plugins. Maybe there’s some jQueryUI in there or Backbone, Angular, or Ember that performs well enough in their isolated corners of the site.

As these enhancements age and multiply, you start to notice a disturbing trend. While your business logic primarily lives on the server-side with its test suite and QA tooling (RIGHT?!), more and more is having to be replicated in the JavaScript layer. You don’t want to double up, but you have logic in your UI now and it needs to make the right decisions. Similarly, the custom components and formatting helpers you have accrued on the server need to be replicated on the client side to turn your API responses into correctly formatted views.

So now you’re at a crossroads. Do you continue down this path, replicating logic across two codebases and risk them getting out of sync, or do you decide to refocus your energies on an API-backed “thick client” approach with one of the big JavaScript application frameworks?

What about a third option – one that allows you to avoid rewriting your core business logic and view layer while giving you an extremely loosely coupled, lightweight JavaScript methodology that is highly testable, easy to understand and refactor, and most importantly allows to you gradually move from your mishmash of existing JavaScript features. What if this same alternative was just as easy to migrate away from if you one day decide it’s no longer the right fit, all the while allowing you to easily innovate by getting new ideas in front of your users quickly and with confidence that they’ll work as intended?

Option three sounds good to me. So how does Flight propose to deliver these lofty promises?

Everyone’s Talking, Everyone’s Listening

While you define your components in a style very much like regular classes (including have the this context bound to your component in event handlers), it’s not possible for any component to reference instances of other components. This means you cannot tightly couple APIs, nor any of the related mistakes possible through well-intentioned designs that organically grow out of control. Components may only communicate through events, which are either scoped to the DOM node the component is attached to, or the document. This is a Flight convention for broadcasting an event to anyone who might wish to hear it.

Due to this, a Flight component doesn’t know or care if it’s talking to another Flight component. There’s no expected interface, because there’s practically no interface at all. If you want a piece of your existing JS to send data to a Flight component, all it has to do is trigger an event with a name the component is listening for, and send the data (e.g. $(document).trigger('dataShoppingCart' cartContents)).

The Hardest Problem in Computer Science

The dataShoppingCart event name hints at the part Flight doesn’t solve for you — event naming. If every component is listening to and triggering all these events, how are you going to keep track of them? At least with a traditional instance-based API you can easily see what’s depending on what, and where the data is expected to come from. You must remember, however, Twitter made this for themselves. They’re not looking to make a framework that will guide beginners, they hire experienced developers and they have internal coding conventions which must be followed.

Coding conventions are exactly what prevents the event naming problem from getting out of hand. At the heart of it, there are two types of events — UI events whose names start with ui, and data events whose names begin with data. A data event is always the broadcast of newly available data, while a UI event represents a user interaction. For more guidance on event naming, Tom Hamshere has some tips on naming Flight events from his experience migrating TweetDeck to Flight.

Separation of Concerns

This UI vs data delineation continues into the components themselves, and it is here that I see the biggest payoff from using Flight in the way it is intended. Twitter’s example components for Flight are split into two distinct groups, components_ui and components_data. Data components know nothing of the DOM, and UI components never touch the network. By extension, user events are therefore only ever handled in UI components, so you don’t get data processing methods (e.g. XHR form submission) starting with e.preventDefault(), amongst other anti-patterns.

Entirely Practical Test-Driven Development

This separation of behaviour-specific and data-specific JavaScript provides payoffs to the developer(s) in reduced cognitive load when developing, and easier testing. Preparing and executing API calls happens in a data component, and is easily tested with jasmine-ajax. On the other hand, interaction and user behavior events are handled in a matching UI component and tested with jasmine-jquery and jasmine-flight. The specs for data components will load JSON fixtures representing API responses, while the UI components will load minimal HTML fixtures that also serve as the canonical reference for the markup expected by the component.

Flight Uses the DOM Rather Than Dominates It

Flight convinced me it was a great fit for real-world long-lived products the first time I used it (which was only a month ago). Myself and another developer on my team were working in parallel on an area of the UI which offers a tabbed collaborator selection, where the list of collaborators has search criteria that are applied asynchronously. I was implementing the tab panels and navigation as one UI component, and he was implementing the search controls and results list in another. When we were both finished I went to merge the two git branches, fully expecting both of our JS additions to be broken due to assumptions by the other.

It just worked!

Flight components don’t take any ownership of the DOM nodes to which they’re attached, nor make assumptions about the state of those nodes. There’s no temptation to tightly couple like this.el from Backbone Views, no ng-app from Angular — none of those possessive directives. Flight truly utilizes the DOM, it doesn’t mirror it, hijack it, or take an “our way or the highway” framework approach. Since then I’ve gone back and refactored the results list into its own component, and again this required no changes to the expectations or implementation of the neighbouring components. Our code is easy to test, and easy to change, and easy to understand for the next person who needs to read it.

Conclusion

There’s no shortage of appropriate use cases for the plethora of encompassing, capable JavaScript frameworks available. If I was starting a new web app where I knew at the outset the client would be very JavaScript-heavy, I’d likely go with something like Ember, Angular, or Backbone + Marionette. I’d use their routing, their opinionated approach to X, I’d implement it “The ____ Way”. But in the world I work in — one of long-lived web apps being developed and maintained by a diverse team — a component system like Flight is a perfect fit and an option I’d confidently recommend to any team in a similar position.

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • Vinoth Kumar

    Thanks for the article, tempted me to learn more about the Flight :)

  • Jingqi Xie

    Sounds great…