Last active
August 11, 2020 09:47
-
-
Save ecoologic/5537908 to your computer and use it in GitHub Desktop.
My Conventions
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{}.as_json # {} | |
{}.to_json # "{}" | |
# query / command | |
user_params # query, no get_params etc, prefer queries over commands, leads to more declarative code, which is simpler | |
remote_document # Query, not get_document, more declarative | |
buy_document! # Exception to the above, it's probably a POST and I want to express that it's not a free call | |
prepare_document! # Bang to express imperative behaviour (?) | |
compact_params(params) # command - takes args and can be reused | |
compacted_user_params # YES, a noun is better | |
compact_user_params # NO!! no need for a command, try to use a _query_ first | |
# Order | |
User.ordered # (adj) values: 'asc' 'desc' etc | |
user.position # 1,2,3 etc., not order, which is the "type" | |
# When it's two words in the dictionary, it should be two words in your code (eg: first_name) - Easier to remember | |
# File System naming should be consistent with Namespacing. eg: `/app/gateways/payment.rb` -> `Gateways::Payment` - Easier to remember | |
logo_file_name = logo_file_base_name = "logo.png" # As returned by File.basename | |
logo_file_root_name = 'logo' | |
logo_partial_path = logo_path = "public/logo.png" | |
logo_full_path = "/app/public/logo.path" | |
# Use the name of the abstraction - More modular, easier to change | |
Gateways::Payment # Instead of Gateways::Paypal | |
PaymentGateway # In Rails, to keep conventions | |
Paypal::Gateway # Other way around, because paypal is the wrapper module | |
# Call because it's a "doer" class (so does one thing only) - easier polymorphism | |
user = UserBuilder.new(attributes).call | |
# user not as a keyword arg because the name makes it obvious | |
presenter = UserPresenter.new(user) | |
# Use keyword arguments when the param is not in the name of the class | |
user = Users::Services::Creator.new(user, params[:user], { skip_children: true }).call | |
def a_private_method(dont, use, keyword, arguments) # because it's shorter and it's local | |
end | |
# Pass primitives, never AR models, test using spec_helper, not rails_helper | |
PaymentGateway.new(credit_card_info: credit_card_params, amount_in_cents: 50).pay! | |
# info/data usually hashes | |
validate :valid_field # prefix valid_ for validations | |
before_save :sync_xx_fields # sync is excellent for callbacks | |
before_create :set_xx_fields # set OK here | |
class UserService | |
# Params are mandatory | |
def initialize(user, params, options = {}) | |
@user, @params, @options = user, params, options | |
end | |
def save | |
@user.save! # as a default, unless you check if save was successful | |
# [do stuff] | |
# return true / false - match AR behavior | |
end | |
private | |
# Private readers, set with `@user = u`, not attr_writers | |
attr_reader :user, :params, :options | |
end | |
def hashify_url(url:) # NOOOO - Redundant | |
def hashify(url:) # Maybe: Good naming, but beware of SRP | |
def hashify_url(url) # YES: Separates the logic nicely | |
def convert_user(user:, options: {}) # NOO: Both these types are implied | |
def convert_user(user, options = {}) # Nice | |
def convert_user(user, options) # Defaults are not hints of their type, use them when not passing a value is accepted | |
end end end end end end | |
# Always pass the date down to the model (easier to test / debug), SRP | |
def active_on?(date) | |
# ... true or false, maybe nil, nothing else | |
end | |
# Don't care what is the subject of your comparision, | |
# Create an expectation that time always increase from left to right | |
# TODO: starts_on ends_on | |
before_date < after_date # (always use < when comparing dates, so it's like a temporal line left to right) | |
before_date < (after_date + offset) | |
(before_date + offset) < after_date # Never use minus, always plus, simpler to read left to right | |
date # -> 23/1/16 (for arguments and local vars, not for DB fields) | |
day # -> cardinal: 1,2,3 | |
purchase.made_on # instead of purchase.date | |
survey.completed_at # instead of survey.time | |
# Field name conventions by type: | |
t.date :due_on # and not due_date | |
t.datetime :due_at # and not due_time | |
t.integer :xxx_id # use id only for models | |
t.string :x_api_uid # use uid for non-AR IDs | |
VALUES_BY_KEY = { key1: 'value1', key2: 'value2' } | |
def product_uid; end # product_id is a typical Rails thing, use uid for external services | |
def product_token; end # _key, _code a bit ambiguous | |
def x_array; end # NO: avoid Hungarian Notation, give it a name and pluralize it | |
def xs; end # OK | |
# A period is a range of time | |
def active_period | |
(starts_on..ends_on) | |
end | |
def firstname; end # NO: Two words in the dictionary, two words in the code | |
def first_name; end # OK | |
def req; end # NO: params / attrs is accepted | |
def request; end # OK | |
# Use result for the returning values in procedures | |
def complex_method | |
result = [] | |
result = complex_stuff | |
result | |
end | |
# Use the same name for memoization | |
def things | |
@things ||= calc_things | |
end | |
# Casting | |
def to_y; end | |
# Actual call to external service | |
def get_user; end # same length (: | |
def set_user; end | |
def buy_plan; end | |
def request_user; end # fetch, load | |
def store_user; end # TODO?? better? | |
# AVOID | |
check_xxx # not clear | |
get_xxx # | |
# Gemfile: Explain why you're locking a version (in the commit message too) | |
gem 'my_gem', '1.0.0' # Locked because v 1.0.0 introduced breaking change with xyz | |
# Rails | |
def price_incl_tax # NOT price_inc_gst, incl is easier to search and GST could be too specific and regional | |
end | |
# Put a link to the PR in the main file of a new feature |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment