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
7
710
Building Do on Heroku
Gopal Patel (@nixme) presents "Building Do on Heroku"
Austin Bales
March 15, 2012
Tweet
Share
More Decks by Austin Bales
See All by Austin Bales
Design Minded Development
austin
8
520
UI Encapsulation with Handlebars and Sass
austin
5
1.1k
Building Awesome Products at Do.com
austin
2
200
Other Decks in Programming
See All in Programming
CSC307 Lecture 07
javiergs
PRO
1
560
AIとペアプロして処理時間を97%削減した話 #pyconshizu
kashewnuts
1
110
生成AIを使ったコードレビューで定性的に品質カバー
chiilog
1
300
ふん…おもしれぇ Parser。RubyKaigi 行ってやるぜ
aki_pin0
0
110
OSSとなったswift-buildで Xcodeのビルドを差し替えられるため 自分でXcodeを直せる時代になっている ダイアモンド問題編
yimajo
3
650
AIによる高速開発をどう制御するか? ガードレール設置で開発速度と品質を両立させたチームの事例
tonkotsuboy_com
7
2.5k
Go1.26 go fixをプロダクトに適用して困ったこと
kurakura0916
0
220
Railsの気持ちを考えながらコントローラとビューを整頓する/tidying-rails-controllers-and-views-as-rails-think
moro
4
310
ぼくの開発環境2026
yuzneri
1
280
AIコーディングの理想と現実 2026 | AI Coding: Expectations vs. Reality 2026
tomohisa
0
410
AIエージェントのキホンから学ぶ「エージェンティックコーディング」実践入門
masahiro_nishimi
7
1.1k
並行開発のためのコードレビュー
miyukiw
2
1.9k
Featured
See All Featured
WENDY [Excerpt]
tessaabrams
9
36k
Marketing to machines
jonoalderson
1
4.9k
We Analyzed 250 Million AI Search Results: Here's What I Found
joshbly
1
810
Reality Check: Gamification 10 Years Later
codingconduct
0
2k
End of SEO as We Know It (SMX Advanced Version)
ipullrank
3
4k
Connecting the Dots Between Site Speed, User Experience & Your Business [WebExpo 2025]
tammyeverts
11
840
Impact Scores and Hybrid Strategies: The future of link building
tamaranovitovic
0
210
The Curious Case for Waylosing
cassininazir
0
250
The browser strikes back
jonoalderson
0
720
Balancing Empowerment & Direction
lara
5
910
Making the Leap to Tech Lead
cromwellryan
135
9.7k
Embracing the Ebb and Flow
colly
88
5k
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