SlideShare a Scribd company logo
The State of
Front-end
At CrowdTwist
Different apps with lots of code...
☞ FanCenter: 22k (SLOC)
☞ Control Center: 12k
☞ Widgets: 9k
...written in various libraries and frameworks...
☞ FanCenter: Backbone.js, Marionette.js, Geppetto,
RequireJS
☞ Control Center & Widgets: AngularJS
...using different tools.
☞ FanCenter: Crusher (in-house build tool), Mocha
☞ Control Center & Widgets: npm, Bower, Grunt, Express,
Karma
The State of Front-end At CrowdTwist
So what?
Growing pains
As 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.
❝Just because you're using
Backbone and Angular doesn't
mean your code has to suck.❞
—a reasonable developer
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.
Unless, of course, every developer is a
rockstar and their thought process is
identical to mine!
That's how it works, right?
Right!?
The State of Front-end At CrowdTwist
How did we get here?
☞ Magical frameworks
☞ False separation of concerns
☞ Nascent tools and rapidly evolving ecosystem
Magical
Frameworks
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.
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?
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)?
The State of Front-end At CrowdTwist
Separation
of
Concerns
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?
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
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.
Here, here, Angular...
You're not so bad!
You're just growing up so fast...
Mommy can't keep up with your needs!
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!!
Rapidly
Evolving
Ecosystem
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.
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!
...they say
But I'm feeling more optimistic it's not going to be for long.
Let's talk about JavaScript
mmm vanilla
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!
The ecosystem is maturing
The tooling is maturing
import {get} from "common/request.js";
class MyComponent {...};
export default MyComponent;
I know what you're thinking
❝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
Let's talk about React
Skeptical? I was too...
Library? User Interfaces?
What year is this, 2006?
There has been a lot of wizardry recently
React may have just found the sweet spot
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.
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;
❝Dude, is that HTML in your
business logic? WAT?? You know
you're not supposed to...❞
—a reasonable developer
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.
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!
With React, you at least get all the dependencies isolated to a
single component in a single file.
That is ownership.
If you still oppose the idea of JSX, you can use React without JSX:
React.createElement(Question, {data: question}, "innerText");
Composition
Your component can render other components!
<QuestionList>
<Question />
<Question />
</QuestionList>
(without the complexity of isolate scopes and transclusion)
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.
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.
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
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.
Used in production
There's more...
☞ Check out http://facebook.github.io/react/
☞ Play around with it
☞ Form your own opinion
How could we
use React
at CrowdTwist?
Quick look at FanCenter
White-label web app for user engagement with brands
Users complete various activities to earn rewards
The State of Front-end At CrowdTwist
The State of Front-end At CrowdTwist
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?
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
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
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.
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
Custom build tool, a.k.a Crusher
config.scss
clients/
├── 1
└── stylesheets
├── config.scss
Pros
☞ Front-end owns the view (kinda)
☞ Fits well into model of Cascading Style Sheets
☞ Sharing code with minimal duplication
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
What if we move all client configuration to the server and use
React to build components that manage their own data, styling,
and templates?
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>
);
}
});
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();
});
});
across various platforms and devices
$ npm test
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
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.
Thanks!
☞ @pheuter
☞ CrowdTwist

More Related Content

The State of Front-end At CrowdTwist

  • 2. Different apps with lots of code... ☞ FanCenter: 22k (SLOC) ☞ Control Center: 12k ☞ Widgets: 9k
  • 3. ...written in various libraries and frameworks... ☞ FanCenter: Backbone.js, Marionette.js, Geppetto, RequireJS ☞ Control Center & Widgets: AngularJS
  • 4. ...using different tools. ☞ FanCenter: Crusher (in-house build tool), Mocha ☞ Control Center & Widgets: npm, Bower, Grunt, Express, Karma
  • 7. Growing pains As 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.
  • 8. ❝Just because you're using Backbone and Angular doesn't mean your code has to suck.❞ —a reasonable developer
  • 9. 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.
  • 10. Unless, of course, every developer is a rockstar and their thought process is identical to mine! That's how it works, right? Right!?
  • 12. How did we get here? ☞ Magical frameworks ☞ False separation of concerns ☞ Nascent tools and rapidly evolving ecosystem
  • 14. 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.
  • 15. 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?
  • 17. 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)?
  • 20. 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?
  • 21. 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
  • 22. 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.
  • 24. You're just growing up so fast... Mommy can't keep up with your needs!
  • 25. 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!!
  • 27. 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.
  • 28. 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!
  • 30. But I'm feeling more optimistic it's not going to be for long.
  • 31. Let's talk about JavaScript mmm vanilla
  • 32. 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!
  • 33. The ecosystem is maturing
  • 34. The tooling is maturing import {get} from "common/request.js"; class MyComponent {...}; export default MyComponent;
  • 35. I know what you're thinking
  • 36. ❝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
  • 37. Let's talk about React Skeptical? I was too...
  • 38. Library? User Interfaces? What year is this, 2006?
  • 39. There has been a lot of wizardry recently
  • 40. React may have just found the sweet spot
  • 41. 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.
  • 42. 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;
  • 43. ❝Dude, is that HTML in your business logic? WAT?? You know you're not supposed to...❞ —a reasonable developer
  • 45. 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.
  • 46. 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!
  • 47. With React, you at least get all the dependencies isolated to a single component in a single file. That is ownership.
  • 48. If you still oppose the idea of JSX, you can use React without JSX: React.createElement(Question, {data: question}, "innerText");
  • 49. Composition Your component can render other components! <QuestionList> <Question /> <Question /> </QuestionList> (without the complexity of isolate scopes and transclusion)
  • 50. 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.
  • 51. 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.
  • 52. 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
  • 53. 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.
  • 55. There's more... ☞ Check out http://facebook.github.io/react/ ☞ Play around with it ☞ Form your own opinion
  • 56. How could we use React at CrowdTwist?
  • 57. Quick look at FanCenter White-label web app for user engagement with brands Users complete various activities to earn rewards
  • 61. 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?
  • 63. 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
  • 64. 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
  • 65. 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.
  • 66. 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
  • 67. Custom build tool, a.k.a Crusher config.scss clients/ ├── 1 └── stylesheets ├── config.scss
  • 68. Pros ☞ Front-end owns the view (kinda) ☞ Fits well into model of Cascading Style Sheets ☞ Sharing code with minimal duplication
  • 69. 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
  • 70. What if we move all client configuration to the server and use React to build components that manage their own data, styling, and templates?
  • 71. 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> ); } });
  • 72. 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(); }); });
  • 73. across various platforms and devices $ npm test
  • 74. 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
  • 75. 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.