Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Building Do on Heroku
Search
Austin Bales
March 15, 2012
Programming
730
7
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Building Do on Heroku
Gopal Patel (@nixme) presents "Building Do on Heroku"
Austin Bales
March 15, 2012
More Decks by Austin Bales
See All by Austin Bales
Design Minded Development
austin
8
530
UI Encapsulation with Handlebars and Sass
austin
5
1.1k
Building Awesome Products at Do.com
austin
2
210
Other Decks in Programming
See All in Programming
ふつうのFeature Flag実践入門
irof
8
4.2k
キャリア迷子上等 ─ "ない道"は自分で作ればいい
16bitidol
3
2.3k
脅威をエンジニアリングの糧にして――現場編 / Turning Threats into Engineering Fuel — Field Edition
nrslib
0
300
TSKaigi Night Talks 2026_TypeScriptでサプライチェーンの整合性を型に閉じ込める
geekplus_tech
0
410
肥大化するレガシーコードに立ち向かうためのインターフェース分離と依存の逆転 / JJUG CCC 2026 Spring
hirokunimaeta
0
620
エージェンティックRAGにAWSで入門しよう!
har1101
9
1.7k
AI駆動開発を妨げる技術的負債の解消アプローチ / ai-refactoring-approach
minodriven
13
6.7k
はてなアカウント基盤 State of the Union
cockscomb
0
700
ECSアプリログをFireLensでコスト削減しようとしたけど諦めた話 in Fargate×Node.js
akihisaikeda
2
4.2k
「なぜそう決めたのか」を残し続ける仕組み ― Notion AI カスタムエージェント × Slack連携による設計判断の自動記録 - NIKKEI Tech Talk #47
niftycorp
PRO
0
230
JavaDoc 再入門
nagise
1
420
Skillsは効率化、Agentsは"自分の拡張"——Builder時代のエージェント編成(CC Night 2026)
wemra
1
160
Featured
See All Featured
How to audit for AI Accessibility on your Front & Back End
davetheseo
0
450
The agentic SEO stack - context over prompts
schlessera
0
820
16th Malabo Montpellier Forum Presentation
akademiya2063
PRO
0
150
Claude Code どこまでも/ Claude Code Everywhere
nwiizo
65
56k
New Earth Scene 8
popppiees
3
2.4k
Done Done
chrislema
186
16k
Building Experiences: Design Systems, User Experience, and Full Site Editing
marktimemedia
0
540
Templates, Plugins, & Blocks: Oh My! Creating the theme that thinks of everything
marktimemedia
31
2.8k
Exploring anti-patterns in Rails
aemeredith
3
430
Noah Learner - AI + Me: how we built a GSC Bulk Export data pipeline
techseoconnect
PRO
0
200
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
659
62k
Imperfection Machines: The Place of Print at Facebook
scottboms
270
14k
Transcript
Building Do on Heroku
Gopal Patel, Director of Engineering
[email protected]
• @nixme github.com/nixme
Social Productivity Everywhere “ ”
None
None
SINGLE PAGE
SINGLE PAGE MOBILE FIRST
SINGLE PAGE MOBILE FIRST MANY CLIENTS
↑ ↓ RUBY Heroku Postgres, New Relic, Logplex… ADD-ONS iOS
Android Desktop Widgets CoffeeScript + Backbone.js WEB APPS NATIVE CLIENTS CUSTOM UI FRAMEWORK
API First BEST PRACTICE
API First BEST PRACTICE
Build Platform API First BEST PRACTICE
Build Platform Separation of concerns. Modularity. API First BEST PRACTICE
Build Platform Separation of concerns. Modularity. Constraints lead to guided
design API First BEST PRACTICE
Build Platform Separation of concerns. Modularity. Constraints lead to guided
design Speak HTTP API First BEST PRACTICE
None
def hello puts 'Hello World!' end hello # => Hello
World
class Note include SoftDelete def initialize(title = 'Untitled') @id =
Sequence.next_id @title = title @body = 'Enter your note...' end def print puts "#{@id}: #{@title} - #{@body}" end end
None
VIEW MODEL CONTROLLER BROWSER ↑ ↓ ↑ ↓ ROUTER ↑
↓ ↑ ↓
JSON MODEL CONTROLLER BROWSER ↑ ↓ ↑ ↓ ROUTER ↑
↓ ↑ ↓
Notes Controller RUBY DEMO
The New Age of JS BIG MOVES
None
$('.name').bind('change', function(event) { $.ajax({ type: "PUT", url: "/names/" + $(event.target).attr('context_id'),
data: { value: $(event.target).attr('data-value') }, success: function(response) { statusIcon = $('.name + .status'); statusIcon.show(); setTimeout(function() { statusIcon.hide(); }, 5000); }, error: function(response) { console.log(response); $('.name').attr('disabled', true); } }); }); $('.name_delete').click(function(event) { event.preventDefault(); id = $(event.target).attr('context_id'); if (confirm('Are you sure?')) { $.ajax({ type: "DELETE", url: "/names/" + id, success: function(response) { $(".row-" + id).remove(); $(".row-" + (id + 1)).focus(); },
None
Backbone.Events binds and triggers
Backbone.Events binds and triggers Backbone.Router front-end routing, pushState
Backbone.Events binds and triggers Backbone.Router front-end routing, pushState Backbone.View controllers
for your UI
Backbone.Events binds and triggers Backbone.Router front-end routing, pushState Backbone.View controllers
for your UI Backbone.Model domain objects
Backbone.Events binds and triggers Backbone.Router front-end routing, pushState Backbone.View controllers
for your UI Backbone.Model domain objects Backbone.Collection model sets
var object = {}; _.extend(object, Backbone.Events); object.on("alert", function(msg) { alert("Triggered
" + msg); }); object.trigger("alert", "an event"); Backbone.Events
var Note = Backbone.Model.extend({ initialize: function() {…}, coordinates: function() {…},
}); var note = new Note({ title: "Today's Dinner", body: … }); note.on("error", function(model, error) {…}); note.save(); Backbone.Model
var DocumentRow = Backbone.View.extend({ tagName: "li", className: "document-row", events: {
"click .icon": "open", "click .button.edit": "openEditDialog", "click .button.delete": "destroy" }, render: function() {…} }); Backbone.View
Do’s Router COFFEESCRIPT DEMO
Ember Knockout Spine
None
None
None
number = 42 flag = true greeting = 'world' var
flag, greeting, number; number = 42; flag = true; greeting = 'world';
square = (x) -> x * x cube = (x)
-> x * square x @customer = new Customer $('.cart').on 'click', (event) => @customer.buy event.target var cube, square, _this = this; square = function(x) { return x * x; }; cube = function(x) { return x * square(x); }; this.customer = new Customer; $('.cart').on('click', function(event) { return _this.customer.buy(event.target); });
kids = brother: name: "Max" age: 11 sister: name: "Ida"
age: 9 var kids; kids = { brother: { name: "Max", age: 11 }, sister: { name: "Ida", age: 9 } };
for own view, element of views $(element).remove() view.removed = true
var element, view, __hasProp = Object.prototype.hasOwnProperty; for (view in views) { if (!__hasProp.call(views, view)) continue; element = views[view]; $(element).remove(); view.removed = true; }
coffeescript.org
coffeescript.org sass-lang.com
None
None
BUNDLE (Sprockets) ↓ MINIFY (UglifyJS) COMPRESSION (GZIP) git push ↓
↓
BUNDLE (Sprockets) ↓ MINIFY (UglifyJS) COMPRESSION (GZIP) git push ↓
↓ ↑ ↓ → CLOUDFRONT ↑ ↓
web: unicorn -p $PORT -c ./config/unicorn.rb worker: rake resque:work QUEUE=notifications,...
workerprovisioning: rake resque:work QUEUE=provisioning,... scheduler: rake resque:scheduler VERBOSE=true Procfile
BACKUPS • FORKING • FOLLOWING
None
CONTINUOUS INTEGRATION AUTOMATED TESTING
CONTINUOUS DEPLOYMENT
CONTINUOUS DEPLOYMENT FEATURE FLAGS if Do.Flags.check ‘recurrence’ # Recurring Tasks
Code
Design HOW WE DO
Design HOW WE DO User Experience is #1
Design HOW WE DO User Experience is #1 Engineering +
Design = Like
Design HOW WE DO User Experience is #1 Engineering +
Design = Like Design is a process / conversation
Design HOW WE DO User Experience is #1 Engineering +
Design = Like Design is a process / conversation We work together. Everyone commits.
WHAT WAS THIS TALK ABOUT? API First Expressive Languages Modern
Browsers Continuous Deployment
WHAT WAS THIS TALK ABOUT? API First Expressive Languages Modern
Browsers Continuous Deployment Loosely-coupled Code Tightly-coupled Team
do.com
THE PANEL Manav Monga Product Guy. @xmanav Bing Yang Product
Guy. @bsbox David Yung Developer. @azethoth Austin Bales Designer. @arbales http://pris.ma/9M