From 19b25dbd88550be0a499e8f779232831e695165d Mon Sep 17 00:00:00 2001 From: Steve Polito Date: Thu, 14 May 2026 05:27:41 -0400 Subject: [PATCH 1/3] Remove rules that can be replaced with a linter. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In an effort to follow [best practices], we remove items that can be enforced with a linter. The spirit of this commit is to keep CLAUDE.md as lean as possible, and relates to this [feedback]. File | Removed rule | Cop / Tool | |---|---|---| | controllers.md | Actions should not exceed 10 lines | `Metrics/MethodLength` (built-in, default `Max: 10`) | | controllers.md | Never `params.permit!` (kept in security.md) | No built-in RuboCop cop. Brakeman flags it; custom cop needed for lint coverage. `Rails/StrongParametersExpect` targets `params.require(...).permit(...)` → `params.expect(...)`, **not** `permit!`. | | database.md | Migrations must be reversible | `Rails/ReversibleMigration` (rubocop-rails) | | database.md | Add `null: false` and database-level defaults where appropriate | Partial only. `Rails/NotNullColumn` flags `add_column ..., null: false` *without* a default (a related gotcha, not the same rule). `Rails/ThreeStateBooleanColumn` enforces `null: false` on boolean columns specifically. No general cop enforces `null: false` across all columns. | | models.md | Long parameter lists (max 3 args) | `Metrics/ParameterLists` (built-in, set `Max: 3`) | | models.md | Extract models >200 lines / 15 public / 7 private methods | `Metrics/ClassLength` covers the 200-line threshold only. No built-in cop for public/private method counts — custom cop required. | | testing.md | One `expect` per `it` block | `RSpec/MultipleExpectations` (rubocop-rspec) | | testing.md | Max 2 levels of context nesting | `RSpec/NestedGroups` (rubocop-rspec, set `Max: 2`) [best practices]: https://code.claude.com/docs/en/best-practices#write-an-effective-claude-md [feedback]: https://github.com/thoughtbot/readysetgo/pull/190#issuecomment-4424181576 --- rails/ai-rules/rules/controllers.md | 2 -- rails/ai-rules/rules/database.md | 2 -- rails/ai-rules/rules/models.md | 4 +--- rails/ai-rules/rules/testing.md | 1 - 4 files changed, 1 insertion(+), 8 deletions(-) diff --git a/rails/ai-rules/rules/controllers.md b/rails/ai-rules/rules/controllers.md index 7968e0f5..fe849f0c 100644 --- a/rails/ai-rules/rules/controllers.md +++ b/rails/ai-rules/rules/controllers.md @@ -1,9 +1,7 @@ # Controllers - Controllers handle HTTP only: receive request, delegate to model, return response. -- Actions should not exceed 10 lines (excluding strong params). Longer actions often signal business logic that belongs in a model or PORO. - Maximum one instance variable per action. - No business logic, calculations, email sending, or multi-object operations in controllers. -- Always use strong parameters. Never `params.permit!`. - Return `status: :unprocessable_entity` on failed form renders (required by Turbo). - Prefer RESTful routes. Custom verb actions (e.g., post "activate") usually mean a missing noun/resource (e.g., resource :trial, only: [:create]). diff --git a/rails/ai-rules/rules/database.md b/rails/ai-rules/rules/database.md index bb13fc44..3cc30bfe 100644 --- a/rails/ai-rules/rules/database.md +++ b/rails/ai-rules/rules/database.md @@ -1,8 +1,6 @@ # Database & Migrations - Always use the `rails generate migration` command to create migration files. -- Migrations must be reversible. -- Add `null: false` and database-level defaults where appropriate. - Use `text` over `string` if length varies significantly. - Wrap multi-record operations in transactions. Use `save!` (bang) inside transactions. - Keep scopes as one-liners. Complex queries belong in search/query objects. diff --git a/rails/ai-rules/rules/models.md b/rails/ai-rules/rules/models.md index 71a8716f..87f6595c 100644 --- a/rails/ai-rules/rules/models.md +++ b/rails/ai-rules/rules/models.md @@ -4,8 +4,6 @@ - Name classes after domain nouns, not actions. No `*Service`, `*Manager`, `*Handler` suffixes. - Use `ActiveModel::Model` for POROs that need validation or form integration. - Replace `.call` / `.perform` with domain verbs: `#save`, `#complete`, `#submit`, `#deliver`. -- Look to identify domain models that can be extracted when an existing - model exceeds: 200 lines, 15 public methods, or 7 private methods. - Callbacks only for data integrity (normalise fields, set defaults). Never for emails, payments, or external systems. - Prefer composition over inheritance. Extract behaviour into small, focused objects. -- Avoid feature envy, long parameter lists (max 3 args), case statements on type, and mixin abuse. +- Avoid feature envy, case statements on type, and mixin abuse. diff --git a/rails/ai-rules/rules/testing.md b/rails/ai-rules/rules/testing.md index 3ecf75fd..7f3c2538 100644 --- a/rails/ai-rules/rules/testing.md +++ b/rails/ai-rules/rules/testing.md @@ -11,5 +11,4 @@ - Factories: only required attributes with sensible defaults. Start in `spec/factories.rb`. - Shoulda Matchers for validations and associations. - WebMock blocks all external HTTP in tests — always stub external requests. -- One `expect` per `it` block. Max 2 levels of context nesting. - Never test private methods directly. Never stub the system under test. From e7be045beb1840ffa8159a12d155d73a2fa41669 Mon Sep 17 00:00:00 2001 From: Steve Polito Date: Mon, 18 May 2026 15:44:13 -0400 Subject: [PATCH 2/3] Address feedback --- rails/ai-rules/rules/controllers.md | 1 + rails/ai-rules/rules/models.md | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/rails/ai-rules/rules/controllers.md b/rails/ai-rules/rules/controllers.md index fe849f0c..7978012d 100644 --- a/rails/ai-rules/rules/controllers.md +++ b/rails/ai-rules/rules/controllers.md @@ -1,6 +1,7 @@ # Controllers - Controllers handle HTTP only: receive request, delegate to model, return response. +- Avoid long actions, since they often signal business logic that belongs in a model or PORO. - Maximum one instance variable per action. - No business logic, calculations, email sending, or multi-object operations in controllers. - Return `status: :unprocessable_entity` on failed form renders (required by Turbo). diff --git a/rails/ai-rules/rules/models.md b/rails/ai-rules/rules/models.md index 87f6595c..b8f70c37 100644 --- a/rails/ai-rules/rules/models.md +++ b/rails/ai-rules/rules/models.md @@ -4,6 +4,7 @@ - Name classes after domain nouns, not actions. No `*Service`, `*Manager`, `*Handler` suffixes. - Use `ActiveModel::Model` for POROs that need validation or form integration. - Replace `.call` / `.perform` with domain verbs: `#save`, `#complete`, `#submit`, `#deliver`. +- Look to identify domain models that can be extracted when an existing model is large. - Callbacks only for data integrity (normalise fields, set defaults). Never for emails, payments, or external systems. - Prefer composition over inheritance. Extract behaviour into small, focused objects. -- Avoid feature envy, case statements on type, and mixin abuse. +- Avoid feature envy, long parameter lists, case statements on type, and mixin abuse. From 1a9bc057bec3a9b46a2077661dbc64d55bedd06b Mon Sep 17 00:00:00 2001 From: Steve Polito Date: Mon, 1 Jun 2026 07:30:52 -0400 Subject: [PATCH 3/3] Restore `null: false` rule Addresses https://github.com/thoughtbot/guides/pull/792#discussion_r3302471393 --- rails/ai-rules/rules/database.md | 1 + 1 file changed, 1 insertion(+) diff --git a/rails/ai-rules/rules/database.md b/rails/ai-rules/rules/database.md index 3cc30bfe..9f730eea 100644 --- a/rails/ai-rules/rules/database.md +++ b/rails/ai-rules/rules/database.md @@ -2,6 +2,7 @@ - Always use the `rails generate migration` command to create migration files. - Use `text` over `string` if length varies significantly. +- Add `null: false` and database-level defaults where appropriate. - Wrap multi-record operations in transactions. Use `save!` (bang) inside transactions. - Keep scopes as one-liners. Complex queries belong in search/query objects. - Never use `Post.all` without pagination.