• Stars
    star
    350
  • Rank 116,864 (Top 3 %)
  • Language
  • Created almost 12 years ago
  • Updated almost 6 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A developer's guide of practices to follow when building Rails applications.

Rails 4.X Development Standards Guide

Approach

Apply the YAGNI and KISS principles to all of the following.

  • General architecture
  • Product and API features
  • Implementation specifics

Documentation

Make an effort for code to be self documenting.

  • Prefer descriptive names in your code. e.g. user_count is a better name than len.
  • Use YARD formatted comments when code documentation is deemed necessary.
  • Avoid in method comments as they are a cue that the method is too complex; instead, refactor into additional classes/methods that better express intent & purpose.

General Guidelines

These guidelines are based on Sandi Metz's programming "rules" which she introduced on Ruby Rogues.

The rules are purposefully aggressive and are designed to give you pause so your app won't run amok. It's expected that you will break them for pragmatic reasons... just be sure that you aware of the trade-offs.

  • Classes can be no longer than 100 lines of code.
  • Methods can be no longer than 5 lines of code.
  • Methods can take a maximum of 4 parameters.
  • Controllers should only instantiate 1 object.
  • Views should only have access to 1 instance variable.
  • Never directly reference another class/module from within a class. Such references should be passed in.

Be thoughtful when applying these rules. If you find yourself fighting Rails too often, a more pragmatic approach might be warranted.

Models

  • Never use dynamic finders. e.g. find_by_...
  • Apply extreme caution when using callbacks and observers as they typically lead to unwanted coupling.

All models should be organized using the following format.

class MyModel < ActiveRecord::Base
  # extends ...................................................................
  # includes ..................................................................
  # relationships .............................................................
  # validations ...............................................................
  # callbacks .................................................................
  # scopes ....................................................................
  # additional config (i.e. accepts_nested_attribute_for etc...) ..............
  # class methods .............................................................
  # public instance methods ...................................................
  # protected instance methods ................................................
  # private instance methods ..................................................
end

NOTE: The comments listed above should exist in the file to serve as a visual reminder of the format.

Controllers

Controllers should use strong parameters to sanitize params before performing any other logic.

Concerns

Its generally a good idea to isolate concerns into separate modules. Use concerns as outlined in this blog post.

  • Concerns should be created in the app/COMPONENT/concerns directory.
  • Concerns should use the naming convention COMPONENT + BEHAVIOR. For example, app/models/concerns/model_has_status.rb or app/controllers/concerns/controller_supports_cors.rb.
  • CRUD operations that are limited to a single model should be implemented in the model. For example, a full_name method that concats first_name and last_name
  • CRUD operations that reach beyond this model should be implemented as a Concern. For example, a status method that needs to look at a different model to calculate.
  • Simple non-CRUD operations should be implemented as a Concern.
  • Important! Concerns should be isolated and self contained. They should NOT make assumptions about how the receiver is composed at runtime. It's unacceptable for a concern to invoke methods defined in other concerns; however, invoking methods defined in the intended receiver is permissible.

Extensions & Monkey Patches

  • Be thoughtful about monkey patching and look for alternative solutions first.
  • Use an initializer to load extensions & monkey patches.

All extensions & monkey patches should live under an extensions directory in lib.

|-project
  |-app
  |-config
  |-db
  |-lib
    |-extensions <-----

Use modules to extend objects or add monkey patches. This provides some introspection assistance when you need to track down weirdness.

Here's an example:

module CowboyString
  def downcase
    self.upcase
  end
end
::String.send(:include, CowboyString)
String.ancestors # => [String, CowboyString, Enumerable, Comparable, Object, Kernel]

Project Structure

Unfortunately, some of the defaults in Rails seem to encourage monolithic design. This is especially true for developers new to the framework. However, it's important to note that Ruby & Rails include everything you need to create well organized projects.

The key is to keep concerns physically isolated. Applying the right strategies will ensure your project is testable and maintainable well into the future.

Domain Objects

Meaningful projects warrant the creation of domain objects, which can usually be grouped into categories like:

  • policies
  • presenters
  • services
  • etc...

Domain objects should be treated as first class citizens of your Rails application. As such they should live under app/DOMAIN. For example:

  • app/policies
  • app/presenters
  • etc...

Always apply Rails-like standards to domain object names. For example, app/presenters/user_presenter.rb

Additional reading on the subject of domain objects.

Gems & Engines

Sometimes concerns should be grouped into isolated libraries. This creates clear separation & allows strict control of depedencies.

Note: This approach does not require you to open-source parts of the app.

Bundler supports 2 strategies that facilitate this type of application structure.

  1. Locally pathed gems - look for the :path option
  2. Git hosted gems

You can also host a local Gemserver.

It can be daunting to know when creating a gem or engine is appropriate. Stephan Hagemann's presentation at Mountain West Ruby provides some guidance. He is also writing a book on Component based Rails Applications.

Custom gems & engines should be created under the vendor directory within your project.

|-project
  |-vendor
    |-assets
    |-engines <-----
    |-gems    <-----

Additional reading on creating modular Rails applications.

Here are some popular Rails engines that illustrate how to properly isolate responsibilities to achieve modularity.

Refactoring

Good software design often emerges empirically from implementation. The practice of continually moving toward better design is known as refactoring. Plan on a persistent effort to combat code's natural state of entropy. Use prudence to ensure you don't attempt refactoring too much at once.

Refactoring Priorities

  1. Refactor to methods before classes
  2. Refactor to classes before libraries
  3. Refactor to libraries before services
  4. Refactor to libraries and/or services before a rewrite

Gem Dependencies

Gem dependencies should be hardened before deploying the application to production. This will ensure application stability.

We recommend using exact or tilde version specifiers. When using tilde specifiers, be sure to include at least the major & minor numbers. Here's an example.

# Gemfile
gem 'rails', '3.2.11' # GOOD: exact
gem 'pg', '~>0.9'     # GOOD: tilde
gem 'yell', '>=1.2'   # BAD: unspecific
gem 'nokogiri'        # BAD: unversioned

Bundler's Gemfile.lock solves the same problem, but we discovered that inadvertent bundle updates can cause problems. It's much better to be explicit in the Gemfile and guarantee app stability.

This will cause your project to slowy drift away from the bleeding edge. A strategy should be employed to ensure the project doesn't drift too far from contemporary gem versions. For example, upgrade gems on a regular schedule (every 3-4 months) and be vigilant about security patches. Using bundle outdated will help with this.

Code Quality Tools

It's a good idea to run regular code analysis against your project. Here are some of the tools we use.

A Note on Client Side Frameworks

Exciting things are happening in the world of client side frameworks.

Be thoughtful about the decision to use a client side framework. Ask yourself if the complexity of maintaining 2 independent full stacks is the right decision. Often times there are better and simpler solutions.

Read the following articles before deciding. In the end, you should be able to articulate why your decision is the right one.

JavaScript is like a spice. Best used in sprinkles and moderation.

β€” DHH (@dhh) September 2, 2013
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

We generally agree with DHH regarding client side frameworks and have discovered that thoughtful use of something like Vue meets most of our needs.

More Repositories

1

docker-graphite-statsd

Docker image for Graphite & Statsd
Python
871
star
2

universalid

Fast, recursive, optimized, URL-Safe serialization for any Ruby object
Ruby
325
star
3

turbo_boost-commands

Commands to help you build robust reactive applications with Rails & Hotwire.
HTML
277
star
4

turbo_boost-streams

Take full control of the DOM with Turbo Streams
HTML
260
star
5

model_probe

ActiveRecord schema visualization and model organization made easy
Ruby
161
star
6

relay

140
star
7

sr_mini

A single file Rails app that will have you running a StimulusReflex and CableReady demo in just 2 steps.
Ruby
138
star
8

stimulus_reflex_expo

StimulusReflex demos
Ruby
90
star
9

turbo_boost-elements

Pre-built easy to use reactive TurboBoost behaviors for Rails/Hotwire apps.
JavaScript
83
star
10

goldmine

Extract a wealth of information from lists
Ruby
75
star
11

composite_cache_store

A composite cache store comprised of layered ActiveSupport::Cache::Store instances
Ruby
74
star
12

job_contracts

Enforceable contracts with test-like assurances for jobs
Ruby
74
star
13

debounced

Debounced versions of standard DOM events
JavaScript
68
star
14

stimulus_controllers

[WIP] Stimulus controller library common enough to span multiple projects
JavaScript
59
star
15

chatter

Build a twitter clone in 10 mins with Rails, CableReady, and StimulusReflex
Ruby
52
star
16

tag_columns

Fast & simple Rails ActiveRecord model tagging using PostgreSQL's Array datatype
Ruby
51
star
17

stimulus_reflex_todomvc

An implementation of TodoMVC using Ruby on Rails, StimulusJS, and StimulusReflex
Ruby
49
star
18

bg

Non-blocking ActiveRecord method invocation
Ruby
47
star
19

pipe_envy

Elixir style pipe operator for Ruby
Ruby
46
star
20

hero

Business process modeling for the Rubyist
Ruby
39
star
21

polysearch

Simplified polymorphic full text + similarity search based on postgres
Ruby
38
star
22

coast

RESTful behavior for Rails controllers
Ruby
28
star
23

field_mapper

Data mapping & transformation
Ruby
25
star
24

pry-test

A small test framework that supports debugging test failures & errors when they happen
Ruby
25
star
25

stimulus_toolbox

Stimulus Toolbox is a catalog of Stimulus projects tracked by popularity and other metrics to help you find the libraries you need to build better applications.
Ruby
22
star
26

credentials_demo

Demo of environment aware Rails encrypted credentials with environment variable override
Ruby
21
star
27

active_storage_svg_sanitizer

Sanitize ActiveStorage SVG uploads
Ruby
20
star
28

perm

Simple authorization/permission management in Ruby
Ruby
15
star
29

stimulus_todomvc

[WIP] An implementation of TodoMVC using Ruby on Rails and StimulusJS
Ruby
14
star
30

coin

An absurdly simple DRb based in-memory cache
Ruby
13
star
31

grumpy_old_man

Asserts for RSpec
Ruby
13
star
32

render_later

Improve the user perceived performance of your Rails app
Ruby
13
star
33

docker-ruby-rbx

Trusted Docker Image for Rubinius Ruby
Shell
11
star
34

trix_embed

Take control over what external links and embedded media is permitted in the Trix editor via copy/paste
JavaScript
10
star
35

ellington

A different take on flow based programming.
Ruby
10
star
36

todomvc

TodoMVC with Rails 7, StimulusReflex, & Import Maps
Ruby
9
star
37

state_jacket

A simple & intuitive state machine
Ruby
9
star
38

dotfiles

Lua
8
star
39

turbo_boost-devtools

Devtools for the Hotwire/Turbo ecosystem (TurboBoost, CableReady, StimulusReflex, etc.)
JavaScript
7
star
40

containers

Ruby
7
star
41

roleup

Simple role management
Ruby
6
star
42

legion

Parallel processing made easy
Ruby
6
star
43

kvn

KVN (Key/Value Notation) converter & parser
Ruby
4
star
44

apex-doc

Oracle Application Express (APEX) Quick Reference
4
star
45

bighorn

A standardized interface for event tracking
JavaScript
4
star
46

docker-images

Shell
4
star
47

private-docker-registry

A project to help you build a private docker registry image.
Shell
4
star
48

todo

WIP: CableReady + StimulusReflex + Web Components todo app demo
Ruby
4
star
49

foreign_key_migrations

Plugin that supports adding and removing foreign key constraints in your migrations.
Ruby
3
star
50

github_search

Ruby
3
star
51

docker-nodejs

Docker image for NodeJS
Shell
2
star
52

footing

An ActiveSupport style utility library that employs delegation instead of monkey patching
Ruby
2
star
53

coin_rack

A REST API for Coin
Ruby
2
star
54

fig

The smart way to manage config settings for Rails and Ruby applications using YAML configuration files.
Ruby
2
star
55

self_renderer

Rails model & object rendering outside the context of web requests
Ruby
1
star
56

docker-ruby-mri

Trusted Docker Image for MRI Ruby
Shell
1
star
57

model_definition_tester

Powerful schema and validations testing.
Ruby
1
star
58

string_extensions

Extensions and monkey patches for String.
Ruby
1
star
59

looker

Hash based enumerated types (ENUMS)
Ruby
1
star
60

cable_ready_todomvc

Rails implementation of TodoMVC using the CableReady GEM instead of a heavy SPA framework
Ruby
1
star
61

safe_migrations

Rails plugin that gracefully handles errors in data migrations and prevents you from getting stuck in between.
Ruby
1
star
62

hopsoft.github.io

Web pages for Hopsoft on Github.
JavaScript
1
star
63

active_record_addons

A small library of helper methods for ActiveRecord
Ruby
1
star
64

docker

Docker images for development and other environments
Dockerfile
1
star
65

hustle

Offload CPU heavy computing to separate processes with Ruby blocks.
Ruby
1
star
66

raml_doc

Generate API documentation from RAML files
HTML
1
star
67

stimulus_reflex_client

WIP: Will eventually replace the JavaScript in the StimulusReflex project
JavaScript
1
star
68

han

Hypermedia API Navigation spec
1
star
69

docker-postgres

Trusted Docker Image for PostgreSQL
Shell
1
star