Apply Code Conventions
Style source of truth: Style and formatting defer to the project's configured linter(s). This skill adds non-style behavior and architecture guidance only. For Hotwire + Tailwind specifics, see apply-stack-conventions.
Quick Reference
| Topic |
Rule |
| Principles |
DRY, YAGNI, PORO where it helps, CoC, KISS |
| Comments / tags |
Explain why; tagged notes need actionable context |
| Logging |
First arg: static string; second arg: hash with event: key; no interpolation; backtrace on errors |
| Deep stacks |
Chain apply-stack-conventions → domain skills (services, jobs, RSpec) |
HARD-GATE
TESTS GATE IMPLEMENTATION:
When this skill guides new behavior, the tests gate still applies:
PRD → TASKS → TEST (write, run, fail) → IMPLEMENTATION → …
No implementation code before a failing test. See write-tests.
Core Process
When reviewing or refactoring Rails code, follow this sequence. Each step maps to a required checkpoint in your output.
- Run linter — Detect config (e.g.
.rubocop.yml or .standard.yml), run the appropriate tool, note absence if none found. Output: linter detected (or absent); style defers to it.
- Apply area-specific rules — Check path patterns and apply targeted guidance from the Apply by area table. Output: concrete per-path recommendations for every relevant changed file.
- Verify tests gate — Confirm failing tests exist before any new behavior. Output: failing spec, run command, expected failure, minimal implementation step, passing rerun.
- Enforce structured logging — Ensure all
Rails.logger calls use static strings + structured hashes with an event: key, plus backtrace for errors. Output: apply structured logging rules from Sub-Rules below.
- Enforce comment discipline — Ensure all tags (
TODO:, FIXME:) have actionable context (owner, ticket). Output: apply comment discipline rules from Sub-Rules below.
- Chain to specialised skills — Use the Integration table to pull in deeper guidance (security, jobs, specs) as needed.
Language: English unless explicitly requested otherwise.
Sub-Rules
Comments and tagged notes
Comment why, not what. Tags — TODO: / FIXME: / HACK: / NOTE: / OPTIMIZE: — must carry actionable context (owner, ticket, next step). Naked tags fail review.
# BAD — naked tag, no context
# TODO: fix this
# GOOD — TODO with next step + dependency
# TODO(jsmith, JIRA-1234): replace TIER_RATES with DB-backed lookup once billing API v2 is stable.
Structured Logging
MANDATORY SHAPE — every Rails.logger.* call uses exactly two positional arguments.
Rails.logger.<level>(static_string_message, { event: "dot.namespaced", ...domain_fields })
# GOOD — error path with backtrace
rescue StandardError => e
Rails.logger.error("order.processing_failed", {
event: "order.processing_failed",
error: e.message,
backtrace: e.backtrace.first(5).join("\n")
})
raise
end
- 1st arg (string): static string literal.
- 2nd arg (hash): first key is always
event:.
Apply by area (path patterns)
| Area |
Path pattern |
Guidance |
| ActiveRecord performance |
app/models/**/*.rb |
Eager load in loops; prefer pluck / exists? / find_each. |
| Controllers |
app/controllers/**/*_controller.rb |
Strong params; thin actions → services; IDOR / PII → security-check. |
| RSpec |
spec/**/*_spec.rb |
FactoryBot; let > let! unless eager setup required. |
| Service objects |
app/services/**/*.rb |
Single responsibility; .call / injected deps. |
| Background jobs |
app/jobs/**/*.rb / app/workers/**/*.rb |
Idempotency, retries, queue choice, and side-effect boundaries → implement-background-job. |
RSpec and let_it_be (test-prof)
Only recommend let_it_be if test-prof is already in Gemfile.lock. Otherwise default to let; reach for let! only when lazy evaluation would break the example. Don't introduce test-prof unless asked.
Extended Resources (Progressive Disclosure)
Load these files only when their specific content is needed:
- assets/checklist.md — Use for detailed code review checklists.
- assets/snippets.md — Use for quick code snippets of common patterns.
Document which assets were loaded and why in your output so the process is verifiable.
Integration
| Skill |
When to chain |
| apply-stack-conventions |
Stack-specific: PostgreSQL, Hotwire, Tailwind |
| model-domain |
When domain concepts and invariants need clearer Rails-first modeling choices |
| create-service-object |
Implementing or refining service objects |
| implement-background-job |
Workers, queues, retries, idempotency |
| write-tests |
Spec style, tests gate (red/green/refactor), request vs controller specs |
| security-check |
Controllers, params, IDOR, PII |
| code-review |
Full PR pass before merge |
1---2name: apply-code-conventions3description: Use when applying daily Rails conventions by path (DRY, YAGNI, PORO, CoC, KISS). Style defers to the project linter. Trigger words: conventions, clean code, RuboCop, DRY, YAGNI.4license: MIT5---67# Apply Code Conventions89**Style source of truth:** Style and formatting defer to the project's configured linter(s). This skill adds **non-style behavior** and **architecture guidance** only. For Hotwire + Tailwind specifics, see **apply-stack-conventions**.1011## Quick Reference1213| Topic | Rule |14|-------|------|15| Principles | DRY, YAGNI, PORO where it helps, CoC, KISS |16| Comments / tags | Explain **why**; tagged notes need actionable context |17| Logging | First arg: static string; second arg: hash with `event:` key; no interpolation; backtrace on errors |18| Deep stacks | Chain **apply-stack-conventions** → domain skills (services, jobs, RSpec) |1920## HARD-GATE2122```text23TESTS GATE IMPLEMENTATION:24When this skill guides new behavior, the tests gate still applies:25PRD → TASKS → TEST (write, run, fail) → IMPLEMENTATION → …26No implementation code before a failing test. See write-tests.27```2829## Core Process3031When reviewing or refactoring Rails code, follow this sequence. Each step maps to a required checkpoint in your output.32331. **Run linter** — Detect config (e.g. `.rubocop.yml` or `.standard.yml`), run the appropriate tool, note absence if none found. *Output: linter detected (or absent); style defers to it.*342. **Apply area-specific rules** — Check path patterns and apply targeted guidance from the Apply by area table. *Output: concrete per-path recommendations for every relevant changed file.*353. **Verify tests gate** — Confirm failing tests exist before any new behavior. *Output: failing spec, run command, expected failure, minimal implementation step, passing rerun.*364. **Enforce structured logging** — Ensure all `Rails.logger` calls use static strings + structured hashes with an `event:` key, plus backtrace for errors. *Output: apply structured logging rules from Sub-Rules below.*375. **Enforce comment discipline** — Ensure all tags (`TODO:`, `FIXME:`) have actionable context (owner, ticket). *Output: apply comment discipline rules from Sub-Rules below.*386. **Chain to specialised skills** — Use the Integration table to pull in deeper guidance (security, jobs, specs) as needed.3940> **Language:** English unless explicitly requested otherwise.4142## Sub-Rules4344### Comments and tagged notes45Comment **why**, not **what**. Tags — `TODO:` / `FIXME:` / `HACK:` / `NOTE:` / `OPTIMIZE:` — must carry actionable context (owner, ticket, next step). Naked tags fail review.46```ruby47# BAD — naked tag, no context48# TODO: fix this4950# GOOD — TODO with next step + dependency51# TODO(jsmith, JIRA-1234): replace TIER_RATES with DB-backed lookup once billing API v2 is stable.52```5354### Structured Logging55**MANDATORY SHAPE — every `Rails.logger.*` call uses exactly two positional arguments.**56```ruby57Rails.logger.<level>(static_string_message, { event: "dot.namespaced", ...domain_fields })5859# GOOD — error path with backtrace60rescue StandardError => e61 Rails.logger.error("order.processing_failed", {62 event: "order.processing_failed",63 error: e.message,64 backtrace: e.backtrace.first(5).join("\n")65 })66 raise67end68```69- **1st arg (string):** static string literal.70- **2nd arg (hash):** first key is always `event:`.7172### Apply by area (path patterns)73| Area | Path pattern | Guidance |74|------|--------------|----------|75| **ActiveRecord performance** | `app/models/**/*.rb` | Eager load in loops; prefer `pluck` / `exists?` / `find_each`. |76| **Controllers** | `app/controllers/**/*_controller.rb` | Strong params; thin actions → services; IDOR / PII → **security-check**. |77| **RSpec** | `spec/**/*_spec.rb` | FactoryBot; `let` > `let!` unless eager setup required. |78| **Service objects** | `app/services/**/*.rb` | Single responsibility; `.call` / injected deps. |79| **Background jobs** | `app/jobs/**/*.rb` / `app/workers/**/*.rb` | Idempotency, retries, queue choice, and side-effect boundaries → **implement-background-job**. |8081### RSpec and `let_it_be` (test-prof)82Only recommend `let_it_be` if `test-prof` is already in `Gemfile.lock`. Otherwise default to `let`; reach for `let!` only when lazy evaluation would break the example. Don't introduce `test-prof` unless asked.8384## Extended Resources (Progressive Disclosure)8586Load these files only when their specific content is needed:8788- **[assets/checklist.md](assets/checklist.md)** — Use for detailed code review checklists.89- **[assets/snippets.md](assets/snippets.md)** — Use for quick code snippets of common patterns.9091Document which assets were loaded and why in your output so the process is verifiable.9293## Integration9495| Skill | When to chain |96|-------|---------------|97| **apply-stack-conventions** | Stack-specific: PostgreSQL, Hotwire, Tailwind |98| **model-domain** | When domain concepts and invariants need clearer Rails-first modeling choices |99| **create-service-object** | Implementing or refining service objects |100| **implement-background-job** | Workers, queues, retries, idempotency |101| **write-tests** | Spec style, **tests gate** (red/green/refactor), request vs controller specs |102| **security-check** | Controllers, params, IDOR, PII |103| **code-review** | Full PR pass before merge |