-
Notifications
You must be signed in to change notification settings - Fork 109
Coding_Conventions
As our codebase grows and evolves, we need to be sure that features are still working as expected, and that new code does not introduce any breaks in the code.
Please do write tests for any new code you apply, and feel free to contribute any missing tests for existing code—we would appreciate the help,
Read our Testing conventions guide for more information.
We realize that every developer has their own style and we encourage a bit of individuality. However, we firmly believe that it's easier to work on code when everyone follows similar conventions and styles.
Aiming to maintain a consistent coding style throughout the codebase, we decided to use EditorConfig.
EditorConfig consists in a single file, include at the root of the project where rules are added to automaticaly maintain the coding style. Most of the IDEs have plugin or native support for EditorConfig. You can find the list of supported editors on the official website.
Some basic rules have been activated for most of the files in the project :
- Idents should use two spaces
- A new line is inserted at the end of each file.
Please do not submit code for review that has trailing whitespaces. This adds a lot of noise to git commits, and is wasteful.
Most text editors can be configured to remove trailing whitespace automatically.
If a line is going to go beyond 90 characters please break it out onto multiple lines.
Many developers work on narrower, vertical, or split screens. Keeping your code within 90 characters makes it easier to read, and to compare diffs using Git.
For example:
Bad
users = [{email: @user.email, password: 'bAd_pas$word1', remember_me: true}, {email: 'unknown@institution.org', password: 'password123', remember_me: true}]
Good
users = [{email: @user.email, password: 'bAd_pas$word1', remember_me: true},
{email: 'unknown@institution.org', password: 'password123', remember_me: true}]
We've installed Rubocop in the app with some sensible modifications made to the standard Rubocop style guide.
You can read through the standad Rubocop style guide here: http://docs.rubocop.org/en/latest/cops/
And you can view the rationales behind our own preferences, where different, here: https://github.com/dmproadmap/rubocop-dmp_roadmap/tree/master/config/
Please check your code against our style guide before you commit a new PR. You can run Rubocop via a bash shell via:
# The -p argument will run checks faster on devices with multi-core processors
$ rubocop -p
Most text editors can be configured to run Rubocop style checks as you write your code, if not you can install external plugins such as the [solargraph
] gem (https://github.com/castwide/solargraph).
You will probably notice a lot of existing code does not yet conform to our style guide. We're currently trying to move towards full complicance, but this is is not an immediate process.
Please assist us in this by running Rubocop checks on any files you've changed, and committing these as a separate commit.
The following executable will run Rubocop only on the files changed on your branch.
$ bin/rubocop_changed
If you'd like to understand the code better, you can generate HTML documentation for the current codebase by running the following rake task:
$ rake doc:app
This will create a directory filled with automatically generated HTML at ./doc
. Open the index file there to view the application documentation.
$ open ./doc/index.html
Please provide documentation for code you add by using Ruby comments. This is not essential in every instance, but encouraged—particularly when the code is complex.
Specifying the return values of each method is particularly useful, as it lets other developers better understand the contract of the Object they're working with.
Use the TomDoc syntax to document your code. This is the most lightweight and readable documentation syntax. It's better for you, and for us.
Documentation is generated using the YARD gem with the TomDoc plugin.
Add comments inside your methods where you feel the code isn't clear. Keep comments terse.
Bad
def sum_ids
# Pluck out all of the ids of associated records in an array
ids = records.pluck(:id)
ids.sum
end
Good
def sum_ids
# Array of all records ids.
ids = records.pluck(:id)
ids.sum
end
Add comments above the line(s) of code, not on the same line
Bad
def sum_ids
ids = records.pluck(:id) # Array of all records ids.
ids.sum
end
Good
def sum_ids
# Array of all records ids.
ids = records.pluck(:id)
ids.sum
end
When you change a piece of code, update the existing documentation where necessary. Please do not leave redundant documentation in this shared codebase.
If code is no longer necessary, please delete it. If needed, it can be retrieved from source control at a later date. Do not comment-out unwanted code.
Avoid writing HTML code in your views with complex conditionals or variable assignments.
Where possible, extract these instances out into meaningful helper methods.
Bad
<!-- users/show.html.erb -->
<h2>
<%- if @user.name? %>
<%= @user.name %>
<% else %>
Unknown user
<% end %>
</h2>
Good
# users_helper.rb
def header_for_user(user)
if @user.name?
@user.name
else
"Unknown user".freeze
end
end
<!-- users/show.html.erb -->
<h2><%= header_for_user(@user) %></h2>
When writing database migrations, ensure they can also be rolled back via the $ rake db:rollback
command. This is essential when working across various branches and pull-requests.
In the few cases that it's necessary to write your own custom SQL queries, please ensure your code works on both MySQL database and PostgreSQL.
You can test this by specifying the database adapter when running tests.
$ DB_ADAPTER=mysql2 rspec spec
$ DB_ADAPTER=postgresql rspec spec
When modifying the controller layer, keep controllers lean and focussed on handling a single resource. Bloated controllers increase the cost of maintaining a codebase.
When adding new actions and routes, consider whether you're defining a new domain concept or abstraction. A good hueristic to test this is to ask: Is this action one of the 7 standard RESTful actions? (index, new, create, show, edit, update, destroy). If not, the chances are you're defining a new domain concept and resource that warrants its own controller.
Bad
class PlansController < ApplicationController
def request_feedback
# ...
end
end
Good
class FeedbackRequests < ApplicationController
def create
@plan = Plan.find(params[:plan_id])
# ...
end
end
See Open-closed principle, below.
Observe SOLID design princples when writing code. These are tried and tested principles for writing reliable code that is easy to maintain over time.
If you haven't heard of SOLID before, or need a refresher, this talk by Sandi Metz is great: https://www.youtube.com/watch?v=v-2yFMzxqwU&frags=pl%2Cwn
Don't give objects too many jobs to do. This leads to tightly coupled code that's difficult to test and to change in future. Where possible, break objects into smaller objects that deal with discrete aspects of the overall domain.
Once your code has been written, it should not require modification unless to fix bugs. Strive to write code in a way that components are as atomic, extensible, and interchangable as possible.
Write your code in a way that any instance of a class may be substituted by any instance of one of its subclasses.
Write your code in such a way that objects are not cluttered with methods they will not directly use.
Bad
class User
attr_reader :name, :age, :admin_permissions
def initialize(**attributes)
@name = attributes[:name]
@age = attributes[:age]
@admin_permissions = attributes[:admin_permissions]
end
end
class Admin < User
end
class NonAdmin < User
end
Good
class User
attr_reader :name, :age
def initialize(**attributes)
@name = attributes[:name]
@age = attributes[:age]
end
end
# Only give the admin class the permissions, if that's the only place it's needed
class Admin < User
attr_reader :permissions
def initialize(**attributes)
super
@permissions = attributes[:permissions]
end
end
class NonAdmin < User
end
DRY (Don't Repeat Yourself). Please always look through the existing code to make sure you are not reinventing something that has already been done. Also, try to write code that's as reusable as possible and avoid copying and pasting is always appreciated.
The 12-Factor App tenets help us create an application that's easy for others to configure and deploy on a variety of servers.
- Home
- About
- Development roadmap
- Releases
- Themes
- Google Analytics
- Get involved
- Translations
- Developer guide