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 premature optimizations. Get a version 1.0 out there as soon as you can. Until you have some users to measure, you're optimizing based on guesses.
- Use
bin/setup
for initial setup of the project and for development environment seed data. After running this script in a clean environment you should be able to run the tests and those should pass. - Use
bin/server
for starting all the processes required to run the project in a development environment, when suitable. - Use
bin/deploy
for deploying the project to staging and production environments, when suitable.
- Avoid global variables.
- Avoid long parameter lists (no more than 4).
- 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).
- Prefer small objects with a single, well-defined responsibility (no more than 100 lines).
- Tell, don't ask.
- Avoid optional parameters. Does the method do too much?
- Avoid monkey-patching.
- Prefer classes to modules when designing functionality that is shared by multiple models.
- Prefer
private
when indicating scope. Useprotected
only with comparison methods likedef ==(other)
,def <(other)
, anddef >(other)
.
- Declare dependencies in the
<PROJECT_NAME>.gemspec
file. - Reference the
gemspec
in theGemfile
. - 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 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 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. - If there are default values, set them in migrations.
- Keep
db/schema.rb
under version control. - Use only one instance variable in each view.
- Use SQL, not
ActiveRecord
models, in migrations. - Use the
.ruby-version
file convention to specify the Ruby version and patch level for a project. - Use
_url
suffixes for named routes in mailer views and redirects. Use_path
suffixes for named routes everywhere else. - Validate the associated
belongs_to
object (user
), not the database column (user_id
).
- Avoid
any_instance
in rspec-mocks and mocha. Prefer dependency injection. - Avoid
its
,let
,let!
,specify
,before
, andsubject
in 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::Job
matcher (or correspondent for Resque, Sidekiq, etc...) - Use stubs and spies (not mocks) in isolated tests.
- Use a single level of abstraction within scenarios.
- Use an
it
example 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.
- Use a pessimistic version in the
Gemfile
for gems that follow semantic versioning, such asrspec
,factory_girl
, andcapybara
. - Use a versionless
Gemfile
declarations for gems that are safe to update often, such as pg, thin, and debugger. - Use an exact version in the
Gemfile
for fragile gems, such as Rails. - Use
require: false
, whenever possible. Manually require the gem's files only where it is used
- 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
ActiveRecord
objects for cleaner serialization, then re-find theActiveRecord
object in theperform
method.
- Use a tool like LetterOpener 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-url
andfont-url
, noturl
, so the asset pipeline will re-write the correct paths to assets.
- Don't support clients without Javascript.
- Don't support IE8 or below.
- Don't parse the output of
ls
. See here for details and alternatives. - Don't use
cat
to provide a file onstdin
to a process that accepts file arguments itself. - Prefer a
/bin/sh
shebang. - Don't use any non-POSIX features when using a
/bin/sh
shebang. - If calling
cd
, have code to handle a failure to change directories. - If calling
rm
with 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
for
loops overwhile read
loops. - Prefer
grep -c
togrep | wc -l
. - Prefer
mktemp
over using$$
to "uniquely" name a temporary file. - Prefer
printf
overecho
. - 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
if
statements (if grep -q ...;
, notif [ -n "$(grep ...)" ];
). - Prefer reading environment variables over process output (
$TTY
not$(tty)
,$PWD
not$(pwd)
, etc). - Use
$( ... )
, not backticks for capturing command output. - Use
$(( ... ))
, notexpr
for executing arithmetic expressions. - Use
1
and0
, nottrue
andfalse
to 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
local
keyword with function-scoped variables. - Identify common problems with shellcheck.
In addition to Shell best practices,
- Prefer
${var,,}
and${var^^}
overtr
for changing case. - Prefer
${var//from/to}
oversed
for simple string replacements. - Prefer
[[
overtest
or[
. - Prefer process substitution over a pipe in
while read
loops. - Use
((
orlet
, not$((
when you don't need the result