100
from github’s codebase Ruby Patterns

2012 Github Ruby Patterns

Embed Size (px)

Citation preview

Page 1: 2012 Github Ruby Patterns

from github’s codebase

Ruby Patterns

Page 2: 2012 Github Ruby Patterns

@holman

Page 3: 2012 Github Ruby Patterns
Page 4: 2012 Github Ruby Patterns

Like everyone, we have some really dumb ideas.

Page 5: 2012 Github Ruby Patterns

But we have somegood ideas too! Honest!

Page 6: 2012 Github Ruby Patterns

So let’s talk about a few.

Page 7: 2012 Github Ruby Patterns

bootstrappingmaking your app newbie-proof™

Page 8: 2012 Github Ruby Patterns

your company is going to have

TONS OF SUCCESS

which means you’ll have to hire

TONS OF PEOPLE

Page 9: 2012 Github Ruby Patterns

GitHub has 200+ repositories.and 60+ employees.and 20+ languages.

and about a billion egos.

Page 10: 2012 Github Ruby Patterns

It needs to be QUICK AND EASY to step into an unfamiliar app

Page 11: 2012 Github Ruby Patterns

script/bootstrap

Page 12: 2012 Github Ruby Patterns

Most of our Ruby projects have a file called in the directory.

bootstrap script/

Easily jump into a project and start contributing.

Page 13: 2012 Github Ruby Patterns

{script/bootstrap

static page compilation

language compilation

db seeding

dependency checks

bundler

db creation

db migration

Page 14: 2012 Github Ruby Patterns

{static page compilation

language compilation

db seeding

dependency checks

bundler

db creation

db migration

is mysql installed?(here’s how to install it)

is redis running?(here’s how to run it)

Page 15: 2012 Github Ruby Patterns

{static page compilation

language compilation

db seeding

dependency checks

bundler

db creation

db migration

bundle install \ --binstubs \ --local \ --path=vendor/gems \ --without=production

Page 16: 2012 Github Ruby Patterns

{static page compilation

language compilation

db seeding

dependency checks

bundler

db creation

db migrationrake db:create

Page 17: 2012 Github Ruby Patterns

{static page compilation

language compilation

db seeding

dependency checks

bundler

db creation

db migrationrake db:migrate

Page 18: 2012 Github Ruby Patterns

{static page compilation

language compilation

db seeding

dependency checks

bundler

db creation

db migrationscript/replicate-repo(see rtomayko/replicate)

Page 19: 2012 Github Ruby Patterns

{static page compilation

language compilation

db seeding

dependency checks

bundler

db creation

db migration404, 500

Page 20: 2012 Github Ruby Patterns

{static page compilation

language compilation

db seeding

dependency checks

bundler

db creation

db migrationpython, c, erlang...

Page 21: 2012 Github Ruby Patterns

{static page compilation

language compilation

db seeding

dependency checks

bundler

db creation

db migrationt

Page 22: 2012 Github Ruby Patterns

cache bootstrap results

Page 23: 2012 Github Ruby Patterns

md5 << File.read('Gemfile') checksum = md5.hexdigest installed = File.read('.bundle/checksum').strip

Check if we need to bundle install

Page 24: 2012 Github Ruby Patterns

should be fast, straightforward, and require no knowledge of the underlying language or app setup

script/bootstrap

Page 25: 2012 Github Ruby Patterns

You can also run during any process that loads the environment, like

script/bootstrap

script/server

Page 26: 2012 Github Ruby Patterns

You can do the same with your testing environment, too.

Page 27: 2012 Github Ruby Patterns

Our CI server only needs to ever run .script/cibuild

We add to most projects. Lets us keep test config in the repository.

script/cibuild

Page 28: 2012 Github Ruby Patterns

{script/cibuild

export RACK_ROOT=["..."] export RACK_ENV="test"

script/bootstrap

bin/rake

Page 29: 2012 Github Ruby Patterns

QUICK AND EASY Make sure your environment is

to set up.

Page 30: 2012 Github Ruby Patterns

conditional monkeypatchingjust like a good rubyist

Page 31: 2012 Github Ruby Patterns

- GitHub is a Rails 2.3(ish) app

PROBLEM:

- Upgrading to Rails 3.x sucks- Biggest concern is escaping-by-default- We wanted incremental rollout

Page 32: 2012 Github Ruby Patterns

JOSH PEEK Yo let’s just make itstaff-only like

everything else we do

Page 33: 2012 Github Ruby Patterns

def enable_xss_escaping(&block) if staff? ActiveSupport::SafeBuffer.enable_escaping(&block) else yield end end

Page 34: 2012 Github Ruby Patterns

def enable_xss_escaping(&block) if staff? ActiveSupport::SafeBuffer.enable_escaping(&block) else yield end end

Page 35: 2012 Github Ruby Patterns

def enable_xss_escaping(&block) if staff? ActiveSupport::SafeBuffer.enable_escaping(&block) else yield end end

(#enable_escaping is just a patch on SafeBuffer)

Page 36: 2012 Github Ruby Patterns

Use Ruby’s strengths to limit your liability during big code changes.

Page 37: 2012 Github Ruby Patterns

Also, use partial deployments to further limit your exposure.

Page 38: 2012 Github Ruby Patterns

api designgithub api v3

Page 39: 2012 Github Ruby Patterns

Sinatra is (naturally) great for an API.

get “/” do “an web app” end

translateseasily toGET / HTTP/1.1

Page 40: 2012 Github Ruby Patterns

GITHUB API V3

~30 Sinatra apps

located in app/api in our Rails app

mounted at api.github.com/(version in headers)

routed in rack with path regex

Page 41: 2012 Github Ruby Patterns

USE HELPERS, SIMPLIFY ROUTES # List a user's followers. get "/users/:user/followers" do control_access :public_read deliver find_user.followers.paginate(pagination) end

Page 42: 2012 Github Ruby Patterns

USE HELPERS, SIMPLIFY ROUTES # List a user's followers. get "/users/:user/followers" do control_access :public_read deliver find_user.followers.paginate(pagination) end

control_accessverifies user has permission for the request

pulls user data from currently-authenticated user

Page 43: 2012 Github Ruby Patterns

USE HELPERS, SIMPLIFY ROUTES # List a user's followers. get "/users/:user/followers" do control_access :public_read deliver find_user.followers.paginate(pagination) end

find_userauto-populated based on params[:user]

manages error handling for nonexistent users

Page 44: 2012 Github Ruby Patterns

USE HELPERS, SIMPLIFY ROUTES # List a user's followers. get "/users/:user/followers" do control_access :public_read deliver find_user.followers.paginate(pagination) end

paginationauto-populated based on per_page & page params

Page 45: 2012 Github Ruby Patterns

USE HELPERS, SIMPLIFY ROUTES # List a user's followers. get "/users/:user/followers" do control_access :public_read deliver find_user.followers.paginate(pagination) end

deliverhandles nil objects (404)

appends OAuth scope & rate limiting headers

encodes as JSON and sets Content-Type

handles JSONP callbacks

Page 46: 2012 Github Ruby Patterns

USE HELPERS, SIMPLIFY ROUTES # List a user's followers. get "/users/:user/followers" do control_access :public_read deliver find_user.followers.paginate(pagination) end

Page 47: 2012 Github Ruby Patterns

USE HELPERS, SIMPLIFY ROUTES

Extract all these little methods out

Your API becomes extremely

Your API becomes extremely

readable

testable

Page 48: 2012 Github Ruby Patterns

inner-app projectsfor services that aren’t yet services

Page 49: 2012 Github Ruby Patterns

Big apps suck, services are great.

Page 50: 2012 Github Ruby Patterns

Single responsibility principle and all that jazz.

But it’s harder to do!

LET ME JUST THROWTHIS IN THE MODEL!!!!!!111

Page 51: 2012 Github Ruby Patterns

Compromise: library-in-an-app

Page 52: 2012 Github Ruby Patterns

lib/#{project}test/#{project}

Page 53: 2012 Github Ruby Patterns

INNER-APP READMEGitHub displays READMEs for every directory we find

Clear area to add documentation

Page 54: 2012 Github Ruby Patterns

Easy access to your models

y

Page 55: 2012 Github Ruby Patterns

(Potentially) easier to extract

Page 56: 2012 Github Ruby Patterns

TEST HELPERS

Separate your project’s helper methods

Fixtures, too

Page 57: 2012 Github Ruby Patterns

As you grow, it’s harder to mentally grasp your entire app. Splitting into well-documented chunks can really help.

Page 58: 2012 Github Ruby Patterns

github hd™zooooooooooooooooooooooom

Page 59: 2012 Github Ruby Patterns

Okay this totally isn’t Ruby, but it’s cool.

Page 60: 2012 Github Ruby Patterns

⌘+ ⌘-ZOOM IN ZOOM OUT

Page 61: 2012 Github Ruby Patterns
Page 62: 2012 Github Ruby Patterns
Page 63: 2012 Github Ruby Patterns
Page 64: 2012 Github Ruby Patterns
Page 65: 2012 Github Ruby Patterns

<img src=”logo-4x.png” width=”70” height=”30” />

280px

120px

SCALE IN-BROWSER

Page 66: 2012 Github Ruby Patterns

This is great for iPhones and iPads, too.

Page 67: 2012 Github Ruby Patterns

documentationreally doesn’t have to suck. honest.

Page 68: 2012 Github Ruby Patterns

As you grow larger, it’s harder to understand your entire app.

(I’m totally a broken record)

Page 69: 2012 Github Ruby Patterns

DOCUMENTATION!TO THE RESCUE!

Page 70: 2012 Github Ruby Patterns

We TomDoc theshit out of everything

Page 71: 2012 Github Ruby Patterns

Pretty much every method has 3-6 lines of documentation

Page 72: 2012 Github Ruby Patterns

Sure, you can laugh.

But working on highly-documented code is AMAZING.

Page 73: 2012 Github Ruby Patterns

...particularly in Ruby, because following this sucks:

def mailing_address

full_name + “\n”

street_address_1 + “\n” +

street_address_2 + “\n” +

“#{city}, #{state} #{zip}”

end def street_address_1 house_with_street.formatted end

def house_with_street

StreetAddress.new(data)

end

Page 74: 2012 Github Ruby Patterns

Debugging a STRONGLY DYNAMICMETAPROGRAMMING-HAPPYFLEXIBLE LANGUAGE is hard.

Page 75: 2012 Github Ruby Patterns

SPEC:

TOMDOC

GOAL: internal API documentation for your developers

tomdoc.org

(generated docs not a priority)

Page 76: 2012 Github Ruby Patterns

# Cures cancer. Beware of race conditions or else you’ll turn into a # newt or something. # # options - A Hash consisting of: # age - The Integer age of the patient. # money - The Float amount of money in the patient’s account # valuable - A Boolean value of whether the patient is valuable # to society or not. # rollback - A Boolean of whether we should troll the patient and give # cancer again once they’re cured. # # This returns a Patient record. Raises UncurableError if the patient can’t # be cured. def cure_cancer (options, rollback) # boring implementation details, you can figure it out yourself end

Page 77: 2012 Github Ruby Patterns

# Cures cancer. Beware of race conditions or else you’ll turn into a # newt or something. # # options - A Hash consisting of: # age - The Integer age of the patient. # money - The Float amount of money in the patient’s account # valuable - A Boolean value of whether the patient is valuable # to society or not. # rollback - A Boolean of whether we should troll the patient and give # cancer again once they’re cured. # # This returns a Patient record. Raises UncurableError if the patient can’t # be cured. def cure_cancer (options, rollback) # boring implementation details, you can figure it out yourself end

Page 78: 2012 Github Ruby Patterns

# Cures cancer. Beware of race conditions or else you’ll turn into a # newt or something. # # options - A Hash consisting of: # age - The Integer age of the patient. # money - The Float amount of money in the patient’s account # valuable - A Boolean value of whether the patient is valuable # to society or not. # rollback - A Boolean of whether we should troll the patient and give # cancer again once they’re cured. # # This returns a Patient record. Raises UncurableError if the patient can’t # be cured. def cure_cancer (options, rollback) # boring implementation details, you can figure it out yourself end

Page 79: 2012 Github Ruby Patterns

# Cures cancer. Beware of race conditions or else you’ll turn into a # newt or something. # # options - A Hash consisting of: # age - The Integer age of the patient. # money - The Float amount of money in the patient’s account # valuable - A Boolean value of whether the patient is valuable # to society or not. # rollback - A Boolean of whether we should troll the patient and give # cancer again once they’re cured. # # This returns a Patient record. Raises UncurableError if the patient can’t # be cured. def cure_cancer (options, rollback) # boring implementation details, you can figure it out yourself end

Page 80: 2012 Github Ruby Patterns

# Cures cancer. Beware of race conditions or else you’ll turn into a # newt or something. # # options - A Hash consisting of: # age - The Integer age of the patient. # money - The Float amount of money in the patient’s account. # valuable - A Boolean value of whether the patient is valuable # to society or not. # rollback - A Boolean of whether we should troll the patient and give # cancer again once they’re cured. # # This returns a Patient record. Raises UncurableError if the patient can’t # be cured. def cure_cancer (options, rollback) # boring implementation details, you can figure it out yourself end

Page 81: 2012 Github Ruby Patterns

# Cures cancer. Beware of race conditions or else you’ll turn into a # newt or something. # # options - A Hash consisting of: # age - The Integer age of the patient. # money - The Float amount of money in the patient’s account. # valuable - A Boolean value of whether the patient is valuable # to society or not. # rollback - A Boolean of whether we should troll the patient and give # cancer again once they’re cured. # # This returns a Patient record. Raises UncurableError if the patient can’t # be cured. def cure_cancer (options, rollback) # boring implementation details, you can figure it out yourself end

Page 82: 2012 Github Ruby Patterns

# Cures cancer. Beware of race conditions or else you’ll turn into a # newt or something. # # options - A Hash consisting of: # age - The Integer age of the patient. # money - The Float amount of money in the patient’s account # valuable - A Boolean value of whether the patient is valuable # to society or not. # rollback - A Boolean of whether we should troll the patient and give # cancer again once they’re cured. # # This returns a Patient record. Raises UncurableError if the patient can’t # be cured. def cure_cancer (options, rollback) # boring implementation details, you can figure it out yourself end

Page 83: 2012 Github Ruby Patterns

DON’T WORRY, IT’S NOT VERBOSE:Most of your TomDoc will only be two lines:

# EXPLANATION## RETURNS

Complicated methods (like the previous example) will require more detailed TomDoc.

Page 84: 2012 Github Ruby Patterns

IT’S FLEXIBLE:Document as you see fit; it’s a loose spec.

Page 85: 2012 Github Ruby Patterns

IT’S ALWAYS UP-TO-DATE:If you update the code, update the fucking docs.

Page 86: 2012 Github Ruby Patterns

IT’S OUTRAGEOUSLY HELPFULHumans will always read English easier than code.

Page 87: 2012 Github Ruby Patterns

IT HELPS WITH TESTSYou’re detailing input + output already.

Page 88: 2012 Github Ruby Patterns

TOMDOC IS NOT REQUIREDIf you don’t dig TomDoc, choose something to document your code.

Page 89: 2012 Github Ruby Patterns

LONG-FORM IS GREAT TOOInner-app READMEs, wiki entries, internal posts.

Page 90: 2012 Github Ruby Patterns

DOCUMENT EVERYTHING

DOCUMENT EVERYTHING

— no, really —

Page 91: 2012 Github Ruby Patterns

beware noveltycustom vs. stock

Page 92: 2012 Github Ruby Patterns

GitHub has opinionated employees.

Page 93: 2012 Github Ruby Patterns

Custom hacks become difficult to deal with as you grow larger.

Page 94: 2012 Github Ruby Patterns

jump to Rails 3.x

speed of new hires

possible reliabilityIMPACTS OUR

Page 95: 2012 Github Ruby Patterns

“Doing It Yourself” isn’t wrong...but be wary of the implications.

Page 96: 2012 Github Ruby Patterns

maintainability,A lot of this is focused on

especially as you grow.

Page 97: 2012 Github Ruby Patterns

Technical scaling is sometimes easier than organizational scaling.

Page 98: 2012 Github Ruby Patterns

Keep that in mind as you build your Next Big Thing.

Page 99: 2012 Github Ruby Patterns

Thanks.

Page 100: 2012 Github Ruby Patterns

Zach Holmanzachholman.com/talks@holman