This guide provides a set of best practices and style prescriptions for application development based on SciMed Solutions' years of development experience.
Read this before making any changes to the style guide
If you make any changes to the style guide, please clearly describe the logic that led to the decision in your commit message. Developers who are not privy to the initial discussion will need to understand why the decision was made in order to evaluate whether the decision is still relevant and whether it is pertinent to their current situation.
- Adhere to Rubocop when possible.
- See our default
.rubocop.yml
. - In general, we disable Rubocop rules via magic comment around the method, block or line that contains the offense. For
Metrics/ClassLength
andMetrics/BlockLength
, we disable them by adding the file to theExclude
list under the offending cop in the project's.rubocop.yml
.
- See our default
- Mark class methods as private by calling
private_class_method def self.foo
when using recent Ruby versions (> 2.1.0). For earlier versions, mark methods as private by callingprivate_class_method :my_method
directly after the end of the method. - Keep private instance methods clustered together at the end of the file.
- Keep private class methods clustered together after public class methods but before public instance methods.
- Write arrays on a single line when they are fewer than 80 characters long. Otherwise, write them as one line per item.
- Avoid meta-programming classes for reasons of readability.
- Define your
Rakefile
to be similar to the example. - When writing predicate methods (methods ending in
?
), prefer having them return explicittrue
orfalse
values rather than truthy or falsey values. - When writing Ruby modules, keep them in the following locations based on use case:
- When the module is meant for a model or controller, put them in
app/models/concerns/
orapp/controllers/concerns/
(even if they are not an explicitActiveSupport::Concern
). - When the module is not meant for a model or controller, keep it in the directory for its namespace (e.g.
SpreadsheetImporters::Formatter
would likely live inapp/services/spreadsheet_importers/formatter.rb
).
- When the module is meant for a model or controller, put them in
- When calling methods, include the optional parentheses unless the method call fits on one line and fulfills one of the following criteria. If such a criterion is fulfilled, you may choose to use the parentheses or not.
- The method call takes place in a view
- The method call takes place in a spec
- The caller of the method is implicit and a class (e.g.
has_many
,before_action
)
- Pretty much all of the application's code should stay out of
lib/
. Think oflib/
as a place to put components that are generalized enough that they could be used in other applications (but then why not make those things into gems?). - Organize objects into
app/
:presenters/
,services/
,serializers/
,validators/
. If you have another collection of objects that are general enough to warrant their own folder, feel free to do so (e.g.app/forms
). - Organize classes into namespaces when appropriate.
- Treat acronyms as words in class names.
- Consider using methods instead of constants. Methods are easier to stub, test, and mark private.
- Avoid using single letter functions (such as
j
,l
, andt
). Use their more descriptive aliases instead.
- Avoid more than 1 level of resource nesting.
- Do not use the
match
wildcard route matcher. - Only add routes that are explicitly used/enabled. Use
only
orexcept
in your routes file to prevent additional unsupported routes from being created.
- Structure Controller content in this order.
- Try to avoid adding non RESTful actions to a resource. Consider if a non RESTful action for one controller could be a RESTful action for a new controller (that does not necessarily map to a model).
- Storing anything other than the session id in session is discouraged.
- This can be a security risk unless done carefully. This also makes the browser part of the current state of the application, allowing things to get out of whack. For example keeping the current working record in the session causes problems if users try to use the application with more than one tab or window.
- Keep controllers skeletal—they shouldn't contain business logic.
- Controllers should ideally only have the following responsibilities:
- Looking up objects
- Redirecting
- View rendering
- Set flash
- Assigning raw params to objects
- Converting params into the form they will take in the database should be done via service objects
- Wrapping objects in presenters
- Authentication/authorization
- Instantiating/calling service objects
- If you find yourself needing more than two instance variables consider using a Presenter object, which could be more easily unit tested.
- If you use code generation tools (e.g. scaffold) remove any unused code like
respond_to
blocks and default comments. - Ensure there is a method in the controller for every route that is rendered even if the method is empty.
- Setting
current_user
should be the responsibility of theApplicationController
- Structure model content in this order.
- Using non-ActiveRecord models is encouraged.
- Avoid adding
default_scope
. - Avoid adding callbacks in favor of an object decorator.
- Avoid adding callbacks that modify other models.
- Use of
class << self
is discouraged in ActiveRecord models. - Use a Validator object for custom validations.
- Use of
update_attribute
,update_column
,update_columns
, andupdate_all
skip validations. Rubocop should help you with this.update_attribute
will also persist changes to any other dirty attributes on your model as well, not just the attribute that you are trying to update. However, if you do not need validations or callbacks,update_column
is preferred over update_attribute because it is easier to remember that it does not run validations. - When accessing associations through other associations, use
has_many
orhas_one
through
rather than a delegation or a custom method when all target objects in the associations are persisted. Use delegations or custom methods when there is useful data that is not yet persisted. Keeping the data in associations allows preloading to prevent n+1 queries and ensures that more processing is done via SQL than Ruby, which is more performant. Unfortunately, associationsthrough
other associations will not see non-persisted objects. - Extract complicated scopes into a Database View.
- Accessing database models or the database from the view breaks the separation that is the goal of MVC. It would be best to build presenter objects which store the data needed for the view.
- Never make complex formatting in the views, moving the formatting to
a presenter object.
- For example, if you are displaying a schedule, do not put the logic for grouping events into days in the view.
- Avoid using DRY principles to reduce duplication of code that is visually the same, rather than essentially the same. Code should not be made DRY if the business motivation behind duplicated code differs between cases. Please see In Defense of Copy Paste for more information. While this is applicable in all forms of code, this is particularly problematic in view code.
- Put data attributes in a hash when using a view helper.
- Example:
f.input :foo, data: { input_method: :foo }
- Note that underscores in keys will be translated to dashes when rendered to HTML. The previous example's data attribute will render as
data-input-method="foo"
.
- Example:
- Prefer
f.collection_select :foo
overf.select :foo, options_from_collection_for_select
when possible.- Note that
collection_select
sets the selected value by default whereasoptions_from_collection_for_select
needs the selected value to be specified as a parameter.
- Note that
- Ensure that label tags connect with the correct form input.
- Note that
form_with
does not do this automatically.
- Note that
- Don't make partials for the fun of it. It is discouraged to have partials more than 2 levels deep.
- This is both for ease of maintainability and performance.
- Partials should rely on local variables rather than instance variables.
- Utilize collection rendering rather than rendering a partial for each element in a collection for purposes of performance.
- For new projects, use ERB as the templating engine. For old projects, use whatever is already in place.
- Prefer
form_with
overform_tag
andform_for
when it is available. Ensure that it generatesid
attributes (possibly by settingRails.application.config.action_view.form_with_generates_ids = true
). - Try to avoid multiline embedded Ruby—it's likely indicative of logic that should be extracted to a presenter or service object. When necessary, use the following format:
<%=
f.select(
:foo,
select_options(...),
class: 'foo',
data: { bar: :baz }
)
%>
- Both
schema.rb
/structure.sql
and migration files should be maintained so that developers can migrate from scratch or load the schema. - Migrations should define any classes that they use (if the class is deleted in the future, the migrations should still be able to run).
- Use those database features! Enforce default values, null constraints, uniqueness, etc. in the database (via migrations) in addition to the application layer, as the database can avoid race conditions and is faster and more reliable.
- If you have
NOT NULL
constraints, be sure to add adependent
option on the association. Otherwise, you will get invalid query exceptions when things are deleted. Also, consider a foreign key with theCASCADE
option. - When you create a new migration, run it both
up
anddown
before committing the change (rake db:migrate:redo
will run the very last migration down and then up again). - Prefer using
change
in migrations to writing individualup
anddown
methods when possible. - Make sure to update seeds/factories when adding columns (particularly those with validations) or tables.
- If you are doing something that cannot be reversed (such as removing data or deleting a column), raise
ActiveRecord::IrreversibleMigration
in yourdown
method. Please leave a comment about why the migration is irreversible. Try to avoid irreversible migrations when possible. - If a migration is partially reversible, implement the partial reversion and leave a comment as to what the unreverted side effects will likely be.
- If you are modifying data in a migration be sure to call two methods at the beginning of the migration. If you don't reset the column information then some data could be silently lost instead of saved. Also, Rails will only reset column information once even if you call it multiple times, which is why the
schema_cache
needs to be cleared first.
- Avoid relying on production database dumps for development. Make sure there are reliable seeds so that future developers can get up and running and access all features quickly.
- Create and update seeds as you develop.
- Seeds should be capable of running multiple times without producing errors or bad duplicates.
- Seeds should mirror the current state of the app and provide enough data to access and test all features of the application.
- Data needed for all environments, including production, should be in seeds.
- Other seeds should be kept in the
db/seeds
directory. - Structure the seeds as follows:
db/
seeds/
development/
model_1.rb
production/
model_1.rb
staging/
model_1.rb
development.rb
production.rb
staging.rb
seeds.rb
- Consider using FactoryGirl/Bot factories to seed development data.
- Tear down and rebuild your database regularly.
- Provide both HTML and plain-text view templates, or use
premailer-rails
to auto-generate text templates based on your HTML templates. - If you need to use a link in an email, always use the
_url
, not_path
methods, to ensure the absolute path is correct. - Format the from and to addresses as
Your Name <info@your_site.com>
. - Consider sending emails in a background process to prevent page load delays.
- When sending email to multiple users, send an individual email to each person rather than having multiple recipients in one email.
- This increases security because users can't see each other's addresses and makes it easier to handle errors with invalid email addresses.
- Log when emails are sent and when they fail to send. Consider separating the email logs from the application logs.
- Structure Gemfile content in alphabetical order with groups at the bottom in the following order:
:development
:test
:staging
:production
- Do not run
bundle update
unless for a specific gem. Updating all of the gems without paying attention could unintentionally break things. - Remove default comments.
- Versioning is discouraged unless a specific version of the gem is required (but keep an eye out for breaking things when gem versions update!). If you do need to version, add a comment.
- Run
bundle audit --update
before pushing. - Prefer using
update --source
orupdate --conservative
when you want to update a specific gem without updating its dependencies.
- Add a default date and datetime format to your
en.yml
. - Consider using localization/internationalization config files to encapsulate customer-facing strings such as error messages when:
- the text is likely to change frequently OR
- the text is a template that is used in multiple places (i.e. to keep the code DRY). Note that i18n supports variable interpolation.
-
When rescuing errors, log the error message and backtrace similarly to the following:
rescue SomeError => e ExceptionLogger.log_error(e) # custom error handling behavior end class ExceptionLogger def self.log_error(e) Rails.logger.error("ERROR: #{e.message}") Rails.logger.error(e.backtrace.join("\n")) end end
-
Consider logging error messages displayed to users as well.
-
When avoiding this logging (due to performance, for example), add a comment to the
rescue
block that informs future developers why logging is not occurring.
- Query DOM elements using
data-
attributes instead of CSS classes. CSS classes should be used exclusively for styling, whereasdata-
attributes should be used exclusively for JavaScript querying. - Any JS involving the DOM should be deferred until the DOM is ready.
- Be careful when including data in the DOM.
- This is particularly true from a security stand point. Do not put secure or sensitive information in the DOM. For example, do not have a hidden field with a SSN or that determines whether someone is an admin.
- Just because a user can't see data on the page by default doesn't mean that they can't access it.
- Always prefix jQuery variables with
$
.- Example:
$finder = $('.finder')
- Example:
- Always prefix Immutable.js variables with
$$
.- Example:
$$state
- Example:
- When short-circuiting an event listener, explicitly call
event.stopPropagation
and/orevent.preventDefault
as appropriate rather than relying onreturn false
.- This is because
return false
has different effects in different contexts (see the discussion here).
- This is because
- Use an Initializer class to set up application-wide JavaScript functionality such as date pickers, tooltips, dropdowns, and the like. It should be run with the whole HTML body upon page load and can be re-run with portions of the DOM that are inserted/updated via AJAX.
- Use
getUTCDay()
overgetDay()
to prevent surprises due to time zones. - Consider RIP.js when your JS needs would be served by refreshing parts of the page
- Avoid writing inline JavaScript in your HTML
All new apps should:
- Aim to support at least ES6/ES2015.
- Use Babel to transpile to ES5+ if called for by browser support requirements.
- Use webpack for bundling and module resolution.
- Use
yarn
for npm package management.
The most straightforward way to meet these requirement is by installing and configuring the webpacker
gem.
- Prefer the ES6
import
syntax when possible, but you can userequire
for npm packages where needed.
Each project supporting modern JavaScript should use eslint, using a .eslintrc
file, as in this example (note that the env
and globals
values may vary by project).
Eslint can by run on the command line with yarn eslint
if the following is added to your project's package.json
:
{
"scripts": {
"eslint": "eslint --format codeframe app/client"
}
}
Eslint should be run as part of your project's default rake task. See the example Rakefile.
Only use CoffeeScript on applications that already use it; consider writing new features on those projects in modern JS. You may find the following tool useful for converting JS to CoffeeScript: http://js2.coffee/
When using CoffeeScript:
- Namespace JavaScript objects for related chunks of functionality (Components, Engines, etc...).
- Use JS classes and encapsulation (e.g., IIFE or Object Literal notation) wherever possible.
- Separate responsibilities of vanilla JavaScript into separate entities.
- CoffeeScript file extensions should not include
.js
where possible. Preferfoo.coffee
tofoo.js.coffee.
- For ternary operations in CoffeeScript, prefer
if foo then bar else baz
. - Camelcase variable and function names.
Use React when building complex, stateful UIs.
- In addition to the above suggestions:
- Use Redux.
- Use jest for testing.
- Use selectors to read the state tree.
- Always test your selectors.
- Put selectors in the same file as the reducers for the same part of the state tree.
- Do not mutate the state tree.
- If you are designing a JSON API, consider conforming to the JSON API specification.
- It is generally considered good practice to keep your JSON structure as shallow as possible.
- Test the schema of JSON generated by your app.
- Create a JSON schema according to the documentation provided here.
- Check your schema's validity using tools like this one.
- Add examples to your specs to check JSON generated by your app against the appropriate schema. The approach described by Thoughtbot using the json-schema gem has proven successful.
- See this example for whitespace usage.
- Use agreed-upon CSS framework (e.g. fomantic-ui) where possible.
- Try to group styles by function; consider alphabetizing when unsure.
- Use the SCSS
@import
declaration to require stylesheets vs*= require
. - Use the
asset-url
helper for images and fonts.- Example
- Note that if you use any of the Ruby helpers, the file name requires
.erb
at the end of the file name (e.g.my_styles.scss.erb
).
- Lowercase and dasherize class names.
- Avoid the
>
direct descendant selector when the descendant is a tag for performance reasons. - Use caution when using ID selectors.
- Use SCSS variables (for example, colors are often used in multiple places).
- Place variables in either your
application.scss
orbase/_variables.scss
.
- Place variables in either your
- Generic naming is favored over domain-driven naming (e.g.
.aside
vs.cytometry
). - Prefer naming elements by content type over visual traits (e.g.
.aside
vs.left-side-bar
). - Use
box-sizing: border-box;
.- Whenever browsers support it, this allows you to define the size of actual boxes, rather than the size before padding and borders are added. Generally works for IE8+.
- Write out the full hex code in uppercase.
- Example:
#AAAAAA
instead of#aaa
.
- Example:
- SCSS file extensions should not include
.css
where possible. Preferfoo.scss
tofoo.css.scss
. - Prefer
.scss
over.sass
. - Follow SMACSS guidelines for CSS organization (Base, Layout, Module, State, Theme).
- Always use a CSS reset. If the CSS framework being used does not provide one, consider using normalize-rails.
- When creating individual [S]CSS files, prefix the file name with
_
to denote that that file is a partial that should be compiled intoapplication.css
rather than being served by itself as a separate file.
- Do not use
<image>
tags for decorative content. - Use of presentational markup is discouraged (
<b>
,<i>
, etc). - Use of XHTML markup is discouraged, e.g.
<br />
. - Avoid inline CSS and JavaScript
- Use layout tags (e.g.
<section>
,<header>
,<footer>
,<aside>
).- Note that you are not bound to have only one
<header>
or one<footer>
tag on a page, but you can only have one<main>
tag.
- Note that you are not bound to have only one
- If in a partial, modal file names should be suffixed with
_modal.html.erb
. - Double-quote raw HTML attributes.
- Example:
<div id="foo"></div>
- Example:
data
attributes should use-
as a word separator (e.g.data-attribute
rather thandata_attribute
)- Default to the following FontAwesome icons for these common use cases:
- Save: 'check'
- Delete: 'times'
- Cancel: 'ban'
- Edit: 'pencil alternate'
- Adhere to Rubocop (rubocop-rspec) when possible.
- See our default
.rubocop.yml
.
- See our default
- Avoid incidental state when setting up expectations.
- Avoid defining new constants in specs, as it could lead to flakey tests due to application constants being overwritten.
- Be careful when using iterators to generate tests; they make debugging more difficult (all of the tests share line numbers and the
it
description block). Or consider printing a custom error message to give more information about which test is failing.- It's okay not to be DRY if repetition of code improves readability.
- Use factories rather than fixtures.
- Use linting with FactoryGirl/Bot (
FactoryBot.lint
). - When testing attributes that are set via FactoryBot/Girl, make sure to set the attribute directly in the test rather than relying on the default Factory value.
- This ensures that someone reading the test only needs to look at the test file to understand what is being checked.
- Also makes changing the factory less destructive.
- Define factories in
spec/factories/model_name.rb
rather thanspec/factories.rb
. - Prefer using FactoryBot traits over nested factories.
- Avoid redefining parts of the application in tests. For example, don't re-define
Rails.development?
. - Follow the whitespace guide here.
- Prefer
let!
overlet
andbefore
. - Keep files used for specs (such as spreadsheets, PDFs or images) in
spec/assets/
. - Review the internal best practices guide on GitLab.
- Use shared examples to test common behavior.
- Use shoulda-matchers for testing validations, associations, nested attributes, and delegations in models.
- Prefer testing complicated scopes using an integration test that confirms the expected behavior against persisted records.
- Use
let
blocks for assignment instead ofbefore(:each)
(let
blocks are lazily evaluated). - When writing unit tests, stub out all public dependencies that are not the unit itself.
- Stub all external connections (HTTP, FTP, etc).
- Prefer liberally writing feature tests over controller tests.
- Favor new syntax (on new projects, use RSpec 3.0+), e.g. favor
expect
overshould
on RSpec 2.11+.
- Always provide a description string to
it
blocks unless the block contains a Shoulda Matcher expectation. - Keep the full spec description as grammatically correct as possible.
- Format
describe
block descriptions for class methods as'.some_class_method_name'
and for instance methods as'#some_instance_method_name'
. - Avoid conditionals in
it
block descriptions. Instead, usecontext
blocks to organize different states. - Prefer using only one expectation per
it
block for unit tests.- If setup is expensive, it may be reasonable to use multiple expectations in a single
it
block.
- If setup is expensive, it may be reasonable to use multiple expectations in a single
- Prefer using
describe
,context
, andit
instead offeature
andscenario
.
- Keep the README in the project root directory.
- Use markdown in the
docs/
directory when appropriate. - When a new medium or large feature gets added, add a paragraph or a couple of paragraphs of documentation (perhaps as a new file in the
docs/
folder) describing the real life scenarios and people including their goals and how it is intended to be used. - Deploy and server information should go in
docs/DEPLOY.md
. Link to this document in the README. This document should include the following:- Deploy instructions.
- Consider adding a link to the Tempus task template for the deploys.
- Links to credentials for each server.
- Link to the application for each environment.
- The README should include information about dependencies that people will need to understand to understand the application, how to run the tests, which systems and browsers are supported, and how a developer can get the application up and running.
- Prefer adding documentation to the
README
anddocs/
over the wiki. If documentation exists elsewhere, link to it from the README. - If you notice any of the above has become outdated, update the README accordingly.
-
Write SQL keywords and function calls in upper case.
-
Write table names, column names, and aliases in lower case.
-
Put primary keywords for the main query at the beginning of a new line.
- Example:
SELECT ... FROM ... WHERE ... ORDER BY ...
-
When using heredoc for SQL strings, use
<<~SQL
as the opening tag. This allows for SQL syntax highlighting in certain text editors. -
For performance reasons, prefer Common Table Expressions over subqueries when available.
-
Avoid making materialized views that depend upon other materialized views.
-
Don't be afraid to add new database views.
-
Put the first selected column on the same line as
SELECT
. Put each additional selected column name on its own line, indented by two spaces.- Example:
SELECT users.first_name, users.last_name, posts.title, posts.body FROM ...
-
Qualify column names with their table name if the query involves more than one table.
-
Alias column names at will.
-
In general, avoid aliasing tables; use the full table name instead.
-
Do alias tables when your query refers to the same table more than once in different contexts. In this case, choose aliases that explain how the table is used differently in each context.
- Example:
SELECT supervisors.first_name AS "supervisor_first_name", supervisors.last_name AS "supervisor_last_name", employees.first_name AS "employee_first_name", employees.last name AS "employee_last_name" FROM people AS "employees" JOIN people AS "supervisors" ON employees.supervisor_id = supervisors.id
-
Start
JOIN
clauses on the line after theFROM
clause, indented by two spaces. -
Start
ON
clauses on the line after itsJOIN
clause, indented by two spaces more than theJOIN
line. -
Start
AND
orOR
clauses on the line after theON
clause, indented by two spaces more than theON
line.SELECT ... FROM assays JOIN preps ON assays.source_id = preps.id AND assays.source_type = 'Prep' JOIN cultures ... ON ... WHERE ...
-
Start
AND
orOR
clauses on the line after theWHERE
clause, indented by two spaces more than theWHERE
line. -
If parentheses are required or clarifying, start a new line after the opening paren and indent lines inside the parentheses. Place the closing paren on a new line indented as far as the start of the line where the parentheses start:
SELECT ... FROM ... WHERE scored_on IS NOT NULL OR ( discarded = TRUE AND id < 500 ) ORDER BY ...
-
Give explanatory aliases to nested queries when it is needed or clarifying
-
Start a new line after the opening paren and indent lines inside the parentheses. Place the closing paren on a new line indented as far as the start of the line where the parentheses start, followed by the alias if one is used:
SELECT ... FROM ( SELECT COUNT(*) AS "num_positions_filled", box_id FROM stocks WHERE disabled_on IS NULL GROUP BY box_id ) AS "box_counts"
-
Use the same style guidelines as the main query, but indented.
-
Set up bullet in all development environments to detect n+1 queries.
-
Consider memory usage and garbage collection. Using too much of a machine's memory causes performance issues by itself, but it also leads to more garbage collection, which is very slow.
-
When appropriate and possible, include filtering/sorting/aggregation logic in your SQL queries rather than running a query and operating on the resulting relation in Ruby. For example:
- Use ActiveRecord's
pluck
over Ruby'smap
. - Use ActiveRecord's
order
over Ruby'ssort
/sort_by
. - Use ActiveRecord's
maximum
overmap
/pluck
and Ruby'smax
. - Use ActiveRecord's
minimum
overmap
/pluck
and Ruby'smin
. - Use ActiveRecord's
sum
overmap
/pluck
and Ruby'ssum
. - Use ActiveRecord's
average
over a customaverage
ormean
method.
- Use ActiveRecord's
-
Use ActiveRecord's
exists?
over itsany?
for Rails less than 5.1. Source -
Use database indexes (including multi-column and partial indexes when appropriate).
-
Consider using
select
when writing ActiveRecord queries.- Not only will the query be faster, Ruby will be able to instantiate fewer and lighter-weight objects, thereby leading to less garbage collection.
-
Be aware of the following situations where preloading data can lead to worsened performance.
-
When an association needs to be preloaded, do so at the earliest possible opportunity, such as when loading a record with
params[:id]
in the controller layer. This will help provide a single source of truth about what is loaded into memory. This has several advantages:- Future developers will know whether they need to start or stop preloading any associations added or removed in new code.
- Future developers will know whether to use Ruby or SQL for data
filtering (e.g.
where(baz: true)
if the association is not already in memory orselect(&:baz?)
when it is already in memory).
-
Utilize collection rendering rather than rendering a partial for each element in a collection.
-
Minimize external resources that need to be fetched upon page load.
-
Consider the following performance resources:
- Ruby Performance Optimization by Alexander Dymo
- Rails Guide to Caching
- Native extensions such as fast_blank
- Rubocop's performance cops
- Fast Ruby,
fasterer and
Erik Michaels-Ober's talk on Writing fast Ruby
- NOTE: the Fast Ruby/fasterer data currently comes only from Ruby 2.2.0 on OSX; please run the benchmarks in your specific environments to validate their suggestions.
- ruby-prof
- benchmark-ips
- Chrome developer tools' Performance features (including performance audits)
- When possible, opt against reading or writing to the database in unit tests.
- Set
config.profile_examples
in yourspec_helper.rb
to see if any tests are particularly slow. - Consider raising the logging level in your
rails_helper.rb
(e.g.Rails.logger.level = 4
). - When most tables are not populated in a test with Database Cleaner's
:truncation
strategy, usepre_count: true
. - Consider using the
:deletion
strategy with Database Cleaner between specs instead of:truncation
(but alwaysclean_with :truncation
before(:suite)
). - Use FactoryBot >= 4.8 for the config option
FactoryBot.use_parent_strategy
to ensure that associated data is not accidentally created
- Avoid using exclamation points in client-facing messages unless needed.
- Prefer using
—
over--
. - Prefer usage of the Oxford comma.
- Check for typos in client-facing messages. Consider using a spellchecker in your primary text editor.
- Within a project, please be consistent with button text (e.g. all buttons to persist changes should say "Save" rather than some that say "Edit" and some that say "Save").
- Use "Log In" as a verb and "Login" as a noun/adjective.
- Use title case for page headers, button text, and table headers; use normal sentence casing (i.e., only capitalize the first word and proper nouns) elsewhere, including form input labels.
- Document the project's supported browsers in a
.browserslistrc
file in the root directory of the project. See this example. - Please use dockerfile_lint
to lint your
Dockerfile
s.