A guide for programming in style.
Set up your laptop with this script and these dotfiles.
Get the code.
git clone git@github.com:organization/project.git
Set up the project's dependencies.
cd project
bundle --binstubs
rake db:create
rake db:schema:load
rake db:seed
Add Heroku remotes for staging and production environments.
git remote add staging git@heroku.com:<app>-staging.git
git remote add production git@heroku.com:<app>-production.git
Use Heroku config to get ENV
variables.
heroku config:pull --app <app>-staging
Delete lines in .env
that don't apply.
BRAINTREE_MERCHANT_ID
BRAINTREE_PRIVATE_KEY
BRAINTREE_PUBLIC_KEY
S3_KEY
S3_SECRET
- Add stories during weekly retrospectives as a team to Trajectory.
- Make the client prioritize stories.
- Estimate stories with your fellow designers and developers.
- Write stories in the form
As a [user role], I want to [goal], so that [reason].
Start a user story.
Create a local feature branch based off master.
git checkout master
git pull
git checkout -b feature-xyz
Use foreman to run the app locally.
foreman start
It will pick up your .env
file and run declared processes from Procfile
like Heroku's Cedar stack.
Run the whole test suite or individual test files or individual tests.
rake
cucumber features/visitor/signs_in.feature
rspec spec/models/user_spec.rb
cucumber features/visitor/signs_in.feature:50
rspec spec/models/user_spec.rb:8
When feature is complete and tests pass, commit the changes.
rake
git add -A
git status
git commit -v
Write a good commit message.
Present-tense summary under 50 characters
* More information about commit (under 72 characters)
* More information about commit (under 72 characters)
Share your branch.
git push origin [branch]
Submit a Github pull request.
Ask for a code review in Campfire.
A team member other than the author should review the code.
Read the pull request on Github. Comment directly on lines of code.
For larger features, check out the branch.
git checkout <branch>
rake db:migrate
rake
git diff origin/master..HEAD
Make small changes right in the branch, commit, and push.
View the feature in the browser. Click around.
Ask questions of your teammate in a comment on the pull request or in Campfire.
Comment on the pull request Ready to squash and merge.
Rebase frequently to incorporate upstream changes.
git checkout master
git pull
git checkout [branch]
git rebase master
<resolve conflicts>
Interactive rebase (squash) your commits (if necessary).
git rebase -i master
Make sure tests pass. Merge your branch back to master and push your changes.
rake
git checkout master
git diff --stat master [branch]
git merge [branch] --ff-only
git push origin master
Delete your remote feature branch.
git push origin :[branch]
Delete your local feature branch.
git branch -d [branch]
Close pull request and comment Merged.
Merge master into the staging branch.
git checkout staging
git reset --hard staging/master
git log staging..master (view list of new commits)
git diff --stat origin/master (view changed files)
git merge master
Deploy to Heroku.
git push staging
Run migrations (if necessary).
heroku rake db:migrate --app <app>
Restart the dynos if migrations were run.
heroku restart --app <app>
Introspect to make sure everything's ok.
watch heroku ps --app <app>
Smoke test in browser.
Write acceptance criteria on the story and "deliver" the story.
A team member other than the author should review the feature on staging based on acceptance criteria.
Deploy to production.
git checkout production
git reset --hard production/master
git log production..staging (view list of new commits)
git diff --stat staging/master (view changed files)
git merge staging
git push production
heroku rake db:migrate --app <app>
heroku restart --app <app>
watch heroku ps --app <app>
- Delete trailing whitespace.
- Don't include spaces after
(
,[
or before]
,)
. - Don't vertically align tokens on consecutive lines.
- Include spaces around infix method invocations like
+
and-
. - Indent continued lines two spaces.
- Indent private methods equal to public methods.
- Limit lines to a maximum of 80 characters.
- Order methods and attributes alphabetically where possible.
- Use 2 space indentation (no tabs) unless otherwise noted.
- Use an empty line between methods, blocks and conditionals.
- Use spaces around operators, after commas, colons and semicolons, around
{
and before}
. - Use Unix-style line endings (
\n
).
- Avoid abbreviations.
- Avoid Hungarian notiation (
szName
). - Avoid types in names (
user_array
). - Name background jobs with a
Job
suffix. - Name the enumeration parameter the singular of the collection.
- Name variables, methods, and classes with intention-revealing names..
- Treat acronyms as words in names (
XmlHttpRequest
notXMLHTTPRequest
), even if the acronym is the entire name (class Html
notclass HTML
).
- Aggressively DRY code during development.
- Avoid comments.
- Avoid global variables.
- Avoid long parameter lists.
- Be consistent.
- Consider extracting
private
methods to their own object. - Don't duplicate the functionality of a built-in library.
- Don't program defensively.
- Don't swallow exceptions or "fail silently."
- Don't write code that guesses at future functionality.
- Exceptions should be exceptional.
- Keep the code simple.
- Limit the number of collaborators of an object.
- Prefer composition over inheritance.
- Prefer small methods. One line is best.
- Prefer small objects with a single, well-defined responsibility.
- Tell, don't ask.
- Define functions that operate on
window
or DOM in scope ofwindow
. - Initialize arrays using
[]
. - Initialize empty objects and hashes using
{}
. - Use
CamelCase
for prototypes,mixedCase
for variables and functions,SCREAMING_SNAKE_CASE
for constants,_single_leading_underscore
for private variables and functions. - Use
data-
attributes to bind event handlers. - Use the module pattern to control method visibility.
- Avoid
%q
,%Q
,%x
,%s
, and%W
. - Avoid conditional modifiers (lines that end with conditionals).
- Avoid hashes as optional parameters. Does the method do too much?
- Avoid including code and gems in source control that are specific to your
development machine or process. Examples:
.rvmrc
, file watchers, debuggers. - Avoid meta-programming.
- Avoid monkey-patching core classes.
- Avoid
return
unless required. - Avoid superfluous parentheses when calling methods, but keep them when you
assign the return value:
x = Math.sin(y); array.delete e
- Avoid ternary operators (
boolean ? true : false
). Use multi-lineif
instead to emphasize code branches. - Define the version of Ruby the project uses in the Gemfile.
- Don't use
unless
. - Prefer classes to modules when designing functionality that is shared by multiple models.
- Prefer
detect
overfind
andselect
overfind_all
to avoid confusion with ActiveRecord and keepselect
/reject
symmetry. - Prefer
map
overcollect
andreduce
overinject
due to symmetry and familarity with mapping and reducing in other technologies. - Use
_
for unused block parameters:hash.map { |_, v| v + 1 }
- Use
%{}
for single-line strings needing interpolation and double-quotes. - Use
%w()
over['', '']
for an array of words. - Use
&&
and||
for boolean expressions. - Use
||=
freely. - Use
{...}
overdo..end
for single-line blocks. - Use
!
suffix for dangerous methods (modifiesself
). - Use
?
suffix for predicate methods (return a boolean). - Use
CamelCase
for classes and modules,snake_case
for variables and methods,SCREAMING_SNAKE_CASE
for constants. - Use
def
with parentheses when there are arguments. - Use
do..end
over{...}
for multi-line blocks. - Use
each
, notfor
, for iteration. - Use heredocs for multi-line strings.
- Use
/(?:first|second)/
over/(first|second)/
when you don't need the captured group. - Use
private
overprotected
to indicate scope. - Use
def self.method
overdef Class.method
orclass << self
. - Use
Set
overArray
for arrays with unique elements. The lookup is faster. - Use single-quotes for strings unless interpolating.
- Use
unless boolean?
instead ofif !boolean?
. - Use Factory Girl to set up test data.
- Avoid bypassing validations with methods such as
save(:validate => false)
,update_attribute
, andtoggle
. - Avoid the
:except
option in routes. - Avoid
member
andcollection
routes. - Avoid Single Table Inheritance.
- Don't change a migration after it has been committed unless it cannot be solved with another migration.
- Don't invoke a model's class directly from a view.
- Don't use SQL or SQL fragments (
where('inviter_id is not null')
) outside of models. - Keep the
db/schema.rb
under version control. - Limit the number of instance variables shared between controller and view.
- Name initializers for their gem name. Example:
paperclip.rb
- Order controller contents: filters, public methods, private methods.
- Order model contents: constants, attributes, associations, nested attributes, named scopes, validations, callbacks, public methods, private methods.
- Prefer presenters (Ruby objects responsible for presentation) over view helpers.
- Put all copy text in models, views, controllers, and mailers in
config/locales
. - Serve assets from S3 using asset_sync.
- Set
config.assets.initialize_on_precompile = false
inconfig/application.rb
. - Set default values in the database.
- Use
_path
over_url
for named routes everywhere except mailer views. - Use
def self.method
over thenamed_scope :method
DSL. - Use
I18n.t 'dot.separated.key'
overI18n.t :key, :scope => [:dot, :separated]
. - Use Haml for view templates.
- Use
has_and_belongs_to_many
if all you need is a join table. Start simple. - Use namespaced locale lookup in views by prefixing a period:
t '.title'
. - Use nested routes to express
belongs_to
relationships between resources. - Use the default
render 'partial'
syntax overrender partial: 'partial'
. - Use the
:only
option to explicitly state exposed routes.
- Avoid multicolumn indexes in Postgres. It combines multiple indexes efficiently.
- Create indexes concurrently to avoid table locks and reduced performance during deploys.
- Consider a partial index for queries on booleans.
- Constrain most columns as
NOT NULL
. - Create a read-only Heroku Follower for your production database. If a Heroku database outage occurs, Heroku can use the follower to get your app back up and running faster.
- Index all foreign keys.
- Use a Heroku Follower database for analytics to limit reads on the primary database.
- Set
config.action_mailer.raise_delivery_errors = true
in the development environment. - Set
config.action_mailer.delivery_method = :test
in the test environment. - Use one
ActionMailer
for the app. Name itMailer
. - Use SendGrid or Amazon SES to deliver email in staging and production environments.
- Use single recipient SMTP in staging environment.
- Use the user's name in the
From
header and email in theReply-To
when delivering email on behalf of the app's users.
- Define a
PRIORITY
constant at the top of the class. - Define two public methods:
self.enqueue
andperform
. - Enqueue the job in
self.enqueue
like this. - Put background jobs in
app/jobs
. - Store IDs, not
ActiveRecord
objects for cleaner serialization, then re-find theActiveRecord
object in theperform
method. - Subclass the job from
Struct.new(:something_id)
. - Use
Delayed::Job
for background jobs.
- Avoid
its
,let
,let!
,specify
,subject
, and other DSLs. Prefer explicitness and consistency. - Disable real HTTP requests to external services.
- Don't prefix
it
blocks with 'should'. - Name outer
describe
blocks after the method under test. Use.method
for class methods and#method
for instance methods. - Order factories.rb: sequences, traits, factory definitions.
- Order factory definitions alphabetically by factory name.
- Order factory attributes: implicit attributes, newline, explicit attributes, child factory definitions. Each section's attributes are alphabetical.
- Prefix
context
blocks names with 'given' when receiving input. Prefix with 'when' in most other cases. - Run specs with
--format documentation
. - Test background jobs with a
Delayed::Job
matcher. - Use a
context
block for each execution path through the method. - Use a Fake to stub requests to external services.
- Use a
before
block to define phases of Four Phase Test. - Use integration tests to execute the entire app.
- Use non-SUT methods in expectations when possible.
- Use one expectation per
it
block. - Use stubs and spies (not mocks) in isolated tests.
- Don't support clients without Javascript.
- Don't support IE6.