Top Banner
e State of Front-end At CrowdTwist
76
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: The State of Front-end At CrowdTwist

The State ofFront-endAt CrowdTwist

Page 2: The State of Front-end At CrowdTwist

Different apps with lots of code...☞ FanCenter: 22k (SLOC)

☞ Control Center: 12k

☞ Widgets: 9k

Page 3: The State of Front-end At CrowdTwist

...written in various libraries and frameworks...

☞ FanCenter: Backbone.js, Marionette.js, Geppetto, RequireJS

☞ Control Center & Widgets: AngularJS

Page 4: The State of Front-end At CrowdTwist

...using different tools.☞ FanCenter: Crusher (in-house build tool), Mocha

☞ Control Center & Widgets: npm, Bower, Grunt, Express, Karma

Page 5: The State of Front-end At CrowdTwist
Page 6: The State of Front-end At CrowdTwist

So what?

Page 7: The State of Front-end At CrowdTwist

Growing painsAs a new member of the team, I would like to learn CoffeeScript, Jade, Sass, as well as Backbone, Marionette, Geppetto, Angular, Grunt, and Crusher, so that I can develop new features.

Some of these may seem relatively simple to pick up and start using, but it becomes hard really fast to write scalable and maintainable code across the different apps and frameworks.

Think bloated views and spaghetti events in Backbone and custom Angular directives with crazy $scope magic and deep equality checks.

Page 8: The State of Front-end At CrowdTwist

❝Just because you're using Backbone and Angular doesn't mean your code has to suck.❞—a reasonable developer

Page 9: The State of Front-end At CrowdTwist

To which I respond...

True! But... it makes it harder to grow an idiomatic codebase. Each framework has its own special sauce and set of rules you need to understand and follow if you want to write maintainable code.

Page 10: The State of Front-end At CrowdTwist

Unless, of course, every developer is a rockstar and their thought process is identical to mine!

That's how it works, right?

Right!?

Page 11: The State of Front-end At CrowdTwist
Page 12: The State of Front-end At CrowdTwist

How did we get here?☞ Magical frameworks

☞ False separation of concerns

☞ Nascent tools and rapidly evolving ecosystem

Page 13: The State of Front-end At CrowdTwist

MagicalFrameworks

Page 14: The State of Front-end At CrowdTwist

Frameworks

Frameworks make certain assumptions about how an application will function, providing tools out of the box to greatly simplify the development process as a whole.

Tend to stress thinking less and doing more, faster.

For certain use cases, this is a good solution, eg. basic CRUD interfaces, smaller single-page apps.

Page 15: The State of Front-end At CrowdTwist

Frameworks

When dealing with larger applications and growing pains described earlier, it is valuable to ask the following question:

What are the costs / consequences, if any, of coupling your application to a framework?

Page 16: The State of Front-end At CrowdTwist
Page 17: The State of Front-end At CrowdTwist

Frameworks

Consider a form view leveraging a custom directive to render a dynamic list of inputs:

<div ng-controller="SurveyCtrl"> <form name="surveyForm"> <question-list ng-model="survey.questions" /> </form></div>

Now ask yourself, who owns the view(s)?

Page 18: The State of Front-end At CrowdTwist
Page 19: The State of Front-end At CrowdTwist

SeparationofConcerns

Page 20: The State of Front-end At CrowdTwist

Separation of concerns

Separating concerns, such as making requests, validating forms, and rendering dynamic inputs, into separate components makes sense.

Directives make sense, as do route controllers and form validators.

The problem is how these components communicate, the implicit dependencies between them, and being able to answer the original question of who owns the view?

Page 21: The State of Front-end At CrowdTwist

Separation of concerns

If Robert Plant comes along and wants to add a new feature to the existing functionality, he would first need to:

☞ Understand how nested object properties affect a child's $scope

☞ (Kinda) understand the differences between scope, child scope, isolate scope, transculsion

☞ The evils of ngInit and its partner in crime $scope.$watch

☞ $modelValue → $formatters → $viewValue → $render $modelValue ← $parsers ← $viewValue ← $setViewValue

Page 22: The State of Front-end At CrowdTwist

Separation of concerns

This magic makes AngularJS a simplicity trap.

Angular.js is attractive when coming from Backbone.js where you have to do everything yourself (not an ideal situation either).

The problem is you relinquish a non-trivial amount of ownership and control over your application in hopes of the magic paying off and a false sense of simplicity.

When dealing with large applications that consistently grow in features with an increasing amount of developers, ownership and true simplicity become even more important.

Page 23: The State of Front-end At CrowdTwist

Here, here, Angular...You're not so bad!

Page 24: The State of Front-end At CrowdTwist

You're just growing up so fast...Mommy can't keep up with your needs!

Page 25: The State of Front-end At CrowdTwist

No, seriously, what's up with that?☞ The API is radically different

☞ It's essentially a new framework

☞ No support for IE 8 (no one wants to, but we still need to)

☞ Did I mention it's a completely new framework?

We just started building our Angular apps last year!!

Page 26: The State of Front-end At CrowdTwist

RapidlyEvolvingEcosystem

Page 27: The State of Front-end At CrowdTwist

Change is good

That's how the radical notion of front-end development came to be, as well as amazing tools like jQuery, Backbone and Angular.

Page 28: The State of Front-end At CrowdTwist

Stability is better

Is the tool simple, small and predictable enough to use now and still support next year?

Turns out that was a hard question to answer last year.

And it's still hard to answer today!

Page 29: The State of Front-end At CrowdTwist

...they say

Page 30: The State of Front-end At CrowdTwist

But I'm feeling more optimistic it's not going to be for long.

Page 31: The State of Front-end At CrowdTwist

Let's talk about JavaScript

mmm vanilla

Page 32: The State of Front-end At CrowdTwist

The language is maturing

This means we can simplify our toolchain and remove additional languages like CoffeeScript.

We don't need CoffeeScript, it just makes JavaScript better.

But now, JavaScript is making JavaScript better!

Page 33: The State of Front-end At CrowdTwist

The ecosystem is maturing

Page 34: The State of Front-end At CrowdTwist

The tooling is maturingimport {get} from "common/request.js";

class MyComponent {...};

export default MyComponent;

Page 35: The State of Front-end At CrowdTwist

I know what you're thinking

Page 36: The State of Front-end At CrowdTwist

❝JavaScript is great and all, but I need to write an app and I'm not gonna start rolling my own router, MVC library and templating solution❞—a reasonable developer

Page 37: The State of Front-end At CrowdTwist

Let's talk about React

Skeptical? I was too...

Page 38: The State of Front-end At CrowdTwist

Library? User Interfaces?

What year is this, 2006?

Page 39: The State of Front-end At CrowdTwist

There has been a lot of wizardry recently

Page 40: The State of Front-end At CrowdTwist

React may have just found the sweet spot

Page 41: The State of Front-end At CrowdTwist

Back to basics

Instead of building controllers to manage views that depend on other controllers that manage views that manage templates that manage DOM, just build self-contained Components.

Page 42: The State of Front-end At CrowdTwist

import React from "react";import Question from "views/question";

let QuestionList = React.createClass({ render() { let questions = this.props.questions.map(question => { return <Question data={question} />; });

return ( <div class="questions"> {questions} </div> ); }});

export default QuestionList;

Page 43: The State of Front-end At CrowdTwist

❝Dude, is that HTML in your business logic? WAT?? You know you're not supposed to...❞—a reasonable developer

Page 44: The State of Front-end At CrowdTwist
Page 45: The State of Front-end At CrowdTwist

Not quite.That HTML is actually JSX, a syntax that is essentially XML and allows for the DOM to be composed alongside your logic.

If you think that's breaking all the rules, newsflash. You're doing it already.

Page 46: The State of Front-end At CrowdTwist

unwatch = $scope.$watch('survey', function(survey) { if (survey) { unwatch(); $scope.survey.hasQuestions = survey.questions.length > 0 ? true : false; }});

<div ng-controller="SurveyCtrl"> <form name="surveyForm"> <input type="text" ng-init="survey.title = survey.title || 'Default'" ng-model="survey.title"> <input type="checkbox" ng-model="survey.hasQuestions"> </form></div>

It may not be as obvious as JSX, but the coupling is still there!

Page 47: The State of Front-end At CrowdTwist

With React, you at least get all the dependencies isolated to a single component in a single file.

That is ownership.

Page 48: The State of Front-end At CrowdTwist

If you still oppose the idea of JSX, you can use React without JSX:

React.createElement(Question, {data: question}, "innerText");

Page 49: The State of Front-end At CrowdTwist

CompositionYour component can render other components!

<QuestionList> <Question /> <Question /></QuestionList>

(without the complexity of isolate scopes and transclusion)

Page 50: The State of Front-end At CrowdTwist

Speed

You may be thinking the (re)rendering of these components is slow, but React is smart.

React operates on the DOM in-memory before making the smallest set of changes to the browser's document.

This is facilitated by a straightforward definition of state.

Page 51: The State of Front-end At CrowdTwist

State

A component's data is managed by 2 objects, props and state.

props is the configuration of your component passed in as attributes. It is immutable.

state is private to that component and defines the mutations a component undergoes, typically causing re-render.

Page 52: The State of Front-end At CrowdTwist

State

let QuestionList = React.createClass({ getInitialState: { hasAnswer: false },

processAnswer(answer) { this.setState({ hasAnswer: true }); }});

<QuestionList title="Answer these!" />

☞ title is a prop that is passed into the component

☞ hasAnswer is part of the state of the component

Page 53: The State of Front-end At CrowdTwist

State

With React, you know the exact state a component is in at any given point in time, and the UI will reflect that consistently.

With Angular, it is a lot harder to tell what the state of a particular $scope is, depending on ongoing $digest loops, $watch calls, and other magical constructs.

Page 54: The State of Front-end At CrowdTwist

Used in production

Page 55: The State of Front-end At CrowdTwist

There's more...

☞ Check out http://facebook.github.io/react/

☞ Play around with it

☞ Form your own opinion

Page 56: The State of Front-end At CrowdTwist

How could weuse Reactat CrowdTwist?

Page 57: The State of Front-end At CrowdTwist

Quick look at FanCenter

White-label web app for user engagement with brands

Users complete various activities to earn rewards

Page 58: The State of Front-end At CrowdTwist
Page 59: The State of Front-end At CrowdTwist
Page 60: The State of Front-end At CrowdTwist
Page 61: The State of Front-end At CrowdTwist

From a dev perspective

☞ Heavy data-driven customization of UI

☞ Show this view if this client

☞ Rounded corners for that client

☞ Big views with dynamic subviews

☞ Composition?

Page 62: The State of Front-end At CrowdTwist
Page 63: The State of Front-end At CrowdTwist

We chose Marionette.js

Boilerplate views on top of Backbone to simplify common use cases, eg. ItemView, CollectionView, Layout.

Keep in mind it is late 2012, Backbone.js is the cool kid on the block

It will be at least a year until anyone knows what Angular is

Page 64: The State of Front-end At CrowdTwist

Marionette.js

class HomePage extends Marionette.Layout template: HomePageJadeTemplate

# Marionette.Layout container for subviews regions: dashboardRegion: '#dashboard-region' ...

_initDashboard: => @dashboardView = new DashboardView({...}); @listenTo this, 'show', => @dashboardRegion.show @dashboardView

Page 65: The State of Front-end At CrowdTwist

Backbone / Marionette

☞ Many moving parts

☞ Poor layout/view lifecycle control

☞ Spaghetti events around DOM presence

☞ False separation of concerns

☞ Simple routing of urls to function calls make managing state, nested views, and re-renders a very manual and tedious process.

Page 66: The State of Front-end At CrowdTwist

Custom build tool, a.k.a Crusher

☞ In-house build tool

☞ Developed to facilitate control of custom client implementations on the front-end (white-label)

☞ Each client has its own app.js and app.css files

☞ Built dynamically from a directory structure organized by client id

☞ Leverages Sass $variables to override defaults with custom configuration

Page 67: The State of Front-end At CrowdTwist

Custom build tool, a.k.a Crusher

config.scss

clients/├── 1└── stylesheets ├── config.scss

Page 68: The State of Front-end At CrowdTwist

Pros

☞ Front-end owns the view (kinda)

☞ Fits well into model of Cascading Style Sheets

☞ Sharing code with minimal duplication

Page 69: The State of Front-end At CrowdTwist

Cons

☞ Maintenance costs of custom build tool

☞ Very (very) slow build time

☞ False separation of concerns

☞ Database still requires knowledge of specific client configuration

☞ Back-end already provides view-driven data

Page 70: The State of Front-end At CrowdTwist

What if we move all client configuration to the server and use React to build components that manage their own data, styling, and templates?

Page 71: The State of Front-end At CrowdTwist

let HomePage = React.createClass({ mixins: [TextKeyMixin, StyleMixin],

statics: { fetchData(params) { return get({ page_data: { text: ['title.home'] }, model_data: { activities: ['id', 'title'] }, style_data: { attributes: ['borderRadius', 'fontSize', 'backgroundColor'] } }); } },

render() { return ( <div id="home-page" className={this.styleData}> <h1>{this.getTextKey('title.home')}</h1> <Activities activities={this.getModelData('activities')} /> </div> ); }});

Page 72: The State of Front-end At CrowdTwist

Testing components is simple too..

import SampleComponent from "src/common/sample_component.jsx";let {TestUtils} = React.addons;

describe('Sample Component', function() { it('should render with no props', function() { let sampleComponent = TestUtils.renderIntoDocument( <SampleComponent /> );

let heading = TestUtils.findRenderedDOMComponentWithTag( sampleComponent, 'h1');

expect(heading.getDOMNode().textContent).to.be.empty(); });});

Page 73: The State of Front-end At CrowdTwist

across various platforms and devices

$ npm test

Page 74: The State of Front-end At CrowdTwist

To conclude

☞ The problem of scaling large applications with magical frameworks is real

☞ If you think you separated your concerns, think again

☞ The web is changing, keeping up is nearly impossible

☞ Ownership is important, how would you define simplicity?

☞ JavaScript is slowly solving its own problems

☞ There is no silver bullet

Page 75: The State of Front-end At CrowdTwist

To conclude

☞ React's approach is to keep it small and contained

☞ It's just a library for building UI using JavaScript

Suddenly, it's no longer 2006.

Page 76: The State of Front-end At CrowdTwist

Thanks!

☞ @pheuter

☞ CrowdTwist