Repository: ember-cli/eslint-plugin-ember
Pull request #146 (2017-09-25 19:28:13)

Update configs and recommendations
This PR:
- updates base and recommended configs
- removes default eslint rules' settings, so this plugin will no longer interfere with other plugins' settings
- enables `experimentalObjectRestSpread` feature in eslint parser
- regroups all rules using well known common categories: `Best Practices`, `Possible Errors`, `Stylistic Issues`
- updates script for generating tables in readme with `Deprecated Rules` section.
- updates all rules' recommendations

I hope this PR is a good starting point for an elaborate discussion which rules should be recommended, and which deprecated.

If you have any propositions/suggestions, also regarding categories to which specific rules are assigned to, please post a comment below :)

cc @rwwagner90 @Turbo87 @jbandura 

PS. This PR will be probably open for at least month or more, as it will require us to do another major release after merge. I don't want to rush it, so in the meanwhile we can continue working on new rules/fixing current ones.

Turbo87: some comments from my side:

since the release will likely be after the 2.16 release of Ember and that release will introduce the "New Module Imports" as the default we should probably enable the corresponding rules here and put some comments in the docs on how to disable them if you're on older Ember versions. Specifically this means:

- enable `new-module-imports`
- enable `no-old-shims`
- enable `no-global-jquery` (`import $ from 'jquery'` is provided by New Module Imports and old shims AFAIK)
- add a link to the codemod to the `new-module-imports` rule docs (as a replacement for `--fix` not working here)

Aside from that I have two more things I would like to change:

- disable `use-ember-get-and-set` rule

  Our tutorials, guide and API docs all use `this.get()` and not `get(this)` so we should stay consistent with the rest of the ecosystem; also it's IMHO much easier to read

- disable `order-in-*` rules

  they can be nice to have, but in my experience they are causing too many false positives and until `--fix` works for them they are unfortunately just annoying most of the time
rwwagner90: I would love to see `use-ember-get-and-set` not be the default as well, as I also prefer `this.get`, but I want to stay consistent with what is recommended here 😄 
rwwagner90: @Turbo87 what false positives do you see with `order-in-*`? They have always worked great for me.
Turbo87: @rwwagner90 e.g. empty default hooks like `onSelect() {}`
rwwagner90: @Turbo87 firstly, why define an empty hook? And secondly, didn't I see @michalsnik added support for empty method ordering now?
jbandura: nice job @snik!

As I see it we should also probably consider switching off the `alias-model-in-controller` rule, as it's not able to detect whether we actually have any kind of model in the first place. I do see the value this rule brings, but currently in many cases this rule is just not smart enough :( If user sees a legitimate use case he can always switch it back on.

Apart from that I do agree with @Turbo87 about disabling `use-ember-get-and-set` rule by default, or making it not report errors for object context (i.e using `this.get`, `this.set`, etc. should be allowed).
sandstrom: Great that we're dropping `plugin:ember/recommended` and only provide `plugin:ember/base` (or whatever we end up calling it).

The non-ember rules creates confusion, people don't know were they're coming from and its opinionated outside the realm of Ember (`no-underscore-dangle` on/off does not matter for an Ember app). Will also make the readme cleaner if we don't need to explain the difference between recommended and base. Awesome! ⭐️ 
michalsnik: I addressed all suggestions, but I think order-in-* rules prove to be really helpful and are a quick-win to keep the code well organised from the beginning. But I decided to update `order-in-components` rule and put `empty-method` below `property`, as I think this is clearly a better place for them.

Let me also know guys what do you think about assignments to groups :)
rwwagner90: I agree that `order-in-*` should be on. It gives a lot of value and I'm not sure why you would want to define empty lifecycle hooks, and even if you did, it should still work right?
michalsnik: You want to write empty hooks when you're creating an addon, or if you just want to put a clear information what can be passed down to a component without using ember prop types addon @rwwagner90 Currently empty methods were detected as custom methods, thus this rule wanted them to be put below actions hash, which you'd rather would not.
Turbo87: as I mentioned above: as long as `order-in-*` does not support `--fix` they are mostly just annoying instead of helping when enabled on an older codebase that didn't follow those patterns from the start. due to that fact I'm very strongly against enabling those rules by default.

the other reason is that the order is largely a stylistic preference but not actually catching any bugs or reporting about deprecated code logic which should be the main aim of the rules in this repo.
rwwagner90: @Turbo87 I think the ordering should not be a preference though. With no imposed order, it's very cumbersome to switch between codebases. If you just put random things in no order throughout a 1000+ line JS file, you would have a very hard time finding everything. I think there is great value in having a standard here, so Ember developers can switch easily between projects and expect a general ordering and more easily be productive out of the box.

People could always disable it, if they don't want it, or configure it custom and do order of one thing at a time.

Patch:
diff --git a/.eslintrc.js b/.eslintrc.js
index 88047787..263a79f1 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -13,6 +13,6 @@ module.exports = {
     'strict': 0,
     'comma-dangle': [0, {
       'functions': 'never',
-    }]
+    }],
   },
 }
diff --git a/README.md b/README.md
index 1e5ee1c0..e29775a5 100644
--- a/README.md
+++ b/README.md
@@ -31,7 +31,7 @@ module.exports = {
   ],
   extends: [
     'eslint:recommended',
-    'plugin:ember/recommended'
+    'plugin:ember/recommended' // or 'plugin:ember/base'
   ],
   rules: {
     // override rules' settings here
@@ -40,12 +40,12 @@ module.exports = {
 ```
 
 Possible configurations:
-- [plugin:ember/base](https://github.com/ember-cli/eslint-plugin-ember/blob/master/lib/config/base.js) - contains only recommended settings for custom rules defined in this plugin.
-- [plugin:ember/recommended](https://github.com/ember-cli/eslint-plugin-ember/blob/master/lib/config/recommended.js) - extends base configuration with extra rules' settings provided by eslint
+- [plugin:ember/base](https://github.com/ember-cli/eslint-plugin-ember/blob/master/lib/config/base.js) - contains no rules settings, but the basic eslint configuration suitable for eny ember project. You can use it to configure rules as you wish.
+- [plugin:ember/recommended](https://github.com/ember-cli/eslint-plugin-ember/blob/master/lib/config/recommended.js) - extends base configuration with recommended rules' settings
 
 #### Use plain plugin:
 
-If you don't want to use predefined settings, you can use it as a plain plugin and choose which rules you'd like to use by yourself like this:
+If you don't want to use predefined settings, you can use it as a plain plugin:
 
 ```javascript
 module.exports = {
@@ -68,72 +68,46 @@ All rules from this plugin have to be prefixed with `ember/`
 
 Rules are grouped by category to help you understand their purpose.
 
-All rules below with a check mark :white_check_mark: are enabled by default while using `plugin:ember/base` or `plugin:ember/recommended` configs.
+All rules below with a check mark :white_check_mark: are enabled by default while using `plugin:ember/recommended` config.
 
 The `--fix` option on the command line automatically fixes problems reported by rules which have a wrench :wrench: below.
 
 <!--RULES_TABLE_START-->
 
-### Controllers
+### Best Practices
 
 |    | Rule ID | Description |
 |:---|:--------|:------------|
-| :white_check_mark: | [alias-model-in-controller](./docs/rules/alias-model-in-controller.md) | Enforces aliasing model in controller |
-
-
-### Components
-
-|    | Rule ID | Description |
-|:---|:--------|:------------|
-| :white_check_mark: | [avoid-leaking-state-in-components](./docs/rules/avoid-leaking-state-in-components.md) | Avoids state leakage |
+|  | [alias-model-in-controller](./docs/rules/alias-model-in-controller.md) | Enforces aliasing model in controller |
 | :white_check_mark: | [closure-actions](./docs/rules/closure-actions.md) | Enforces usage of closure actions |
-| :white_check_mark: | [no-attrs-snapshot](./docs/rules/no-attrs-snapshot.md) | Disallow use of attrs snapshot in `didReceiveAttrs` and `didUpdateAttrs` |
+|  | [named-functions-in-promises](./docs/rules/named-functions-in-promises.md) | Enforces usage of named functions in promises |
+| :white_check_mark: | [new-module-imports](./docs/rules/new-module-imports.md) |  Use "New Module Imports" from Ember RFC #176 |
+|  | [no-empty-attrs](./docs/rules/no-empty-attrs.md) | Prevents usage of empty attributes in ember data models |
+| :white_check_mark: | [no-function-prototype-extensions](./docs/rules/no-function-prototype-extensions.md) | Prevents usage of Ember's `function` prototype extensions |
+| :white_check_mark: | [no-global-jquery](./docs/rules/no-global-jquery.md) | Prevents usage of global jQuery object |
+|  | [no-jquery](./docs/rules/no-jquery.md) | Disallow any usage of jQuery |
+|  | [no-observers](./docs/rules/no-observers.md) | Prevents usage of observers |
+| :white_check_mark::wrench: | [no-old-shims](./docs/rules/no-old-shims.md) | Prevents usage of old shims for modules |
 | :white_check_mark: | [no-on-calls-in-components](./docs/rules/no-on-calls-in-components.md) | Prevents usage of `on` calls in components |
+| :wrench: | [use-ember-get-and-set](./docs/rules/use-ember-get-and-set.md) | Enforces usage of Ember.get and Ember.set |
 
 
-### General
+### Possible Errors
 
 |    | Rule ID | Description |
 |:---|:--------|:------------|
+| :white_check_mark: | [avoid-leaking-state-in-components](./docs/rules/avoid-leaking-state-in-components.md) | Avoids state leakage |
 | :white_check_mark: | [jquery-ember-run](./docs/rules/jquery-ember-run.md) | Prevents usage of jQuery without Ember Run Loop |
-|  | [local-modules](./docs/rules/local-modules.md) | Enforces usage of local modules |
-| :white_check_mark: | [named-functions-in-promises](./docs/rules/named-functions-in-promises.md) | Enforces usage of named functions in promises |
-|  | [new-module-imports](./docs/rules/new-module-imports.md) |  Use "New Module Imports" from Ember RFC #176 |
-|  | [no-attrs-in-components](./docs/rules/no-attrs-in-components.md) | Disallow usage of this.attrs in components |
-|  | [no-duplicate-dependent-keys](./docs/rules/no-duplicate-dependent-keys.md) | Disallow repeating dependent keys |
-| :white_check_mark: | [no-function-prototype-extensions](./docs/rules/no-function-prototype-extensions.md) | Prevents usage of Ember's `function` prototype extensions |
-|  | [no-global-jquery](./docs/rules/no-global-jquery.md) | Prevents usage of global jQuery object |
-| :white_check_mark: | [no-observers](./docs/rules/no-observers.md) | Prevents usage of observers |
-| :wrench: | [no-old-shims](./docs/rules/no-old-shims.md) | Prevents usage of old shims for modules |
-| :white_check_mark: | [no-side-effects](./docs/rules/no-side-effects.md) | Warns about unexpected side effects in computed properties |
-|  | [require-super-in-init](./docs/rules/require-super-in-init.md) | Enforces super calls in init hooks |
-| :white_check_mark: | [use-brace-expansion](./docs/rules/use-brace-expansion.md) | Enforces usage of brace expansion |
-| :white_check_mark::wrench: | [use-ember-get-and-set](./docs/rules/use-ember-get-and-set.md) | Enforces usage of Ember.get and Ember.set |
-
-
-### Routing
-
-|    | Rule ID | Description |
-|:---|:--------|:------------|
+| :white_check_mark: | [no-attrs-in-components](./docs/rules/no-attrs-in-components.md) | Disallow usage of this.attrs in components |
+| :white_check_mark: | [no-attrs-snapshot](./docs/rules/no-attrs-snapshot.md) | Disallow use of attrs snapshot in `didReceiveAttrs` and `didUpdateAttrs` |
 | :white_check_mark: | [no-capital-letters-in-routes](./docs/rules/no-capital-letters-in-routes.md) | Raise an error when there is a route with uppercased letters in router.js |
+| :white_check_mark: | [no-duplicate-dependent-keys](./docs/rules/no-duplicate-dependent-keys.md) | Disallow repeating dependent keys |
+| :white_check_mark: | [no-side-effects](./docs/rules/no-side-effects.md) | Warns about unexpected side effects in computed properties |
+| :white_check_mark: | [require-super-in-init](./docs/rules/require-super-in-init.md) | Enforces super calls in init hooks |
 | :white_check_mark: | [routes-segments-snake-case](./docs/rules/routes-segments-snake-case.md) | Enforces usage of snake_cased dynamic segments in routes |
 
 
-### Ember Data
-
-|    | Rule ID | Description |
-|:---|:--------|:------------|
-| :white_check_mark: | [no-empty-attrs](./docs/rules/no-empty-attrs.md) | Prevents usage of empty attributes in ember data models |
-
-
-### Components & tests
-
-|    | Rule ID | Description |
-|:---|:--------|:------------|
-|  | [no-jquery](./docs/rules/no-jquery.md) | Disallow any usage of jQuery |
-
-
-### Organizing
+### Stylistic Issues
 
 |    | Rule ID | Description |
 |:---|:--------|:------------|
@@ -141,6 +115,15 @@ The `--fix` option on the command line automatically fixes problems reported by
 | :white_check_mark: | [order-in-controllers](./docs/rules/order-in-controllers.md) | Enforces proper order of properties in controllers |
 | :white_check_mark: | [order-in-models](./docs/rules/order-in-models.md) | Enforces proper order of properties in models |
 | :white_check_mark: | [order-in-routes](./docs/rules/order-in-routes.md) | Enforces proper order of properties in routes |
+| :white_check_mark: | [use-brace-expansion](./docs/rules/use-brace-expansion.md) | Enforces usage of brace expansion |
+
+### Deprecated
+
+> :warning: We're going to remove deprecated rules in the next major release. Please migrate to successor/new rules.
+
+| Rule ID | Replaced by |
+|:--------|:------------|
+| [local-modules](./docs/rules/local-modules.md) | [new-module-imports](./docs/rules/new-module-imports.md) |
 
 <!--RULES_TABLE_END-->
 
diff --git a/docs/rules/new-module-imports.md b/docs/rules/new-module-imports.md
index c6c93ddd..81779019 100644
--- a/docs/rules/new-module-imports.md
+++ b/docs/rules/new-module-imports.md
@@ -9,6 +9,8 @@ If you use `ember-cli-babel` with version `6.6.0` or above you can start using
 the "New Module Imports" instead of the `Ember` global directly. This will
 enable us to build better tree shaking feature into Ember CLI.
 
+If you want to transition to new module imports in old Ember app use dedicated [codemod](https://github.com/ember-cli/ember-modules-codemod). For more informations please read [the following article](https://medium.com/@Dhaulagiri/embers-javascript-modules-api-b4483782f329) by Brian Runnells.
+
 ```javascript
 // GOOD
 import Component from '@ember/component'
diff --git a/docs/rules/order-in-components.md b/docs/rules/order-in-components.md
index 46af8d5b..add63b8a 100644
--- a/docs/rules/order-in-components.md
+++ b/docs/rules/order-in-components.md
@@ -9,6 +9,7 @@ ember/order-in-components: [2, {
   order: [
     'service',
     'property',
+    'empty-method',
     'single-line-function',
     'multi-line-function',
     'observer',
@@ -24,7 +25,7 @@ ember/order-in-components: [2, {
     'willClearRender',
     'didDestroyElement',
     'actions',
-    ['method', 'empty-method'],
+    'method',
   ]
 }]
 ```
@@ -76,24 +77,27 @@ export default Component.extend({
   // 1. Services
   i18n: service(),
 
-  // 2. Defaults
+  // 2. Properties
   role: 'sloth',
 
-  // 3. Single line Computed Property
+  // 3. Empty methods
+  onRoleChange() {},
+
+  // 4. Single line Computed Property
   vehicle: alias('car'),
 
-  // 4. Multiline Computed Property
+  // 5. Multiline Computed Property
   levelOfHappiness: computed('attitude', 'health', function() {
     const result = this.get('attitude') * this.get('health') * Math.random();
     return result;
   }),
 
-  // 5. Observers
+  // 6. Observers
   onVehicleChange: observer('vehicle', function() {
     // observer logic
   }),
 
-  // 6. Lifecycle Hooks
+  // 7. Lifecycle Hooks
   init() {
     // custom init logic
   },
@@ -101,19 +105,19 @@ export default Component.extend({
   didInsertElement() {
     // custom didInsertElement logic
   },
-  
+
   willDestroyElement() {
     // custom willDestroyElement logic
   },
 
-  // 7. All actions
+  // 8. All actions
   actions: {
     sneakyAction() {
       return this._secretMethod();
     }
   },
 
-  // 8. Custom / private methods
+  // 9. Custom / private methods
   _secretMethod() {
     // custom secret method logic
   }
diff --git a/lib/config/base.js b/lib/config/base.js
index 4a7cbc7c..b1eed506 100644
--- a/lib/config/base.js
+++ b/lib/config/base.js
@@ -1,21 +1,20 @@
-const rules = require('../recommended-rules.js');
-
 module.exports = {
   root: true,
 
   parserOptions: {
     ecmaVersion: 6,
     sourceType: 'module',
+    ecmaFeatures: {
+      experimentalObjectRestSpread: true,
+    },
   },
 
   env: {
     browser: true,
-    jquery: true,
+    es6: true,
   },
 
   plugins: [
     'ember',
   ],
-
-  rules,
 };
diff --git a/lib/config/recommended.js b/lib/config/recommended.js
index 81059927..2c83c722 100644
--- a/lib/config/recommended.js
+++ b/lib/config/recommended.js
@@ -1,17 +1,6 @@
+const rules = require('../recommended-rules.js');
+
 module.exports = {
   extends: require.resolve('./base.js'),
-
-  rules: {
-    'func-names': 0,
-    'space-before-function-paren': 0,
-    'no-shadow': 0,
-    'no-else-return': 0,
-    'no-extend-native': 0,
-    'no-underscore-dangle': 0,
-    'arrow-body-style': 0,
-    'prefer-arrow-callback': 0,
-    'prefer-rest-params': 0,
-    'consistent-return': 0,
-    'generator-star-spacing': [2, 'after'],
-  },
+  rules,
 };
diff --git a/lib/recommended-rules.js b/lib/recommended-rules.js
index 7ea5084c..fc0244d8 100644
--- a/lib/recommended-rules.js
+++ b/lib/recommended-rules.js
@@ -5,31 +5,31 @@
  * definitions, execute "npm run update"
  */
 module.exports = {
-  "ember/alias-model-in-controller": "error",
+  "ember/alias-model-in-controller": "off",
   "ember/avoid-leaking-state-in-components": "error",
   "ember/closure-actions": "error",
   "ember/jquery-ember-run": "error",
   "ember/local-modules": "off",
-  "ember/named-functions-in-promises": "error",
-  "ember/new-module-imports": "off",
-  "ember/no-attrs-in-components": "off",
+  "ember/named-functions-in-promises": "off",
+  "ember/new-module-imports": "error",
+  "ember/no-attrs-in-components": "error",
   "ember/no-attrs-snapshot": "error",
   "ember/no-capital-letters-in-routes": "error",
-  "ember/no-duplicate-dependent-keys": "off",
-  "ember/no-empty-attrs": "error",
+  "ember/no-duplicate-dependent-keys": "error",
+  "ember/no-empty-attrs": "off",
   "ember/no-function-prototype-extensions": "error",
-  "ember/no-global-jquery": "off",
+  "ember/no-global-jquery": "error",
   "ember/no-jquery": "off",
-  "ember/no-observers": "error",
-  "ember/no-old-shims": "off",
+  "ember/no-observers": "off",
+  "ember/no-old-shims": "error",
   "ember/no-on-calls-in-components": "error",
   "ember/no-side-effects": "error",
   "ember/order-in-components": "error",
   "ember/order-in-controllers": "error",
   "ember/order-in-models": "error",
   "ember/order-in-routes": "error",
-  "ember/require-super-in-init": "off",
+  "ember/require-super-in-init": "error",
   "ember/routes-segments-snake-case": "error",
   "ember/use-brace-expansion": "error",
-  "ember/use-ember-get-and-set": "error"
+  "ember/use-ember-get-and-set": "off"
 }
\ No newline at end of file
diff --git a/lib/rules/alias-model-in-controller.js b/lib/rules/alias-model-in-controller.js
index 485aae8b..2bfca794 100644
--- a/lib/rules/alias-model-in-controller.js
+++ b/lib/rules/alias-model-in-controller.js
@@ -11,8 +11,8 @@ module.exports = {
   meta: {
     docs: {
       description: 'Enforces aliasing model in controller',
-      category: 'Controllers',
-      recommended: true
+      category: 'Best Practices',
+      recommended: false
     },
     fixable: null, // or "code" or "whitespace"
   },
diff --git a/lib/rules/avoid-leaking-state-in-components.js b/lib/rules/avoid-leaking-state-in-components.js
index 662b92cb..58755a38 100644
--- a/lib/rules/avoid-leaking-state-in-components.js
+++ b/lib/rules/avoid-leaking-state-in-components.js
@@ -11,7 +11,7 @@ module.exports = {
   meta: {
     docs: {
       description: 'Avoids state leakage',
-      category: 'Components',
+      category: 'Possible Errors',
       recommended: true
     },
     fixable: null, // or "code" or "whitespace"
diff --git a/lib/rules/closure-actions.js b/lib/rules/closure-actions.js
index 508c4a32..28fcdd96 100644
--- a/lib/rules/closure-actions.js
+++ b/lib/rules/closure-actions.js
@@ -10,7 +10,7 @@ module.exports = {
   meta: {
     docs: {
       description: 'Enforces usage of closure actions',
-      category: 'Components',
+      category: 'Best Practices',
       recommended: true
     },
     fixable: null, // or "code" or "whitespace"
diff --git a/lib/rules/jquery-ember-run.js b/lib/rules/jquery-ember-run.js
index 29ca606a..5a342cc3 100644
--- a/lib/rules/jquery-ember-run.js
+++ b/lib/rules/jquery-ember-run.js
@@ -11,7 +11,7 @@ module.exports = {
   meta: {
     docs: {
       description: 'Prevents usage of jQuery without Ember Run Loop',
-      category: 'General',
+      category: 'Possible Errors',
       recommended: true
     },
     fixable: null, // or "code" or "whitespace"
diff --git a/lib/rules/local-modules.js b/lib/rules/local-modules.js
index 0f2680dc..77a88459 100644
--- a/lib/rules/local-modules.js
+++ b/lib/rules/local-modules.js
@@ -10,8 +10,9 @@ module.exports = {
   meta: {
     docs: {
       description: 'Enforces usage of local modules',
-      category: 'General',
-      recommended: false
+      category: 'Best Practices',
+      recommended: false,
+      replacedBy: ['new-module-imports'],
     },
     deprecated: true,
     fixable: null, // or "code" or "whitespace"
diff --git a/lib/rules/named-functions-in-promises.js b/lib/rules/named-functions-in-promises.js
index 4d56d927..37bad478 100644
--- a/lib/rules/named-functions-in-promises.js
+++ b/lib/rules/named-functions-in-promises.js
@@ -10,8 +10,8 @@ module.exports = {
   meta: {
     docs: {
       description: 'Enforces usage of named functions in promises',
-      category: 'General',
-      recommended: true
+      category: 'Best Practices',
+      recommended: false
     },
     fixable: null, // or "code" or "whitespace"
   },
diff --git a/lib/rules/new-module-imports.js b/lib/rules/new-module-imports.js
index d55f5240..5cbcef56 100644
--- a/lib/rules/new-module-imports.js
+++ b/lib/rules/new-module-imports.js
@@ -10,8 +10,8 @@ module.exports = {
   meta: {
     docs: {
       description: ' Use "New Module Imports" from Ember RFC #176',
-      category: 'General',
-      recommended: false
+      category: 'Best Practices',
+      recommended: true
     },
   },
 
diff --git a/lib/rules/no-attrs-in-components.js b/lib/rules/no-attrs-in-components.js
index 922ed637..79cfc26f 100644
--- a/lib/rules/no-attrs-in-components.js
+++ b/lib/rules/no-attrs-in-components.js
@@ -12,8 +12,8 @@ module.exports = {
   meta: {
     docs: {
       description: 'Disallow usage of this.attrs in components',
-      category: 'General',
-      recommended: false
+      category: 'Possible Errors',
+      recommended: true
     },
     fixable: null,
   },
diff --git a/lib/rules/no-attrs-snapshot.js b/lib/rules/no-attrs-snapshot.js
index 9af02dbd..8c66fe14 100644
--- a/lib/rules/no-attrs-snapshot.js
+++ b/lib/rules/no-attrs-snapshot.js
@@ -13,7 +13,7 @@ module.exports = {
   meta: {
     docs: {
       description: 'Disallow use of attrs snapshot in `didReceiveAttrs` and `didUpdateAttrs`',
-      category: 'Components',
+      category: 'Possible Errors',
       recommended: true
     },
     fixable: null, // or "code" or "whitespace"
diff --git a/lib/rules/no-capital-letters-in-routes.js b/lib/rules/no-capital-letters-in-routes.js
index 99f981e5..89b12e24 100644
--- a/lib/rules/no-capital-letters-in-routes.js
+++ b/lib/rules/no-capital-letters-in-routes.js
@@ -9,7 +9,7 @@ module.exports = {
   meta: {
     docs: {
       description: 'Raise an error when there is a route with uppercased letters in router.js',
-      category: 'Routing',
+      category: 'Possible Errors',
       recommended: true,
     },
     fixable: null, // or "code" or "whitespace"
diff --git a/lib/rules/no-duplicate-dependent-keys.js b/lib/rules/no-duplicate-dependent-keys.js
index 35a80a19..d77556c4 100644
--- a/lib/rules/no-duplicate-dependent-keys.js
+++ b/lib/rules/no-duplicate-dependent-keys.js
@@ -11,8 +11,8 @@ module.exports = {
   meta: {
     docs: {
       description: 'Disallow repeating dependent keys',
-      category: 'General',
-      recommended: false
+      category: 'Possible Errors',
+      recommended: true
     },
     fixable: null,
     message: MESSAGE,
diff --git a/lib/rules/no-empty-attrs.js b/lib/rules/no-empty-attrs.js
index 45d80573..a177bddf 100644
--- a/lib/rules/no-empty-attrs.js
+++ b/lib/rules/no-empty-attrs.js
@@ -10,8 +10,8 @@ module.exports = {
   meta: {
     docs: {
       description: 'Prevents usage of empty attributes in ember data models',
-      category: 'Ember Data',
-      recommended: true
+      category: 'Best Practices',
+      recommended: false
     },
     fixable: null, // or "code" or "whitespace"
   },
diff --git a/lib/rules/no-function-prototype-extensions.js b/lib/rules/no-function-prototype-extensions.js
index f42884d6..aeb0e9c8 100644
--- a/lib/rules/no-function-prototype-extensions.js
+++ b/lib/rules/no-function-prototype-extensions.js
@@ -10,7 +10,7 @@ module.exports = {
   meta: {
     docs: {
       description: 'Prevents usage of Ember\'s `function` prototype extensions',
-      category: 'General',
+      category: 'Best Practices',
       recommended: true
     },
     fixable: null, // or "code" or "whitespace"
diff --git a/lib/rules/no-global-jquery.js b/lib/rules/no-global-jquery.js
index 8d36320a..bfbece3e 100644
--- a/lib/rules/no-global-jquery.js
+++ b/lib/rules/no-global-jquery.js
@@ -14,8 +14,8 @@ module.exports = {
   meta: {
     docs: {
       description: 'Prevents usage of global jQuery object',
-      category: 'General',
-      recommended: false,
+      category: 'Best Practices',
+      recommended: true,
     },
     fixable: null,  // or "code" or "whitespace"
     message: MESSAGE,
diff --git a/lib/rules/no-jquery.js b/lib/rules/no-jquery.js
index f39b94a2..41ec7f95 100644
--- a/lib/rules/no-jquery.js
+++ b/lib/rules/no-jquery.js
@@ -14,7 +14,7 @@ module.exports = {
   meta: {
     docs: {
       description: 'Disallow any usage of jQuery',
-      category: 'Components & tests',
+      category: 'Best Practices',
       recommended: false
     },
     fixable: null, // or "code" or "whitespace"
diff --git a/lib/rules/no-observers.js b/lib/rules/no-observers.js
index 548268e9..56c2b73c 100644
--- a/lib/rules/no-observers.js
+++ b/lib/rules/no-observers.js
@@ -10,8 +10,8 @@ module.exports = {
   meta: {
     docs: {
       description: 'Prevents usage of observers',
-      category: 'General',
-      recommended: true
+      category: 'Best Practices',
+      recommended: false
     },
     fixable: null, // or "code" or "whitespace"
   },
diff --git a/lib/rules/no-old-shims.js b/lib/rules/no-old-shims.js
index cd5ee845..87237df1 100644
--- a/lib/rules/no-old-shims.js
+++ b/lib/rules/no-old-shims.js
@@ -10,8 +10,8 @@ module.exports = {
   meta: {
     docs: {
       description: 'Prevents usage of old shims for modules',
-      category: 'General',
-      recommended: false
+      category: 'Best Practices',
+      recommended: true
     },
     fixable: 'code',
   },
diff --git a/lib/rules/no-on-calls-in-components.js b/lib/rules/no-on-calls-in-components.js
index 658e9a8d..eaf25a9b 100644
--- a/lib/rules/no-on-calls-in-components.js
+++ b/lib/rules/no-on-calls-in-components.js
@@ -11,7 +11,7 @@ module.exports = {
   meta: {
     docs: {
       description: 'Prevents usage of `on` calls in components',
-      category: 'Components',
+      category: 'Best Practices',
       recommended: true
     },
     fixable: null, // or "code" or "whitespace"
diff --git a/lib/rules/no-side-effects.js b/lib/rules/no-side-effects.js
index 6da2af05..a58f0e84 100644
--- a/lib/rules/no-side-effects.js
+++ b/lib/rules/no-side-effects.js
@@ -10,7 +10,7 @@ module.exports = {
   meta: {
     docs: {
       description: 'Warns about unexpected side effects in computed properties',
-      category: 'General',
+      category: 'Possible Errors',
       recommended: true
     },
     fixable: null, // or "code" or "whitespace"
diff --git a/lib/rules/order-in-components.js b/lib/rules/order-in-components.js
index 8eef4c4f..663076bf 100644
--- a/lib/rules/order-in-components.js
+++ b/lib/rules/order-in-components.js
@@ -9,6 +9,7 @@ const addBackwardsPosition = propOrder.addBackwardsPosition;
 const ORDER = [
   'service',
   'property',
+  'empty-method',
   'single-line-function',
   'multi-line-function',
   'observer',
@@ -24,7 +25,7 @@ const ORDER = [
   'willClearRender',
   'didDestroyElement',
   'actions',
-  ['method', 'empty-method']
+  'method'
 ];
 
 //------------------------------------------------------------------------------
@@ -35,7 +36,7 @@ module.exports = {
   meta: {
     docs: {
       description: 'Enforces proper order of properties in components',
-      category: 'Organizing',
+      category: 'Stylistic Issues',
       recommended: true
     },
     fixable: null // or "code" or "whitespace"
diff --git a/lib/rules/order-in-controllers.js b/lib/rules/order-in-controllers.js
index b2edbc3e..36aa205d 100644
--- a/lib/rules/order-in-controllers.js
+++ b/lib/rules/order-in-controllers.js
@@ -26,7 +26,7 @@ module.exports = {
   meta: {
     docs: {
       description: 'Enforces proper order of properties in controllers',
-      category: 'Organizing',
+      category: 'Stylistic Issues',
       recommended: true
     },
     fixable: null, // or "code" or "whitespace"
diff --git a/lib/rules/order-in-models.js b/lib/rules/order-in-models.js
index 20d74b01..3efbc838 100644
--- a/lib/rules/order-in-models.js
+++ b/lib/rules/order-in-models.js
@@ -22,7 +22,7 @@ module.exports = {
   meta: {
     docs: {
       description: 'Enforces proper order of properties in models',
-      category: 'Organizing',
+      category: 'Stylistic Issues',
       recommended: true
     },
     fixable: null, // or "code" or "whitespace"
diff --git a/lib/rules/order-in-routes.js b/lib/rules/order-in-routes.js
index a3e5fe6f..1c281720 100644
--- a/lib/rules/order-in-routes.js
+++ b/lib/rules/order-in-routes.js
@@ -34,7 +34,7 @@ module.exports = {
   meta: {
     docs: {
       description: 'Enforces proper order of properties in routes',
-      category: 'Organizing',
+      category: 'Stylistic Issues',
       recommended: true
     },
     fixable: null // or "code" or "whitespace"
diff --git a/lib/rules/require-super-in-init.js b/lib/rules/require-super-in-init.js
index 7270fe43..8b0f6670 100644
--- a/lib/rules/require-super-in-init.js
+++ b/lib/rules/require-super-in-init.js
@@ -11,8 +11,8 @@ module.exports = {
   meta: {
     docs: {
       description: 'Enforces super calls in init hooks',
-      category: 'General',
-      recommended: false
+      category: 'Possible Errors',
+      recommended: true
     },
     fixable: null, // or "code" or "whitespace"
   },
diff --git a/lib/rules/routes-segments-snake-case.js b/lib/rules/routes-segments-snake-case.js
index 434c4f20..383c6f77 100644
--- a/lib/rules/routes-segments-snake-case.js
+++ b/lib/rules/routes-segments-snake-case.js
@@ -12,7 +12,7 @@ module.exports = {
   meta: {
     docs: {
       description: 'Enforces usage of snake_cased dynamic segments in routes',
-      category: 'Routing',
+      category: 'Possible Errors',
       recommended: true
     },
     fixable: null, // or "code" or "whitespace"
diff --git a/lib/rules/use-brace-expansion.js b/lib/rules/use-brace-expansion.js
index 614b1e34..5bce3b3a 100644
--- a/lib/rules/use-brace-expansion.js
+++ b/lib/rules/use-brace-expansion.js
@@ -11,7 +11,7 @@ module.exports = {
   meta: {
     docs: {
       description: 'Enforces usage of brace expansion',
-      category: 'General',
+      category: 'Stylistic Issues',
       recommended: true
     },
     fixable: null, // or "code" or "whitespace"
diff --git a/lib/rules/use-ember-get-and-set.js b/lib/rules/use-ember-get-and-set.js
index 10fb48f1..32ebb708 100644
--- a/lib/rules/use-ember-get-and-set.js
+++ b/lib/rules/use-ember-get-and-set.js
@@ -16,8 +16,8 @@ module.exports = {
   meta: {
     docs: {
       description: 'Enforces usage of Ember.get and Ember.set',
-      category: 'General',
-      recommended: true
+      category: 'Best Practices',
+      recommended: false
     },
     fixable: 'code',
   },
diff --git a/scripts/update-rules.js b/scripts/update-rules.js
index dfaa32cd..620adafe 100644
--- a/scripts/update-rules.js
+++ b/scripts/update-rules.js
@@ -45,14 +45,14 @@ const categories = rules
     return arr;
   }, []);
 
-const rulesTableContent = categories.map(category => `
+let rulesTableContent = categories.map(category => `
 ### ${category}
 
 |    | Rule ID | Description |
 |:---|:--------|:------------|
 ${
   rules
-    .filter(entry => entry[1].meta.docs.category === category)
+    .filter(([, rule]) => rule.meta.docs.category === category && !rule.meta.deprecated)
     .map((entry) => {
       const name = entry[0];
       const meta = entry[1].meta;
@@ -65,6 +65,27 @@ ${
 }
 `).join('\n');
 
+rulesTableContent += `
+### Deprecated
+
+> :warning: We're going to remove deprecated rules in the next major release. Please migrate to successor/new rules.
+
+| Rule ID | Replaced by |
+|:--------|:------------|
+${
+  rules
+    .filter(entry => entry[1].meta.deprecated)
+    .map((entry) => {
+      const name = entry[0];
+      const meta = entry[1].meta;
+      const link = `[${name}](./docs/rules/${name}.md)`;
+      const replacedBy = (meta.docs.replacedBy || []).map(id => `[${id}](./docs/rules/${id}.md)`).join(', ') || '(no replacement)';
+      return `| ${link} | ${replacedBy} |`;
+    })
+    .join('\n')
+}
+`;
+
 const recommendedRules = rules.reduce((obj, entry) => {
   const name = `ember/${entry[0]}`;
   const recommended = entry[1].meta.docs.recommended;


Test patch:
diff --git a/tests/__snapshots__/recommended.js.snap b/tests/__snapshots__/recommended.js.snap
index 287e4fe1..6d17554c 100644
--- a/tests/__snapshots__/recommended.js.snap
+++ b/tests/__snapshots__/recommended.js.snap
@@ -2,25 +2,26 @@
 
 exports[`recommended rules 1`] = `
 Array [
-  "alias-model-in-controller",
   "avoid-leaking-state-in-components",
   "avoid-leaking-state-in-ember-objects",
   "closure-actions",
   "jquery-ember-run",
-  "named-functions-in-promises",
+  "new-module-imports",
+  "no-attrs-in-components",
   "no-attrs-snapshot",
   "no-capital-letters-in-routes",
-  "no-empty-attrs",
+  "no-duplicate-dependent-keys",
   "no-function-prototype-extensions",
-  "no-observers",
+  "no-global-jquery",
+  "no-old-shims",
   "no-on-calls-in-components",
   "no-side-effects",
   "order-in-components",
   "order-in-controllers",
   "order-in-models",
   "order-in-routes",
+  "require-super-in-init",
   "routes-segments-snake-case",
   "use-brace-expansion",
-  "use-ember-get-and-set",
 ]
 `;
diff --git a/tests/lib/rules/order-in-components.js b/tests/lib/rules/order-in-components.js
index 3c67314e..56bf63e0 100644
--- a/tests/lib/rules/order-in-components.js
+++ b/tests/lib/rules/order-in-components.js
@@ -131,7 +131,8 @@ eslintTester.run('order-in-components', rule, {
 
         actions: {},
 
-        customFunction() {
+        customFunc() {
+          return true;
         }
       });`,
       parserOptions: { ecmaVersion: 6, sourceType: 'module' }
@@ -157,7 +158,9 @@ eslintTester.run('order-in-components', rule, {
         actions: {},
 
         customFunc() {
-        }});`,
+          return true;
+        }
+      });`,
       parserOptions: { ecmaVersion: 6, sourceType: 'module' }
     },
     {
@@ -219,6 +222,8 @@ eslintTester.run('order-in-components', rule, {
       code: `export default Component.extend({
         test: service(),
 
+        someEmptyMethod() {},
+
         didReceiveAttrs() {
         },
 
@@ -226,6 +231,7 @@ eslintTester.run('order-in-components', rule, {
         }),
 
         _anotherPrivateFnc() {
+          return true;
         }
       });`,
       parserOptions: { ecmaVersion: 6, sourceType: 'module' }
@@ -357,11 +363,11 @@ eslintTester.run('order-in-components', rule, {
     },
     {
       code: `export default Component.extend({
+        onFoo() {},
+        onFoo: () => {},
         foo: computed(function() {
         }).volatile(),
-        onFoo() {},
-        bar() { const foo = 'bar'},
-        onFoo: () => {}
+        bar() { const foo = 'bar'}
       });`,
       parserOptions: { ecmaVersion: 6, sourceType: 'module' }
     },
@@ -727,6 +733,23 @@ eslintTester.run('order-in-components', rule, {
         message: 'The "init" lifecycle hook should be above the actions hash on line 2',
         line: 6
       }]
+    },
+    {
+      code: `export default Component.extend({
+        foo: computed(function() {
+        }).volatile(),
+        onFoo() {},
+        bar() { const foo = 'bar'},
+        onBar: () => {}
+      });`,
+      parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+      errors: [{
+        message: 'The "onFoo" empty method should be above the "foo" multi-line function on line 2',
+        line: 4
+      }, {
+        message: 'The "onBar" empty method should be above the "foo" multi-line function on line 2',
+        line: 6
+      }]
     }
   ]
 });