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
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
Austin Bales
March 15, 2012
Programming
730
7
Share
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
作って学ぶ、 JSX (TSX) ランタイムの基本
syumai
7
1.5k
TSKaigi 2026 TypeScriptバックエンドのオブザーバビリティ戦略 — Datadog × NestJSの実践
taiseiyamamotoan
2
270
柔軟なPDFレイアウトエディタを支える型システム設計 — Discriminated UnionとConditional Typeの実践
minako__ph
4
1.4k
Oxcを導入して開発体験が向上した話
yug1224
4
280
ローカルLLMを使ってB2Bサービスを作っていての学び
yaotti
0
110
Copilot CLI の継戦能力を高める コンテキスト管理
nozomutu
1
1.2k
AIとRubyの静的型付け
ukin0k0
0
530
AIチームを指揮するOSS「TAKT」活用術 / How to Use “TAKT,” an OSS Tool for Orchestrating AI Teams
nrslib
6
810
ADKを使って簡単にAIエージェントを作ってみよう
k1mu21
0
200
エージェンティックRAGにAWSで入門しよう!
har1101
5
360
さぁV100、メモリをお食べ・・・
nilpe
0
130
[2026年度第1回ORセミナー] 計画最適化ベンチャーと競技プログラミング人材
terryu16
0
240
Featured
See All Featured
Groundhog Day: Seeking Process in Gaming for Health
codingconduct
0
200
Self-Hosted WebAssembly Runtime for Runtime-Neutral Checkpoint/Restore in Edge–Cloud Continuum
chikuwait
0
560
New Earth Scene 8
popppiees
3
2.3k
Ten Tips & Tricks for a 🌱 transition
stuffmc
0
130
How to optimise 3,500 product descriptions for ecommerce in one day using ChatGPT
katarinadahlin
PRO
1
3.6k
Designing Dashboards & Data Visualisations in Web Apps
destraynor
231
55k
Heart Work Chapter 1 - Part 1
lfama
PRO
7
36k
First, design no harm
axbom
PRO
2
1.2k
エンジニアに許された特別な時間の終わり
watany
107
250k
Performance Is Good for Brains [We Love Speed 2024]
tammyeverts
12
1.7k
Helping Users Find Their Own Way: Creating Modern Search Experiences
danielanewman
31
3.2k
Highjacked: Video Game Concept Design
rkendrick25
PRO
1
380
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