Vanilla Rails Naming
37signals naming conventions from production Basecamp codebases.
The Naming Cascade
Every state change flows through this cascade:
| Layer | Pattern | Example |
|---|---|---|
| Concern | Adjective (-able/-ible) | Card::Closeable |
| State model | Noun | Closure |
| Controller | Plural noun | ClosuresController |
| Route | Singular resource | resource :closure |
| Method | Plain verb (no !) | card.close |
| Async enqueue | *_later | notify_watchers_later |
| Sync execute | *_now or plain | notify_watchers_now |
If the cascade doesn't flow, the naming is wrong.
Concern Names: -able/-ible ONLY
# Wrong
Card::Closing # verb
Card::PinManager # Manager/Handler/Service
Card::Closed # past participle
# Right
Card::Closeable
Card::Pinnable
Card::Assignable
If concern name doesn't end in -able/-ible, STOP and rename immediately. No -ing, -er, -ish, Logic, Management, Handler, Service.
State Methods: No Bangs
Plain verbs for state changes. ! only when non-bang counterpart exists.
# Wrong # Right
card.close! card.close
card.pin! card.pin_by(user)
card.archive! card.archive
Async: _later/_now
_later always at the end of compound verbs:
notify_watchers_later # enqueues job
notify_watchers_now # actual logic (called by job)
pin_and_notify_later # compound verb
File Locations
app/models/card/closeable.rb # Model-specific concern
app/models/concerns/eventable.rb # Shared concern
app/models/closure.rb # State record
app/controllers/cards/closures_controller.rb
Commit Messages
Present tense, lowercase, no type prefixes:
add pin feature to cards
extract closeable concern
fix closure validation
Not: feat:, fix:, [FEATURE], past tense, title case.
Red Flags
| Red flag | Fix |
|---|---|
| Concern not -able/-ible | Rename immediately |
State method with ! | Remove bang |
post :close in routes | resource :closure |
Async without _later | Add suffix |
ClosureController (singular) | ClosuresController (plural) |
Commit with feat: prefix | Present tense, no prefix |
