├── style ├── samples │ ├── erb.rb │ ├── testing.rb │ ├── acceptance_test.rb │ ├── sass.scss │ ├── ruby.rb │ └── ObjectiveC.m └── README.md ├── README.md ├── code-review └── README.md ├── protocol └── README.md └── best-practices └── README.md /style/samples/erb.rb: -------------------------------------------------------------------------------- 1 | <%= short_method_call_that_fits_on_one_line arguments %> 2 | 3 | <%= link_to( 4 | some_object_with_a_long_name.title, 5 | parent_object_child_object_path(some_object_with_a_long_name) 6 | ) %> 7 | -------------------------------------------------------------------------------- /style/samples/testing.rb: -------------------------------------------------------------------------------- 1 | describe SomeClass, '#some_method' do 2 | it 'does something' do 3 | expect(something).to eq 'something' 4 | end 5 | end 6 | 7 | describe SomeClass, '#other_method' do 8 | it 'does something in one case' do 9 | end 10 | 11 | it 'does something else in other cases' do 12 | end 13 | 14 | # methods go here 15 | end 16 | -------------------------------------------------------------------------------- /style/samples/acceptance_test.rb: -------------------------------------------------------------------------------- 1 | # spec/features/user_signing_up_spec.rb 2 | require 'spec_helper' 3 | 4 | feature 'User signing up' do 5 | scenario 'with valid email and password' do 6 | visit sign_up_path 7 | 8 | within '#sign_up' do 9 | fill_in 'Email', with: 'user@example.com' 10 | fill_in 'Password', with: 'Examp1ePa$$' 11 | click_button 'Sign up' 12 | end 13 | 14 | expect(page).to have_content('Sign out') 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /style/samples/sass.scss: -------------------------------------------------------------------------------- 1 | section.application { 2 | @extend %extend; 3 | @include mixin; 4 | attribute: value; 5 | 6 | @include media(value) { 7 | attribute: value; 8 | } 9 | 10 | &.additional-selector { 11 | attribute: value; 12 | } 13 | 14 | &:hover { 15 | attribute: value; 16 | } 17 | 18 | .news { 19 | attribute: value; 20 | 21 | article { 22 | 23 | @extend media(value) { 24 | attribute: value; 25 | } 26 | 27 | p { 28 | attribute: value; 29 | } 30 | } 31 | } 32 | 33 | .sidebar { 34 | attribute: value; 35 | 36 | h2 { 37 | attribute: value; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Guides 2 | ====== 3 | 4 | Guides for getting things done, programming well, and programming in style. 5 | 6 | * [Protocol](/protocol) 7 | * [Code Review](/code-review) 8 | * [Best Practices](/best-practices) 9 | * [Style](/style) 10 | 11 | High level guidelines: 12 | 13 | * Be consistent. 14 | * Don't rewrite existing code to follow this guide. 15 | * Don't violate a guideline without a good reason. 16 | * A reason is good when you can convince a teammate. 17 | 18 | A note on the language: 19 | 20 | * "Avoid" means don't do it unless you have good reason. 21 | * "Don't" means there's never a good reason. 22 | * "Prefer" indicates a better option and its alternative to watch out for. 23 | * "Use" is a positive instruction. 24 | 25 | Credits 26 | ------- 27 | 28 | Thank you, [contributors](https://github.com/thoughtbot/guides/graphs/contributors)! 29 | 30 | ![thoughtbot](http://thoughtbot.com/images/tm/logo.png) 31 | 32 | Guides is maintained by [thoughtbot, inc](http://thoughtbot.com/community). 33 | 34 | License 35 | ------- 36 | 37 | Guides is © 2013 thoughtbot, inc. It is distributed under the [Creative Commons 38 | Attribution License](http://creativecommons.org/licenses/by/3.0/). 39 | 40 | The names and logos for thoughtbot are trademarks of thoughtbot, inc. 41 | -------------------------------------------------------------------------------- /style/samples/ruby.rb: -------------------------------------------------------------------------------- 1 | class SomeClass 2 | SOME_CONSTANT = 'upper case name' 3 | 4 | def initialize(attributes) 5 | @some_attribute = attributes[:some_attribute] 6 | @another_attribute = attributes[:another_attribute] 7 | @user_factory = attributes[:user_factory] 8 | end 9 | 10 | def method_with_arguments(argument_one, argument_two) 11 | this_is_a_really_long_line_that_should_be_broken_up_over_multiple_lines_and. 12 | every_line_but_the_first_is_indented 13 | end 14 | 15 | def method_with_multiline_block 16 | some_method_before_block(should_be_followed_by_a_newline) 17 | 18 | items.each do |item| 19 | do_something_with_item 20 | end 21 | 22 | some_method_after_block(should_follow_after_newline) 23 | end 24 | 25 | def method_with_single_line_block 26 | items.map { |item| item.some_attribute } 27 | end 28 | 29 | def method_that_returns_an_array 30 | [item_one, item_two] 31 | end 32 | 33 | def method_that_returns_a_hash 34 | { :key => 'value' } 35 | end 36 | 37 | def method_with_large_hash 38 | { 39 | :one => 'value', 40 | :two => 'value' 41 | } 42 | end 43 | 44 | def invoke_method_with_arguments_on_multiple_lines 45 | some_method( 46 | i_am_a_long_variable_name_that_i_will_never_fit_on_one_line_with_others, 47 | two, 48 | three 49 | ) 50 | 51 | # Bad: 52 | some_method(one, 53 | two) 54 | end 55 | 56 | def method_that_uses_infix_operators 57 | left + middle - right 58 | end 59 | 60 | def method_without_arguments 61 | if complex_condition? 62 | positive_branch 63 | else 64 | negative_branch 65 | end 66 | 67 | rest_of_body 68 | end 69 | 70 | def method_that_uses_factory 71 | user = @user_factory.new 72 | user.ensure_authenticated! 73 | end 74 | 75 | def self.class_method 76 | method_body 77 | end 78 | 79 | private 80 | 81 | def complex_condition? 82 | part_one? && part_two? 83 | end 84 | end 85 | -------------------------------------------------------------------------------- /style/samples/ObjectiveC.m: -------------------------------------------------------------------------------- 1 | #import "Alpha.h" 2 | #import "Beta.h" 3 | 4 | // Use @interface extensions for private properties 5 | @interface ClassName () 6 | 7 | // Keep @properties grouped together by function 8 | @property (strong, nonatomic) IBOutlet UISearchBar *searchBar; 9 | @property (strong, nonatomic) IBOutlet UITableView *tableView; 10 | 11 | @property (strong, nonatomic) NSManagedObjectContext *managedObjectContext; 12 | @property (strong, nonatomic) NSFetchedResultsController *fetchedResultsController; 13 | 14 | @property (strong, nonatomic, readonly) TBObject *someObject; 15 | 16 | @end 17 | 18 | // Use static NSString points to consts for string constants 19 | static NSString *const ConstantName = @"Constant"; 20 | 21 | // Prepend constants with 'k' when being used as keys 22 | static NSString *const kFirstName = @"FirstName"; 23 | 24 | @implementation ClassName 25 | 26 | /* 27 | - Use #pragma mark to organize code by function 28 | - Use descriptive names for #pragma mark 29 | - Use class names if overriding or implementing protocol methods 30 | */ 31 | #pragma mark - Initialization 32 | 33 | - (id)initWithCoder:(NSCoder *)aDecoder 34 | { 35 | self = [super initWithCoder:aDecoder]; 36 | 37 | // Return early if conditions prohibit the intended function of the method 38 | // Use conditionals for exceptional cases 39 | // Keep the 'optimal' path non-indented 40 | if (!self) 41 | return nil; 42 | 43 | return self; 44 | } 45 | 46 | #pragma mark - UI 47 | 48 | // Opening brackets belong on the next line 49 | - (void)shuffleCards 50 | { 51 | // Objective-C literals are your friend 52 | NSDictionary *themeColors = @{ kRedColor : [UIColor redColor], kBlueColor : [UIColor blueColor] }; 53 | NSArray *robots = @[ @"Ralph", @"Bender", @"The Iron Giant" ]; 54 | 55 | NSMutableArray *deckOfCards = [NSMutableArray arrayWithCapacity:52]; 56 | 57 | // Newlines before and after conditional blocks 58 | for (Card *card in deckOfCards) 59 | NSLog(@"%@", [card description]); 60 | 61 | Card *jokerCard = [Card joker]; 62 | [deckOfCards addObject:jokerCard]; 63 | 64 | // Use ! to check for nots. Comparing to 'nil' is redundant 65 | if (![creditCard isValid]) 66 | { 67 | //... 68 | } 69 | } 70 | 71 | @end 72 | -------------------------------------------------------------------------------- /code-review/README.md: -------------------------------------------------------------------------------- 1 | Code Review 2 | =========== 3 | 4 | A guide for reviewing code and having your code reviewed. 5 | 6 | Everyone 7 | -------- 8 | 9 | * Accept that many programming decisions are opinions. Discuss tradeoffs, which 10 | you prefer, and reach a resolution quickly. 11 | * Ask questions; don't make demands. ("What do you think about naming this 12 | `:user_id`?") 13 | * Ask for clarification. ("I didn't understand. Can you clarify?") 14 | * Avoid selective ownership of code. ("mine", "not mine", "yours") 15 | * Avoid using terms that could be seen as referring to personal traits. ("dumb", 16 | "stupid") Assume everyone is attractive, intelligent, and well-meaning. 17 | * Be explicit. Remember people don't always understand your intentions online. 18 | * Be humble. ("I'm not sure - let's look it up.") 19 | * Don't use hyperbole. ("always", "never", "endlessly", "nothing") 20 | * Don't use sarcasm. 21 | * Keep it real. If emoji, animated gifs, or humor aren't you, don't force them. 22 | If they are, use them with aplomb. 23 | * Talk in person if there are too many "I didn't understand" or "Alternative 24 | solution:" comments. Post a follow-up comment summarizing offline discussion. 25 | 26 | Having your code reviewed 27 | ------------------------- 28 | 29 | * Be grateful for the reviewer's suggestions. ("Good call. I'll make that 30 | change.") 31 | * Don't take it personally. The review is of the code, not you. 32 | * Explain why the code exists. ("It's like that because of these reasons. Would 33 | it be more clear if I rename this class/file/method/variable?") 34 | * Extract some changes and refactorings into future tickets/stories. 35 | * Link to the code review from the ticket/story. ("Ready for review: 36 | https://github.com/organization/project/pull/1") 37 | * Push commits based on earlier rounds of feedback as isolated commits to the 38 | branch. Do not squash until the branch is ready to merge. Reviewers should be 39 | able to read individual updates based on their earlier feedback. 40 | * Seek to understand the reviewer's perspective. 41 | * Try to respond to every comment. 42 | * Wait to merge the branch until another person signs off. 43 | * Wait to merge the branch until Continuous Integration (TDDium, TravisCI, etc.) 44 | tells you the test suite is green in the branch. 45 | 46 | Reviewing code 47 | -------------- 48 | 49 | Understand why the code is necessary (bug, user experience, refactoring). Then: 50 | 51 | * Communicate which ideas you feel strongly about and those you don't. 52 | * Identify ways to simplify the code while still solving the problem. 53 | * If discussions turn too philosophical or academic, move the discussion offline 54 | to a regular Friday afternoon technique discussion. In the meantime, let the 55 | author make the final decision on alternative implementations. 56 | * Offer alternative implementations, but assume the author already considered 57 | them. ("What do you think about using a custom validator here?") 58 | * Seek to understand the author's perspective. 59 | * Sign off on the pull request with a :thumbsup: or "Ready to merge" comment. 60 | 61 | Style comments 62 | -------------- 63 | 64 | Reviewers should comment on missed [style](../style) 65 | guidelines. Example comment: 66 | 67 | [Style](../style): 68 | 69 | > Order resourceful routes alphabetically by name. 70 | 71 | An example response to style comments: 72 | 73 | Whoops. Good catch, thanks. Fixed in a4994ec. 74 | 75 | If you disagree with a guideline, open an issue on the guides repo rather than 76 | debating it within the code review. In the meantime, apply the guideline. 77 | 78 | Ruby on Rails review 79 | -------------------- 80 | 81 | * Review data integrity closely, such as migrations that make irreversible 82 | changes to the data, and whether there is a related todo to make a database 83 | backup during the staging and production deploys. 84 | * Review SQL queries for potential SQL injection. 85 | * Review whether dependency upgrades include a reason in the commit message, 86 | such as a link to the dependency's `ChangeLog` or `NEWS` file. 87 | * Review whether new database indexes are necessary if new columns or SQL 88 | queries were added. 89 | * Review whether new scheduler (`cron`) tasks have been added and whether there 90 | is a related todo in the project management system to add it during the 91 | staging and production deploys. 92 | -------------------------------------------------------------------------------- /protocol/README.md: -------------------------------------------------------------------------------- 1 | Protocol 2 | ======== 3 | 4 | A guide for getting things done. 5 | 6 | Set up laptop 7 | ------------- 8 | 9 | Install the latest version of Xcode from the App Store. 10 | 11 | Set up your laptop with [this script](https://github.com/thoughtbot/laptop) 12 | and [these dotfiles](https://github.com/thoughtbot/dotfiles). 13 | 14 | Create Rails app 15 | ---------------- 16 | 17 | Get Suspenders. 18 | 19 | gem install suspenders 20 | 21 | Create the app. 22 | 23 | suspenders app --heroku true --github organization/app 24 | 25 | Create iOS app 26 | -------------- 27 | 28 | Create a new project in Xcode with these settings: 29 | 30 | * Check 'Create local git repository for this project'. 31 | * Check 'Use Automatic Reference Counting'. 32 | * Set an appropriate 2 or 3 letter class prefix. 33 | * Set the Base SDK to 'Latest iOS'. 34 | * Set the iOS Deployment Target to 6.0. 35 | * Use the Apple LLVM compiler. 36 | 37 | Get liftoff. 38 | 39 | gem install liftoff 40 | 41 | Run liftoff in the project directory. 42 | 43 | liftoff 44 | 45 | Set up Rails app 46 | ---------------- 47 | 48 | Get the code. 49 | 50 | git clone git@github.com:organization/app.git 51 | 52 | Set up the app's dependencies. 53 | 54 | cd project 55 | ./bin/setup 56 | 57 | Use [Heroku config](https://github.com/ddollar/heroku-config) to get `ENV` 58 | variables. 59 | 60 | heroku config:pull --remote staging 61 | 62 | Delete extra lines in `.env`, leaving only those needed for app to function 63 | properly. For example: `BRAINTREE_MERCHANT_ID` and `S3_SECRET`. 64 | 65 | Use [Foreman](https://github.com/ddollar/foreman) to run the app locally. 66 | 67 | foreman start 68 | 69 | It uses your `.env` file and `Procfile` to run processes just like Heroku's 70 | [Cedar](https://devcenter.heroku.com/articles/cedar/) stack. 71 | 72 | Maintain a Rails app 73 | -------------------- 74 | 75 | * Avoid including files in source control that are specific to your 76 | development machine or process. 77 | * Delete local and remote feature branches after merging. 78 | * Perform work in a feature branch. 79 | * Rebase frequently to incorporate upstream changes. 80 | * Use a [pull request] for code reviews. 81 | 82 | [pull request]: https://help.github.com/articles/using-pull-requests/ 83 | 84 | Write a feature 85 | --------------- 86 | 87 | Create a local feature branch based off master. 88 | 89 | git checkout master 90 | git pull 91 | git checkout -b 92 | 93 | Prefix the branch name with your initials. 94 | 95 | Rebase frequently to incorporate upstream changes. 96 | 97 | git fetch origin 98 | git rebase origin/master 99 | 100 | Resolve conflicts. When feature is complete and tests pass, stage the changes. 101 | 102 | rake 103 | git add --all 104 | 105 | When you've staged the changes, commit them. 106 | 107 | git status 108 | git commit --verbose 109 | 110 | Write a [good commit message]. Example format: 111 | 112 | Present-tense summary under 50 characters 113 | 114 | * More information about commit (under 72 characters). 115 | * More information about commit (under 72 characters). 116 | 117 | http:://project.management-system.com/ticket/123 118 | 119 | Share your branch. 120 | 121 | git push origin 122 | 123 | Submit a [GitHub pull request]. 124 | 125 | Ask for a code review in [Campfire](https://campfirenow.com/). 126 | 127 | [good commit message]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html 128 | [GitHub pull request]: https://help.github.com/articles/using-pull-requests/ 129 | 130 | Review code 131 | ----------- 132 | 133 | A team member other than the author reviews the pull request. They follow 134 | [Code Review](../code-review) guidelines to avoid 135 | miscommunication. 136 | 137 | They make comments and ask questions directly on lines of code in the Github 138 | web interface or in Campfire. 139 | 140 | For changes which they can make themselves, they check out the branch. 141 | 142 | git checkout 143 | rake db:migrate 144 | rake 145 | git diff staging/master..HEAD 146 | 147 | They make small changes right in the branch, test the feature in browser, 148 | run tests, commit, and push. 149 | 150 | When satisfied, they comment on the pull request `Ready to merge.` 151 | 152 | Merge 153 | ----- 154 | 155 | Rebase interactively. Squash commits like "Fix whitespace" into one or a 156 | small number of valuable commit(s). Edit commit messages to reveal intent. 157 | 158 | git fetch origin 159 | git rebase -i origin/master 160 | rake 161 | 162 | View a list of new commits. View changed files. Merge branch into master. 163 | 164 | git log origin/master.. 165 | git diff --stat origin/master 166 | git checkout master 167 | git merge --ff-only 168 | git push 169 | 170 | Delete your remote feature branch. 171 | 172 | git push origin --delete 173 | 174 | Delete your local feature branch. 175 | 176 | git branch --delete 177 | 178 | Deploy 179 | ------ 180 | 181 | View a list of new commits. View changed files. Deploy to 182 | [Heroku](https://devcenter.heroku.com/articles/quickstart) staging. 183 | 184 | git fetch staging 185 | git log staging/master..master 186 | git diff --stat staging/master 187 | git push staging 188 | 189 | If necessary, run migrations and restart the dynos. 190 | 191 | heroku run rake db:migrate --remote staging 192 | heroku restart --remote staging 193 | 194 | [Introspect] to make sure everything's ok. 195 | 196 | watch heroku ps --remote staging 197 | 198 | Test the feature in browser. 199 | 200 | Deploy to production. 201 | 202 | git fetch production 203 | git log production/master..master 204 | git diff --stat production/master 205 | git push production 206 | heroku run rake db:migrate --remote production 207 | heroku restart --remote production 208 | watch heroku ps --remote production 209 | 210 | Watch logs and metrics dashboards. 211 | 212 | Close pull request and comment `Merged.` 213 | 214 | [Introspect]: http://blog.heroku.com/archives/2011/6/24/the_new_heroku_3_visibility_introspection/ 215 | 216 | Set Up Production Environment 217 | ----------------------------- 218 | 219 | * Make sure that your [`Procfile`] is set up to run Unicorn. 220 | * Make sure the PG Backups add-on is enabled. 221 | * Create a read-only [Heroku Follower] for your production database. If a Heroku 222 | database outage occurs, Heroku can use the follower to get your app back up 223 | and running faster. 224 | 225 | [Heroku Follower]: https://devcenter.heroku.com/articles/improving-heroku-postgres-availability-with-followers 226 | [`Procfile`]: https://devcenter.heroku.com/articles/procfile 227 | -------------------------------------------------------------------------------- /best-practices/README.md: -------------------------------------------------------------------------------- 1 | Best Practices 2 | ============== 3 | 4 | A guide for programming well. 5 | 6 | General 7 | ------- 8 | 9 | * Don't duplicate the functionality of a built-in library. 10 | * Don't swallow exceptions or "fail silently." 11 | * Don't write code that guesses at future functionality. 12 | * [Exceptions should be exceptional]. 13 | * [Keep the code simple]. 14 | 15 | [Exceptions should be exceptional]: http://www.readability.com/~/yichhgvu 16 | [Keep the code simple]: http://www.readability.com/~/ko2aqda2 17 | 18 | Object-Oriented Design 19 | ---------------------- 20 | 21 | * Avoid global variables. 22 | * Avoid long parameter lists. 23 | * Limit collaborators of an object (entities an object depends on). 24 | * Limit an object's dependencies (entities that depend on an object). 25 | * Prefer composition over inheritance. 26 | * Prefer small methods. Between one and five lines is best. 27 | * Prefer small objects with a single, well-defined responsibility. When an 28 | object exceeds 100 lines, it may be doing too many things. 29 | * [Tell, don't ask]. 30 | 31 | [Tell, don't ask]: http://robots.thoughtbot.com/post/27572137956/tell-dont-ask 32 | 33 | Ruby 34 | ---- 35 | 36 | * Avoid optional parameters. Does the method do too much? 37 | * Avoid monkey-patching. 38 | * Prefer classes to modules when designing functionality that is shared by 39 | multiple models. 40 | * Prefer `private` when indicating scope. Use `protected` only with comparison 41 | methods like `def ==(other)`, `def <(other)`, and `def >(other)`. 42 | 43 | Ruby Gems 44 | --------- 45 | 46 | * Declare dependencies in the `.gemspec` file. 47 | * Reference the `gemspec` in the `Gemfile`. 48 | * Use [Appraisal] to test the gem against multiple versions of gem dependencies 49 | (such as Rails in a Rails engine). 50 | * Use [Bundler] to manage the gem's dependencies. 51 | * Use [Travis CI] for Continuous Integration, indicators showing whether GitHub 52 | pull requests can be merged, and to test against multiple Ruby versions. 53 | 54 | [Appraisal]: https://github.com/thoughtbot/appraisal 55 | [Bundler]: http://bundler.io 56 | [Travis CI]: http://travis-ci.org 57 | 58 | Rails 59 | ----- 60 | 61 | * Avoid bypassing validations with methods like `save(validate: false)`, 62 | `update_attribute`, and `toggle`. 63 | * Avoid instantiating more than one object in controllers. 64 | * Avoid naming methods after database columns in the same class. 65 | * Don't change a migration after it has been merged into master if the desired 66 | change can be solved with another migration. 67 | * Don't reference a model class directly from a view. 68 | * Don't use instance variables in partials. Pass local variables to partials 69 | from view templates. 70 | * Don't use SQL or SQL fragments (`where('inviter_id IS NOT NULL')`) outside of 71 | models. 72 | * If there are default values, set them in migrations. 73 | * Keep `db/schema.rb` or `db/development_structure.sql` under version control. 74 | * Use only one instance variable in each view. 75 | * Use SQL, not `ActiveRecord` models, in migrations. 76 | * Use the [`.ruby-version`] file convention to specify the Ruby version and 77 | patch level for a project. 78 | * Use `_url` suffixes for named routes in mailer views and [redirects]. Use 79 | `_path` suffixes for named routes everywhere else. 80 | * Validate the associated `belongs_to` object (`user`), not the database column 81 | (`user_id`). 82 | * Use `db/seeds.rb` for data that is required in all environments. 83 | * Use `dev:prime` rake task for development environment seed data. 84 | 85 | [`.ruby-version`]: https://gist.github.com/fnichol/1912050 86 | [redirects]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.30 87 | 88 | Testing 89 | ------- 90 | 91 | * Avoid `any_instance` in rspec-mocks and mocha. Prefer [dependency injection]. 92 | * Avoid `its`, `let`, `let!`, `specify`, `before`, and `subject` in RSpec. 93 | * Avoid using instance variables in tests. 94 | * Disable real HTTP requests to external services with 95 | `WebMock.disable_net_connect!`. 96 | * Don't test private methods. 97 | * Test background jobs with a [`Delayed::Job` matcher]. 98 | * Use [stubs and spies] \(not mocks\) in isolated tests. 99 | * Use a single level of abstraction within scenarios. 100 | * Use an `it` example or test method for each execution path through the method. 101 | * Use [assertions about state] for incoming messages. 102 | * Use stubs and spies to assert you sent outgoing messages. 103 | * Use a [Fake] to stub requests to external services. 104 | * Use integration tests to execute the entire app. 105 | * Use non-[SUT] methods in expectations when possible. 106 | 107 | [dependency injection]: http://en.wikipedia.org/wiki/Dependency_injection 108 | [`Delayed::Job` matcher]: https://gist.github.com/3186463 109 | [stubs and spies]: http://robots.thoughtbot.com/post/159805295/spy-vs-spy 110 | [assertions about state]: https://speakerdeck.com/skmetz/magic-tricks-of-testing-railsconf?slide=51 111 | [Fake]: http://robots.thoughtbot.com/post/219216005/fake-it 112 | [SUT]: http://xunitpatterns.com/SUT.html 113 | 114 | Bundler 115 | ------- 116 | 117 | * Specify the [Ruby version] to be used on the project in the `Gemfile`. 118 | * Use a [pessimistic version] in the `Gemfile` for gems that follow semantic 119 | versioning, such as `rspec`, `factory_girl`, and `capybara`. 120 | * Use a [versionless] `Gemfile` declarations for gems that are safe to update 121 | often, such as pg, thin, and debugger. 122 | * Use an [exact version] in the `Gemfile` for fragile gems, such as Rails. 123 | 124 | [Ruby version]: http://bundler.io/v1.3/gemfile_ruby.html 125 | [exact version]: http://robots.thoughtbot.com/post/35717411108/a-healthy-bundle 126 | [pessimistic version]: http://robots.thoughtbot.com/post/35717411108/a-healthy-bundle 127 | [versionless]: http://robots.thoughtbot.com/post/35717411108/a-healthy-bundle 128 | 129 | Postgres 130 | -------- 131 | 132 | * Avoid multicolumn indexes in Postgres. It [combines multiple indexes] 133 | efficiently. Optimize later with a [compound index] if needed. 134 | * Consider a [partial index] for queries on booleans. 135 | * Constrain most columns as [`NOT NULL`]. 136 | * [Index foreign keys]. 137 | 138 | [`NOT NULL`]: http://www.postgresql.org/docs/9.1/static/ddl-constraints.html#AEN2444 139 | [combines multiple indexes]: http://www.postgresql.org/docs/9.1/static/indexes-bitmap-scans.html 140 | [compound index]: http://www.postgresql.org/docs/9.2/static/indexes-bitmap-scans.html 141 | [partial index]: http://www.postgresql.org/docs/9.1/static/indexes-partial.html 142 | [Index foreign keys]: https://tomafro.net/2009/08/using-indexes-in-rails-index-your-associations 143 | 144 | Background Jobs 145 | --------------- 146 | 147 | * Store IDs, not `ActiveRecord` objects for cleaner serialization, then re-find 148 | the `ActiveRecord` object in the `perform` method. 149 | 150 | Email 151 | ----- 152 | 153 | * Use [SendGrid] or [Amazon SES] to deliver email in staging and production 154 | environments. 155 | * Use a tool like [MailView] to look at each created or updated mailer view 156 | before merging. 157 | 158 | [Amazon SES]: http://robots.thoughtbot.com/post/3105121049/delivering-email-with-amazon-ses-in-a-rails-3-app 159 | [SendGrid]: https://devcenter.heroku.com/articles/sendgrid 160 | [MailView]: https://github.com/37signals/mail_view 161 | 162 | JavaScript 163 | ---------- 164 | 165 | * Use CoffeeScript. 166 | 167 | HTML 168 | ---- 169 | 170 | * Don't use a reset button for forms. 171 | * Prefer cancel links to cancel buttons. 172 | 173 | CSS 174 | --- 175 | 176 | * Use Sass. 177 | 178 | Sass 179 | ---- 180 | 181 | * Use `image-url` and `font-url`, not `url`, so the asset pipeline will re-write 182 | the correct paths to assets. 183 | 184 | Browsers 185 | -------- 186 | 187 | * Don't support clients without Javascript. 188 | * Don't support IE6 or IE7. 189 | 190 | Objective-C 191 | ----------- 192 | 193 | * Prefer categories on `Foundation` classes to helper methods. 194 | * Prefer string constants to literals when providing keys or key paths to methods. 195 | 196 | Shell 197 | ----- 198 | 199 | * Don't parse the output of `ls`. See [here][parsingls] for details and 200 | alternatives. 201 | * Don't use `cat` to provide a file on `stdin` to a process that accepts 202 | file arguments itself. 203 | * Don't use a `/bin/sh` [shebang][] unless you plan to test and run your 204 | script on at least: Actual Sh, Dash in POSIX-compatible mode (as it 205 | will be run on Debian), and Bash in POSIX-compatible mode (as it will 206 | be run on OSX). 207 | * Don't use any non-POSIX [features][bashisms] when using a `/bin/sh` 208 | [shebang][]. 209 | * If calling `cd`, have code to handle a failure to change directories. 210 | * If calling `rm` with a variable, ensure the variable is not empty. 211 | * Prefer "$@" over "$\*" unless you know exactly what you're doing. 212 | * Prefer `awk '/re/ { ... }'` to `grep re | awk '{ ... }'`. 213 | * Prefer `find -exec {} +` to `find -print0 | xargs -0`. 214 | * Prefer `for` loops over `while read` loops. 215 | * Prefer `grep -c` to `grep | wc -l`. 216 | * Prefer `mktemp` over using `$$` to "uniquely" name a temporary file. 217 | * Prefer `printf` over `echo`. 218 | * Prefer `sed '/re/!d; s//.../'` to `grep re | sed 's/re/.../'`. 219 | * Prefer `sed 'cmd; cmd'` to `sed -e 'cmd' -e 'cmd'`. 220 | * Prefer checking exit statuses over output in `if` statements (`if grep 221 | -q ...; `, not `if [ -n "$(grep ...)" ];`). 222 | * Prefer reading environment variables over process output (`$TTY` not 223 | `$(tty)`, `$PWD` not `$(pwd)`, etc). 224 | * Use `$( ... )`, not backticks for capturing command output. 225 | * Use `$(( ... ))`, not `expr` for executing arithmetic expressions. 226 | * Use `1` and `0`, not `true` and `false` to represent boolean 227 | variables. 228 | * Use `find -print0 | xargs -0`, not `find | xargs`. 229 | * Use quotes around every `"$variable"` and `"$( ... )"` expression 230 | unless you want them to be word-split and/or interpreted as globs. 231 | * Use the `local` keyword with function-scoped variables. 232 | * Identify common problems with [shellcheck][]. 233 | 234 | [shebang]: http://en.wikipedia.org/wiki/Shebang_(Unix) 235 | [parsingls]: http://mywiki.wooledge.org/ParsingLs 236 | [bashisms]: http://mywiki.wooledge.org/Bashism 237 | [shellcheck]: http://www.shellcheck.net/ 238 | 239 | Bash 240 | ---- 241 | 242 | In addition to Shell best practices, 243 | 244 | * Prefer `${var,,}` and `${var^^}` over `tr` for changing case. 245 | * Prefer `${var//from/to}` over `sed` for simple string replacements. 246 | * Prefer `[[` over `test` or `[`. 247 | * Prefer process substitution over a pipe in `while read` loops. 248 | * Use `((` or `let`, not `$((` when you don't need the result 249 | -------------------------------------------------------------------------------- /style/README.md: -------------------------------------------------------------------------------- 1 | Style 2 | ===== 3 | 4 | A guide for programming in style. 5 | 6 | Git 7 | --- 8 | 9 | * Avoid merge commits by using a [rebase workflow]. 10 | * Prefix feature branch names with your initials. 11 | * Squash multiple trivial commits into a single commit. 12 | * Write a [good commit message]. 13 | 14 | [rebase workflow]: https://github.com/thoughtbot/guides/tree/master/protocol#merge 15 | [good commit message]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html 16 | 17 | Formatting 18 | ---------- 19 | 20 | * Avoid inline comments. 21 | * Break long lines after 80 characters. 22 | * Delete trailing whitespace. 23 | * Don't include spaces after `(`, `[` or before `]`, `)`. 24 | * Don't misspell. 25 | * Don't vertically align tokens on consecutive lines. 26 | * If you break up an argument list, keep the arguments on their own lines and 27 | closing parenthesis on its own line. 28 | * If you break up a hash, keep the elements on their own lines and closing curly 29 | brace on its own line. 30 | * Indent continued lines two spaces. 31 | * Indent private methods equal to public methods. 32 | * Use 2 space indentation (no tabs). 33 | * Use an empty line between methods. 34 | * Use newlines around multi-line blocks. 35 | * Use spaces around operators, after commas, after colons and semicolons, around 36 | `{` and before `}`. 37 | * Use [Unix-style line endings] (`\n`). 38 | * Use [uppercase for SQL key words and lowercase for SQL identifiers]. 39 | 40 | [uppercase for SQL key words and lowercase for SQL identifiers]: http://www.postgresql.org/docs/9.2/static/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS 41 | [Unix-style line endings]: http://unix.stackexchange.com/questions/23903/should-i-end-my-text-script-files-with-a-newline 42 | 43 | Naming 44 | ------ 45 | 46 | * Avoid abbreviations. 47 | * Avoid types in names (`user_array`). 48 | * Name the enumeration parameter the singular of the collection. 49 | * Name variables, methods, and classes to reveal intent. 50 | * Treat acronyms as words in names (`XmlHttpRequest` not `XMLHTTPRequest`), 51 | even if the acronym is the entire name (`class Html` not `class HTML`). 52 | * Name variables holding a factory with `_factory` (`user_factory`). 53 | * Name variables created by a factory after the factory (`user_factory` 54 | creates `user`). 55 | 56 | Organization 57 | ------------ 58 | 59 | * Order methods so that caller methods are earlier in the file than the methods 60 | they call. 61 | * Order methods so that methods are as close as possible to other methods they 62 | call. 63 | 64 | Sass 65 | ---- 66 | ### Formatting 67 | * Use the *Scss* syntax. 68 | * Use hyphens when naming mixins, extends, classes, functions & variables: `span-columns` not `span_columns` or `spanColumns`. 69 | * Use space between property and value: `width: 20px` not `width:20px`. 70 | * Use a blank line above selector that has styles. 71 | * Prefer hex color codes `#000`. 72 | * Use `//` for comment blocks not `/* */`. 73 | * Use a space between selector and `{`. 74 | * Use single quotation marks for attribute selectors and property values. 75 | * Use only lowercase, including colors. 76 | * Don't add a unit specification after `0` values, unless required by a mixin. 77 | 78 | ### Order 79 | * Use alphabetical order for declarations. 80 | * Place @extends and @includes at the top of your declaration list. 81 | * Place media queries directly after the declaration list. 82 | * Place concatenated selectors second. 83 | * Place pseudo states and elements third. 84 | * Place nested selectors last. 85 | 86 | ### Selectors 87 | * Don't use ID's for style. 88 | * Use meaningful names: `$visual-grid-color` not `$color` or `$vslgrd-clr`. 89 | * Use ID and class names that are as short as possible but as long as necessary. 90 | * Append the prefix js- to ID's that are used by Javascript. 91 | * Avoid using the direct descendant selector `>`. 92 | * Avoid nesting more than 4 selectors deep. 93 | * Don't nest more than 6 selectors deep. 94 | * Use HTML tags on vague classes that need a qualifier like `header.application` not `.main`. 95 | * Avoid using the HTML tag in the class name: `section.news` not `section.news-section`. 96 | * Avoid using HTML tags on classes for generic markup `
`, ``: `.widgets` not `div.widgets`. 97 | * Avoid using HTML tags on classes with specific class names like `.featured-articles`. 98 | * Avoid using comma delimited selectors. 99 | * Avoid nesting within a media query. 100 | 101 | ### Organization 102 | * Use Bourbon for a Sass Library. 103 | * Use Neat for a grid framework. 104 | * Use Bitters / Base folder for style on HTML tags, global variables, global extends and global mixins. 105 | * Use Normalize as a browser reset. 106 | * Use HTML structure for ordering of selectors. Don't just put styles at the bottom of the Sass file. 107 | * Prefer the same file structure that is found in app/views. 108 | * Avoid having files longer than 100 lines. 109 | 110 | CoffeeScript 111 | ------------ 112 | 113 | * Initialize arrays using `[]`. 114 | * Initialize empty objects and hashes using `{}`. 115 | * Use hyphen-separated filenames, such as `coffee-script.coffee`. 116 | * Use `PascalCase` for classes, `lowerCamelCase` for variables and functions, 117 | `SCREAMING_SNAKE_CASE` for constants, `_single_leading_underscore` for 118 | private variables and functions. 119 | * Prefer `is` to `== ` or `===` 120 | * Prefer `or` and `and` to `||` and `&&` 121 | 122 | Ruby 123 | ---- 124 | 125 | [Sample](samples/ruby.rb) 126 | 127 | * Avoid conditional modifiers (lines that end with conditionals). 128 | * Avoid multiple assignments per line (`one, two = 1, 2`). 129 | * Avoid organizational comments (`# Validations`). 130 | * Avoid ternary operators (`boolean ? true : false`). Use multi-line `if` 131 | instead to emphasize code branches. 132 | * Avoid explicit return statements. 133 | * Avoid using semicolons. 134 | * Avoid bang (!) method names. Prefer descriptive names. 135 | * Don't use `self` explicitly anywhere except class methods (`def self.method`) 136 | and assignments (`self.attribute =`). 137 | * Prefer `detect` over `find`. 138 | * Prefer `inject` over `reduce`. 139 | * Prefer `map` over `collect`. 140 | * Prefer `select` over `find_all`. 141 | * Prefer single quotes for strings. 142 | * Use `_` for unused block parameters. 143 | * Use `%{}` for single-line strings needing interpolation and double-quotes. 144 | * Use `&&` and `||` for Boolean expressions. 145 | * Use `{...}` for single-line blocks. Use `do..end` for multi-line blocks. 146 | * Use `?` suffix for predicate methods. 147 | * Use `CamelCase` for classes and modules, `snake_case` for variables and 148 | methods, `SCREAMING_SNAKE_CASE` for constants. 149 | * Use `def self.method`, not `def Class.method` or `class << self`. 150 | * Use `def` with parentheses when there are arguments. 151 | * Use `each`, not `for`, for iteration. 152 | * Use heredocs for multi-line strings. 153 | 154 | ERb 155 | --- 156 | 157 | [Sample](samples/erb.rb) 158 | 159 | * When wrapping long lines, keep the method name on the same line as the ERb 160 | interpolation operator and keep each method argument on its own line. 161 | * Prefer double quotes for attributes. 162 | 163 | HTML 164 | ---- 165 | 166 | * Prefer double quotes for attributes. 167 | 168 | Rails 169 | ----- 170 | 171 | * Avoid `member` and `collection` routes. 172 | * Use private instead of protected when defining controller methods. 173 | * Name date columns with `_on` suffixes. 174 | * Name datetime columns with `_at` suffixes. 175 | * Name initializers for their gem name. 176 | * Order ActiveRecord associations alphabetically by attribute name. 177 | * Order ActiveRecord validations alphabetically by attribute name. 178 | * Order controller contents: filters, public methods, private methods. 179 | * Order model contents: constants, macros, public methods, private methods. 180 | * Put application-wide partials in the [`app/views/application`] directory. 181 | * Use `def self.method`, not the `scope :method` DSL. 182 | * Use the default `render 'partial'` syntax over `render partial: 'partial'`. 183 | 184 | [`app/views/application`]: http://asciicasts.com/episodes/269-template-inheritance 185 | 186 | Rails Routes 187 | ------------ 188 | 189 | * Avoid the `:except` option in routes. 190 | * Order resourceful routes alphabetically by name. 191 | * Use the `:only` option to explicitly state exposed routes. 192 | 193 | Background Jobs 194 | --------------- 195 | 196 | * Define a `PRIORITY` constant at the top of delayed job classes. 197 | * Define two public methods: `self.enqueue` and `perform`. 198 | * Put delayed job classes in `app/jobs`. 199 | 200 | Email 201 | ----- 202 | 203 | * Use one `ActionMailer` for the app. Name it `Mailer`. 204 | * Use the user's name in the `From` header and email in the `Reply-To` when 205 | [delivering email on behalf of the app's users]. 206 | 207 | [delivering email on behalf of the app's users]: http://robots.thoughtbot.com/post/3215611590/recipe-delivering-email-on-behalf-of-users 208 | 209 | Testing 210 | ------- 211 | 212 | * Avoid the `private` keyword in specs. 213 | * Order ActiveRecord association tests alphabetically by attribute name. 214 | * Order ActiveRecord validation tests alphabetically by attribute name. 215 | * Prefer `eq` to `==` in RSpec. 216 | * Separate setup, exercise, verification, and teardown phases with newlines. 217 | * Use RSpec's [`expect` syntax]. 218 | * Use `not_to` instead of `to_not` in RSpec expectations. 219 | 220 | [`expect` syntax]: http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax 221 | 222 | #### Acceptance Tests 223 | 224 | [Sample](samples/acceptance_test.rb) 225 | 226 | * Avoid scenario titles that add no information, such as "successfully." 227 | * Avoid scenario titles that repeat the feature title. 228 | * Place helper methods for feature specs directly in a top-level `Features` 229 | module. 230 | * Use Capybara's `feature/scenario` DSL. 231 | * Use names like `ROLE_ACTION_spec.rb`, such as 232 | `user_changes_password_spec.rb`, for feature spec file names. 233 | * Use only one `feature` block per feature spec file. 234 | * Use scenario titles that describe the success and failure paths. 235 | * Use spec/features directory to store feature specs. 236 | * Use spec/support/features for support code related to feature specs. 237 | 238 | #### Factories 239 | 240 | * Order `factories.rb` contents: sequences, traits, factory definitions. 241 | * Order factory attributes: implicit attributes, explicit attributes, 242 | child factory definitions. Each section's attributes are alphabetical. 243 | * Order factory definitions alphabetically by factory name. 244 | * Use one factories.rb file per project. 245 | 246 | #### Unit Tests 247 | 248 | [Sample](samples/testing.rb) 249 | 250 | * Don't prefix `it` block descriptions with 'should'. 251 | * Name outer `describe` blocks after the method under test. Use `.method` 252 | for class methods and `#method` for instance methods. 253 | 254 | Objective-C 255 | ----------- 256 | 257 | [Sample](samples/ObjectiveC.m) 258 | 259 | * `#import` linked frameworks in the prefix header (`ProjectName-Prefix.pch`). 260 | * Keep `.xib` files grouped with their associated view class. 261 | * Order `#import` statements alphabetically. 262 | * Order `@class` directives alphabetically. 263 | * Order `@property` modifiers: memory management, atomicity, writability. 264 | * Organize classes into `models`, `views`, `controllers`, `categories`, 265 | and `services` directories. 266 | * Prefer `@class` to `#import` when referring to external classes in a public 267 | `@interface`. 268 | * Prefer `@property` to declaring instance variables. 269 | * Prefix class names with a 2 or 3 letter project acronym. 270 | * Prefix string constants being used as keys with 'k'. 271 | * Remove `#import` statements for `Foundation` and `UIKit` in new project 272 | templates. 273 | * Separate methods by function using `#pragma mark -
` 274 | * Separate sections into subsections using `#pragma mark ` 275 | * Use `@[arrayObject]`, `@{@"key" : value}`, `@(YES or NO)`, and `@5.0` 276 | literals. 277 | * Use `@interface ClassName ()` to declare private properties. 278 | * Use `lowerCamelCase` for method names. 279 | * Use `NSAssert` in methods that require the presence of certain arguments. 280 | * Write methods using the happy path. Indent the exceptional cases. Keep the 281 | optimal case in the left-most column. 282 | 283 | Python 284 | ------ 285 | 286 | * Follow [PEP 8]. 287 | 288 | [PEP 8]: http://www.python.org/dev/peps/pep-0008/ 289 | 290 | Shell 291 | ----- 292 | 293 | * Break long lines on `|`, `&&`, or `||` and indent the continuations. 294 | * Don't add an extension to executable shell scripts. 295 | * Don't put a line break before `then` or `do`, use `if ...; then` and 296 | `while ...; do`. 297 | * Use `for x; do`, not `for x in "$@"; do`. 298 | * Use `snake_case` for variable names and `ALLCAPS` for environment 299 | variables. 300 | * Use single quotes for strings that don't contain escapes or variables. 301 | * Use two-space indentation. 302 | --------------------------------------------------------------------------------