A guide for programming well.
- Don't duplicate the functionality of a built-in library.
- Don't swallow exceptions or "fail silently."
- Don't write code that guesses at future functionality.
- Exceptions should be exceptional.
- Keep the code simple.
- Avoid global variables.
- Avoid long parameter lists.
- Limit collaborators of an object (entities an object depends on).
- Limit an object's dependencies (entities that depend on an object).
- Prefer composition over inheritance.
- Prefer small methods. Between one and five lines is best.
- Prefer small classes with a single, well-defined responsibility. When a class exceeds 100 lines, it may be doing too many things.
- Tell, don't ask.
- Avoid optional parameters. Does the method do too much?
- Avoid monkey-patching.
- Generate necessary Bundler binstubs for the project, such as
rakeandrspec, and add them to version control. - Prefer classes to modules when designing functionality that is shared by multiple models.
- Prefer
privatewhen indicating scope. Useprotectedonly with comparison methods likedef ==(other),def <(other), anddef >(other).
- Declare dependencies in the
<PROJECT_NAME>.gemspecfile. - Reference the
gemspecin theGemfile. - Use Appraisal to test the gem against multiple versions of gem dependencies (such as Rails in a Rails engine).
- Use Bundler to manage the gem's dependencies.
- Use Travis CI for Continuous Integration, indicators showing whether GitHub pull requests can be merged, and to test against multiple Ruby versions.
- Avoid bypassing validations with methods like
save(validate: false),update_attribute, andtoggle. - Avoid instantiating more than one object in controllers.
- Avoid naming methods after database columns in the same class.
- Don't change a migration after it has been merged into master if the desired change can be solved with another migration.
- Don't reference a model class directly from a view.
- Don't return false from
ActiveModelcallbacks, but instead raise an exception. - Don't use instance variables in partials. Pass local variables to partials from view templates.
- Don't use SQL or SQL fragments (
where('inviter_id IS NOT NULL')) outside of models. - Generate necessary Spring binstubs for the project, such as
rakeandrspec, and add them to version control. - If there are default values, set them in migrations.
- Keep
db/schema.rbordb/development_structure.sqlunder version control. - Use only one instance variable in each view.
- Use SQL, not
ActiveRecordmodels, in migrations. - Use the
.ruby-versionfile convention to specify the Ruby version and patch level for a project. - Use
_urlsuffixes for named routes in mailer views and redirects. Use_pathsuffixes for named routes everywhere else. - Validate the associated
belongs_toobject (user), not the database column (user_id). - Use
db/seeds.rbfor data that is required in all environments. - Use
dev:primerake task for development environment seed data. - Prefer
cookies.signedovercookiesto prevent tampering. - Prefer
Time.currentoverTime.now - Prefer
Date.currentoverDate.today - Prefer
Time.zone.parse("2014-07-04 16:05:37")overTime.parse("2014-07-04 16:05:37") - Use
ENV.fetchfor environment variables instead ofENV[]so that unset environment variables are detected on deploy.
- Avoid
any_instancein rspec-mocks and mocha. Prefer dependency injection. - Avoid
its,let,let!,specify,before, andsubjectin RSpec. - Avoid using instance variables in tests.
- Disable real HTTP requests to external services with
WebMock.disable_net_connect!. - Don't test private methods.
- Test background jobs with a
Delayed::Jobmatcher. - Use stubs and spies (not mocks) in isolated tests.
- Use a single level of abstraction within scenarios.
- Use an
itexample or test method for each execution path through the method. - Use assertions about state for incoming messages.
- Use stubs and spies to assert you sent outgoing messages.
- Use a Fake to stub requests to external services.
- Use integration tests to execute the entire app.
- Use non-SUT methods in expectations when possible.
- Specify the Ruby version to be used on the project in the
Gemfile. - Use a pessimistic version in the
Gemfilefor gems that follow semantic versioning, such asrspec,factory_girl, andcapybara. - Use a versionless
Gemfiledeclarations for gems that are safe to update often, such as pg, thin, and debugger. - Use an exact version in the
Gemfilefor fragile gems, such as Rails.
- Avoid multicolumn indexes in Postgres. It combines multiple indexes efficiently. Optimize later with a compound index if needed.
- Consider a partial index for queries on booleans.
- Constrain most columns as
NOT NULL. - Index foreign keys.
- Store IDs, not
ActiveRecordobjects for cleaner serialization, then re-find theActiveRecordobject in theperformmethod.
- Use SendGrid or Amazon SES to deliver email in staging and production environments.
- Use a tool like MailView to look at each created or updated mailer view before merging.
- Use CoffeeScript.
- Don't use a reset button for forms.
- Prefer cancel links to cancel buttons.
- Use Sass.
- Use
image-urlandfont-url, noturl, so the asset pipeline will re-write the correct paths to assets.
- Don't support clients without Javascript.
- Don't support IE6 or IE7.
- Setup new projects using Liftoff and follow provided directory structure.
- Prefer categories on
Foundationclasses to helper methods. - Prefer string constants to literals when providing keys or key paths to methods.
- Don't parse the output of
ls. See here for details and alternatives. - Don't use
catto provide a file onstdinto a process that accepts file arguments itself. - Don't use
echowith options, escapes, or variables (useprintffor those cases). - Don't use a
/bin/shshebang unless you plan to test and run your script on at least: Actual Sh, Dash in POSIX-compatible mode (as it will be run on Debian), and Bash in POSIX-compatible mode (as it will be run on OSX). - Don't use any non-POSIX features when using a
/bin/shshebang. - If calling
cd, have code to handle a failure to change directories. - If calling
rmwith a variable, ensure the variable is not empty. - Prefer "$@" over "$*" unless you know exactly what you're doing.
- Prefer
awk '/re/ { ... }'togrep re | awk '{ ... }'. - Prefer
find -exec {} +tofind -print0 | xargs -0. - Prefer
forloops overwhile readloops. - Prefer
grep -ctogrep | wc -l. - Prefer
mktempover using$$to "uniquely" name a temporary file. - Prefer
sed '/re/!d; s//.../'togrep re | sed 's/re/.../'. - Prefer
sed 'cmd; cmd'tosed -e 'cmd' -e 'cmd'. - Prefer checking exit statuses over output in
ifstatements (if grep -q ...;, notif [ -n "$(grep ...)" ];). - Prefer reading environment variables over process output (
$TTYnot$(tty),$PWDnot$(pwd), etc). - Use
$( ... ), not backticks for capturing command output. - Use
$(( ... )), notexprfor executing arithmetic expressions. - Use
1and0, nottrueandfalseto represent boolean variables. - Use
find -print0 | xargs -0, notfind | xargs. - Use quotes around every
"$variable"and"$( ... )"expression unless you want them to be word-split and/or interpreted as globs. - Use the
localkeyword with function-scoped variables. - Identify common problems with shellcheck.
In addition to Shell best practices,
- Prefer
${var,,}and${var^^}overtrfor changing case. - Prefer
${var//from/to}oversedfor simple string replacements. - Prefer
[[overtestor[. - Prefer process substitution over a pipe in
while readloops. - Use
((orlet, not$((when you don't need the result
- Avoid partial functions (
head,read, etc). - Compile code with
-Wall -Werror.
- Avoid using
$without scoping tothis.$in views and components. - Prefer to make model lookup calls in routes instead of controllers (
find,findAll, etc.). - Prefer adding properties to controllers instead of models.
- Don't use jQuery outside of views and components.
- Prefer to use predefined
Ember.computed.*functions when possible.