layered-rails/skills/layered-rails/SKILL.md
Write, refactor, and review Rails code using layered architecture principles from "Layered Design for Ruby on Rails Applications". Use when writing or refactoring Rails code — models, controllers, services, jobs, mailers, policies, forms, query objects, presenters, view components, state machines, serializers, or AI/LLM features — to apply correct patterns and avoid layer violations; and when reviewing Rails code, PRs, or diffs for layer violations, fat controllers/models, anemic models, callback misuse, god objects, or specification-test failures. Triggers on "layered design", "architecture layers", "abstraction layer", "specification test", "layer violation", "fat controller/model", "god object", "anemic model", "extract service/callback/policy/concern", "service object", "form object", "policy object", "query object", "value object", "presenter", "view component", "state machine", "Active Delivery", "callback scoring", "Rails refactor/review", "Rails patterns/best practices".
npx skillsauth add palkan/skills layered-railsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Design and review Rails applications using layered architecture principles.
Rails applications are organized into four architecture layers with unidirectional data flow:
┌─────────────────────────────────────────┐
│ PRESENTATION LAYER │
│ Controllers, Views, Channels, Mailers │
└─────────────────────────────────────────┘
↓
┌─────────────────────────────────────────┐
│ APPLICATION LAYER │
│ Service Objects, Form Objects, etc. │
└─────────────────────────────────────────┘
↓
┌─────────────────────────────────────────┐
│ DOMAIN LAYER │
│ Models, Value Objects, Domain Events │
└─────────────────────────────────────────┘
↓
┌─────────────────────────────────────────┐
│ INFRASTRUCTURE LAYER │
│ Active Record, APIs, File Storage │
└─────────────────────────────────────────┘
Core Rule: Lower layers must never depend on higher layers.
See Architecture Layers Reference for the full layer responsibilities and the Four Rules deep-dive.
Use this skill when:
In Claude Code with this skill installed as a plugin (/plugin install layered-rails@palkan-skills), each workflow above is also reachable as a slash command — see Slash Commands. Natural-language requests ("review this file with layered-rails", "run the specification test on app/models/order.rb") work in any environment that has the skill loaded.
Reusable procedures bundled inside this skill. Read the file and apply it to the target code:
app/services/ and service-like classes (per-cluster proposals, contracts, layer hygiene)| Violation | Example | Fix |
|-----------|---------|-----|
| Model uses Current | Current.user in model | Pass user as explicit parameter |
| Service accepts request | param :request in service | Extract value object from request |
| Controller has business logic | Pricing calculations in action | Extract to service or model |
| Anemic models | All logic in services | Keep domain logic in models |
| Category | Reference | |----------|-----------| | Layer violations (Current in models, request in services, notifications in models, business logic in controllers) | layer-violations.md | | Service objects (anemic models, bag of random objects, premature abstraction) | service-objects.md | | Callbacks (operation callbacks, skip callbacks, control flags) | callbacks.md | | Concerns (code-slicing, overgrown) | concerns.md | | Helpers (HTML construction in helpers) | helpers.md | | Jobs (anemic jobs) | jobs.md | | Testing (testing wrong layer) | testing.md |
If the specification of an object describes features beyond the primary responsibility of its abstraction layer, such features should be extracted into lower layers.
How to apply:
See Specification Test Reference for detailed guide.
| Pattern | Layer | Use When | Reference | |---------|-------|----------|-----------| | Service Object | Application | Orchestrating domain operations | service-objects.md | | Query Object | Domain | Complex, reusable queries | query-objects.md | | Form Object | Presentation | Multi-model forms, complex validation | form-objects.md | | Filter Object | Presentation | Request parameter transformation | filter-objects.md | | Presenter | Presentation | View-specific logic, multiple models | presenters.md | | Serializer | Presentation | API response formatting | serializers.md | | Policy Object | Application | Authorization decisions | policy-objects.md | | Value Object | Domain | Immutable, identity-less concepts | value-objects.md | | Collaborator Object | Domain | A slice of one model's behavior in a typed delegate | collaborator-objects.md | | State Machine | Domain | States, events, transitions | state-machines.md | | Concern | Domain | Shared behavioral extraction | concerns.md | | Repository | Application | Last resort — returning custom domain objects mapped from AR data, after AR scopes (simple) and query objects (query building) are insufficient | repositories.md |
"Where should this code go?"
| If you have... | Consider... | |----------------|-------------| | Complex multi-model form | Form Object | | Request parameter filtering/transformation | Filter Object | | View-specific formatting | Presenter | | Complex database query used in multiple places | Query Object | | Business operation spanning multiple models | Service Object (as waiting room) | | Authorization rules | Policy Object | | Multi-channel notifications | Delivery Object (Active Delivery) |
Remember: Services are a "waiting room" for code until proper abstractions emerge. Don't let app/services become a bag of random objects.
Canonical before/after transformations for the most common layerification moves. The layerification plan workflow uses these as reference templates when proposing phases.
| Scenario | Goal area | Reference | |----------|-----------|-----------| | Extract callbacks to service | callbacks, after_create chains | callbacks-to-service.md | | Extract authorization to policy | authorization, permissions | authorization-to-policy.md | | Extract query logic to query object | complex scopes, reporting queries | query-to-query-object.md | | Extract Current from model | Current.* in domain | current-from-model.md | | Decompose god object with associated objects | god model, large User/Account | god-object-decomposition.md | | Replace implicit state machine | timestamp-based status | implicit-to-explicit-state-machine.md | | Extract view logic to presenter | template logic, formatting | view-logic-to-presenter.md | | Form object for complex input | fat controllers, multi-model forms | complex-input-to-form-object.md |
These slash commands are available only when this skill is installed as a Claude Code plugin (/plugin install layered-rails@palkan-skills). When the skill is installed via skills.sh or any other path that delivers skills/layered-rails/ without the surrounding plugin, the commands won't be present — invoke the corresponding workflow directly (see Workflows) or just ask in plain language.
| Command | Workflow | Purpose |
|---------|----------|---------|
| /layered-rails:review | review | Review code changes from a layered architecture perspective |
| /layered-rails:spec-test | spec-test | Run specification test on specific files |
| /layered-rails:analyze | analyze | Full codebase abstraction-layer analysis |
| /layered-rails:analyze-services | analyze-services | Audit app/services/ and service-like classes — conventions, clusters, layer hygiene, test consequences |
| /layered-rails:analyze-callbacks | analyze-callbacks | Score model callbacks, find extraction candidates |
| /layered-rails:analyze-gods | analyze-gods | Find god objects via churn × complexity |
| /layered-rails:plan [goal] | plan | Plan gradual adoption of layered patterns |
For deep dives on specific topics:
| Topic | Reference | |-------|-----------| | Authorization (RBAC, ABAC, policies) | authorization.md | | Notifications (multi-channel delivery) | notifications.md | | View Components | view-components.md | | AI Integration (LLM, agents, RAG, MCP) | ai-integration.md | | Configuration | configuration.md | | Callbacks (scoring, extraction) | callbacks.md | | Current Attributes | current-attributes.md | | Instrumentation (logging, metrics) | instrumentation.md |
For library-specific guidance:
| Gem | Purpose | Reference | |-----|---------|-----------| | action_policy | Authorization framework | action-policy.md | | view_component | Component framework | view-component.md | | anyway_config | Typed configuration | anyway-config.md | | active_delivery | Multi-channel notifications | active-delivery.md | | alba | JSON serialization | alba.md | | workflow | State machines | workflow.md | | rubanok | Filter/transformation DSL | rubanok.md | | active_agent | AI agent framework | active-agent.md | | active_job-performs | Eliminate anemic jobs | active-job-performs.md |
When to extract from models:
| Signal | Metric | Action | |--------|--------|--------| | God object | High churn × complexity | Decompose into concerns, delegates, or separate models | | Operation callback | Score 1-2/5 | Extract to service or event handler | | Code-slicing concern | Groups by artifact type | Convert to behavioral concern or extract | | Current dependency | Model reads Current.* | Pass as explicit parameter |
Callback Scoring: | Type | Score | Keep? | |------|-------|-------| | Transformer (compute values) | 5/5 | Yes | | Normalizer (sanitize input) | 4/5 | Yes | | Utility (counter caches) | 4/5 | Yes | | Observer (side effects) | 2/5 | Maybe | | Operation (business steps) | 1/5 | Extract |
See Extraction Signals Reference for detailed guide.
Recommended order within model files:
class User < ApplicationRecord
# 1. Gems/DSL extensions
has_secure_password
# 2. Associations
belongs_to :account
has_many :posts
# 3. Enums
enum :status, { pending: 0, active: 1 }
# 4. Normalization
normalizes :email, with: -> { _1.strip.downcase }
# 5. Validations
validates :email, presence: true
# 6. Scopes
scope :active, -> { where(status: :active) }
# 7. Callbacks (transformers only)
before_validation :set_defaults
# 8. Delegations
delegate :name, to: :account, prefix: true
# 9. Public methods
def full_name = "#{first_name} #{last_name}"
# 10. Private methods
private
def set_defaults
self.locale ||= I18n.default_locale
end
end
Well-layered code:
app/services become permanent residence for codedevelopment
Maintainer-only workflow for handling GitHub Secret Scanning alerts on OpenClaw. Use when Codex needs to triage, redact, clean up, and resolve secret leakage found in issue comments, issue bodies, PR comments, or other GitHub content.
development
Maintainer workflow for OpenClaw releases, prereleases, changelog release notes, and publish validation. Use when Codex needs to prepare or verify stable or beta release steps, align version naming, assemble release notes, check release auth requirements, or validate publish-time commands and artifacts.
development
Run, watch, debug, and extend OpenClaw QA testing with qa-lab and qa-channel. Use when Codex needs to execute the repo-backed QA suite, inspect live QA artifacts, debug failing scenarios, add new QA scenarios, or explain the OpenClaw QA workflow. Prefer the live OpenAI lane with regular openai/gpt-5.4 in fast mode; do not use gpt-5.4-pro or gpt-5.4-mini unless the user explicitly overrides that policy.
development
End-to-end Parallels smoke, upgrade, and rerun workflow for OpenClaw across macOS, Windows, and Linux guests. Use when Codex needs to run, rerun, debug, or interpret VM-based install, onboarding, gateway smoke tests, latest-release-to-main upgrade checks, fresh snapshot retests, or optional Discord roundtrip verification under Parallels.