What is a Claude Skill?

A skill is a Markdown file that teaches Claude how to do a specific task โ€” reliably, repeatably, and with your preferred approach. Think of it as a standing operating procedure that Claude consults whenever a relevant task comes up.

Why Skills Matter for Agentic Coding

When you're building Rails apps, you make the same kinds of decisions over and over: how to structure a migration, how to write a service object, how to set up RSpec factories, how to handle Turbo Streams. Without a skill, Claude improvises each time. With a skill, it follows your pattern every time.

๐Ÿ’ก The Core Idea

A skill is like hiring a senior dev who learned exactly how your team works. They don't need to re-ask "how do we do auth here?" โ€” they already know.

How Skills Fit Into Claude's Brain

When you send Claude a message, it first reads your available_skills list โ€” just the name and description of each skill. If the description matches what you're asking, Claude fetches and reads the full SKILL.md file before responding.

1

You send a message

"Add a service object for Stripe billing to my Rails app"

2

Claude scans skill descriptions

It reads the short description of each installed skill to decide which are relevant.

3

Claude reads matching SKILL.md files

The full instruction body is loaded into context โ€” conventions, patterns, examples.

4

Claude executes with your patterns

Output follows the exact conventions you defined, not Claude's defaults.

What Skills Are Good For

Great Use CaseWhy It WorksExample
Coding conventions Repeatable, objectively verifiable output Service object structure, naming conventions
Boilerplate generation Fixed templates with variable slots RSpec spec structure, factory templates
Multi-step workflows Complex sequences Claude would otherwise improvise Adding a new resource (model + migration + controller + views)
Tool-specific patterns Domain knowledge Claude might not default to Hotwire/Turbo patterns, Sidekiq job structure
Document creation Fixed format outputs Architecture decision records (ADRs), PR descriptions

Anatomy of SKILL.md

Every skill is a single Markdown file with a YAML frontmatter block at the top and a body of instructions below. The structure is simple but each part has a specific job.

The Full Structure

---
name: rails-service-objects
description: Generate service objects in a Rails app following the
  project's established patterns. Use this skill whenever the user
  asks to add business logic, extract code from a controller, create
  a service, or implement an operation class. Also trigger for any
  mention of "service layer", "PORO", or "business logic class".
---

# Rails Service Objects

Short intro paragraph explaining what this skill covers.

## Convention

The specific conventions to follow โ€” naming, file location, structure.

## Template

The actual code template or pattern to use.

## Examples

Before/after or usage examples.

## Notes

Edge cases, gotchas, things to avoid.
SKILL.md

The Three Layers

A

Frontmatter always in context

The name and description fields. These are always loaded, even when the skill isn't triggered. Keep the description sharp and triggering โ€” it's the only thing Claude sees when deciding whether to use your skill.

B

SKILL.md body loaded when triggered

The full instructions. Loaded into Claude's context when the skill is triggered. Aim for under 500 lines. Everything Claude needs to do the task well should be here โ€” or referenced from here.

C

Bundled resources loaded on demand

Scripts, reference files, templates, and assets in subdirectories. Claude reads these only when it needs them, so they can be large. A references/ folder with framework-specific docs is a common pattern.

Required vs Optional Fields

FieldRequired?Purpose
nameโœ… YesIdentifier. Use kebab-case: rails-factories
descriptionโœ… YesTriggering signal โ€” the most critical field
compatibilityโŒ RarelyRequired tools, node version, etc.
licenseโŒ OptionalFor shared/public skills
Body contentโœ… YesThe actual instructions
โš ๏ธ Name Stability

The name field is used as the skill's directory name when packaged. Don't rename a skill after users have installed it โ€” you'll break their installations. Get the name right from the start.

File System Layout

# Simple skill โ€” just a SKILL.md
rails-service-objects/
โ””โ”€โ”€ SKILL.md

# Complex skill with resources
rails-full-resource/
โ”œโ”€โ”€ SKILL.md           # Main instructions + pointers to references
โ”œโ”€โ”€ scripts/
โ”‚   โ””โ”€โ”€ scaffold.sh    # Shell scripts Claude can run
โ”œโ”€โ”€ references/
โ”‚   โ”œโ”€โ”€ hotwire.md     # Framework-specific docs
โ”‚   โ””โ”€โ”€ rspec.md
โ””โ”€โ”€ assets/
    โ””โ”€โ”€ spec_template.rb  # Code templates
Structure

The Description Field

The description is the most critical part of your skill. It's the only thing Claude reads when deciding whether to consult your skill โ€” get it wrong and your skill never fires, no matter how good the body is.

What It Needs to Do

  • Tell Claude what the skill does
  • Tell Claude when to trigger it โ€” the specific contexts, phrases, and request types
  • Be "pushy" โ€” Claude naturally under-triggers skills, so the description should lean toward triggering
  • Include synonyms and alternate phrasings users might say

Good vs Bad Descriptions

โŒ Too Vague
description: Helps with Rails development.
โœ… Specific & Pushy
description: Generate Rails service objects,
  POROs, and operation classes. Use whenever
  the user wants to extract business logic,
  add a service layer, or mentions "service
  object", "operation class", "interactor",
  or "command pattern" in a Rails context.
โŒ Only Describes, Doesn't Trigger
description: A skill for creating RSpec
  tests in Rails projects.
โœ… Includes Trigger Phrases
description: Write RSpec tests for Rails
  models, controllers, and services. Trigger
  for any request to "write a test", "add
  specs", "test this", "write unit tests",
  or "cover this with tests" in a Rails app.

The Anatomy of a Great Description

description: # Part 1: What does it do?
  Generate database migrations for Rails apps following
  safe, reversible migration patterns.
  # Part 2: When to trigger (explicit contexts)
  Use this skill whenever the user asks to add a column,
  create a table, add an index, or change a schema.
  # Part 3: Trigger phrases (synonyms & natural language)
  Also trigger for "need a migration", "update the schema",
  "add field to model", or any mention of ActiveRecord
  schema changes.
Description Formula

Common Trigger Phrase Patterns

PatternExample Phrases to Include
Direct task mention "write a migration", "create a service object", "add RSpec spec"
Problem description "controller is getting too fat", "need to extract this logic"
Framework jargon "Turbo Stream", "Stimulus controller", "Hotwire", "Sidekiq worker"
Pattern names "service object", "PORO", "decorator", "presenter", "interactor"
Action verbs "add", "generate", "scaffold", "create", "extract", "refactor"
๐Ÿ’ก Pro Tip: Write Descriptions Last

Write the skill body first โ€” once you know exactly what it does, you'll write a much more accurate and complete description. The description is a summary of the body, not a plan for it.

โš ๏ธ Don't Over-Broaden

If your description matches too many things, you'll get false triggers โ€” Claude reads your skill when it shouldn't. Balance "pushy" with "specific". A skill for service objects shouldn't trigger when someone asks about ActiveRecord queries.

Instructions That Work

Good skill instructions are like good code: specific, unambiguous, and built around concrete examples rather than abstract principles.

Principles of Effective Instructions

1. Be Concrete, Not Abstract

Abstract principles leave room for interpretation. Concrete rules don't.

โŒ Abstract
Follow clean code principles.
Use meaningful names.
Keep methods short.
โœ… Concrete
Place service objects in app/services/.
Name them VerbNoun (e.g. CreateOrder,
  SendInvoice, CancelSubscription).
Each class exposes a single public method
  called .call.
Return a Result object, not raw data.

2. Show, Don't Just Tell

Include a template or minimal example. Claude uses it as a reference, not just inspiration.

## Template

```ruby
# app/services/verb_noun.rb
class VerbNoun
  Result = Struct.new(:success?, :data, :error, keyword_init: true)

  def initialize(arg1:, arg2:)
    @arg1 = arg1
    @arg2 = arg2
  end

  def call
    # main logic here
    Result.new(success?: true, data: result)
  rescue SomeError => e
    Result.new(success?: false, error: e.message)
  end
end
```
SKILL.md โ€” Template Section

3. Specify File Locations

Always say where files go. Claude will default to something reasonable if you don't โ€” but reasonable isn't always right for your project.

## File Locations

- Service objects: `app/services/`
- Specs for services: `spec/services/`
- Shared contexts: `spec/support/shared_contexts/`
- Custom matchers: `spec/support/matchers/`
SKILL.md โ€” File Locations Section

4. State What NOT to Do

Negative constraints are often more valuable than positive instructions because they override Claude's defaults.

## Constraints

- Do NOT use `after_create` callbacks for business logic โ€” use a service instead
- Do NOT put service object logic in the controller
- Do NOT rescue `StandardError` โ€” rescue specific exceptions only
- Do NOT use `ActiveRecord::Base.transaction` inside a service; let the caller decide
SKILL.md โ€” Constraints Section

5. Handle the Common Variations

Think through the most frequent variations of the task and address them explicitly.

## Variations

### If the service needs to call an external API
Inject the HTTP client as a dependency for testability:
```ruby
def initialize(..., http_client: Faraday.new)
```

### If the service is a background job wrapper
Separate the job class from the service class. The job calls the service.
SKILL.md โ€” Variations Section

Recommended Section Order

SectionContentRequired?
Intro paragraphOne sentence: what this skill producesโœ…
ConventionNaming, location, structure rulesโœ…
TemplateActual code to followโœ… for code skills
ConstraintsWhat NOT to doRecommended
VariationsCommon edge casesWhen relevant
ExamplesBefore/after, usage in contextOptional but powerful
NotesGotchas, further reading, decisionsOptional

Progressive Disclosure

Skills have a three-layer loading system. Understanding it lets you write skills that are fast for simple cases and detailed for complex ones โ€” without bloating Claude's context.

The Three Layers in Practice

Layer 1: Description (~50โ€“150 words)

Always in context. Used to decide whether to trigger. Write this to be maximally useful as a trigger signal. Every word counts โ€” don't waste it on what could go in the body.

Layer 2: SKILL.md Body (under 500 lines ideal)

Loaded when triggered. Contains the main instructions, templates, and conventions. Should be complete enough that Claude can do the task without reading any reference files in the simple case.

Layer 3: Reference Files (unlimited)

In references/ subdirectory. Loaded only when Claude explicitly needs them. Good for: framework-specific docs, large templates, lookup tables, API references.

When to Split Into Reference Files

Split into reference files when your SKILL.md is approaching 400โ€“500 lines, or when you have framework-specific content that only applies in certain cases:

# In SKILL.md body:
## Frontend Integration

If the resource needs Turbo Stream responses, see `references/hotwire.md`.
If using React instead, see `references/react-integration.md`.

# Then the reference files contain the details:
references/
โ”œโ”€โ”€ hotwire.md       # Full Turbo Stream patterns
โ””โ”€โ”€ react-integration.md  # React-specific patterns
Progressive Disclosure Pattern
๐Ÿ’ก The Test

Ask yourself: "For the most common version of this task, does Claude need this content?" If yes โ†’ body. If only sometimes โ†’ reference file. If never for Claude, but useful as human docs โ†’ notes file.

Organizing Large Reference Files

If a reference file is over 300 lines, add a table of contents at the top. Claude can read the TOC and jump to the relevant section:

# references/hotwire.md

# Hotwire / Turbo Reference

## Table of Contents

1. [Turbo Frames](#turbo-frames) โ€” line 30
2. [Turbo Streams from controllers](#controller-streams) โ€” line 95
3. [Turbo Streams from jobs](#job-streams) โ€” line 150
4. [Stimulus controllers](#stimulus) โ€” line 200
5. [Common patterns](#patterns) โ€” line 260

---

## Turbo Frames
...
references/hotwire.md

Domain Organization Pattern

When a skill covers multiple frameworks or environments, organize reference files by domain โ€” Claude reads only what it needs:

rails-test-generation/
โ”œโ”€โ”€ SKILL.md              # Main: choose between rspec / minitest
โ””โ”€โ”€ references/
    โ”œโ”€โ”€ rspec.md           # All RSpec patterns
    โ”œโ”€โ”€ minitest.md        # All Minitest patterns
    โ””โ”€โ”€ factories.md       # FactoryBot shared reference
Domain Organization

In SKILL.md, include a selection block:

## Test Framework

Check the project for which framework is in use:
- If `spec/` directory exists โ†’ RSpec. Read `references/rspec.md`.
- If `test/` directory exists โ†’ Minitest. Read `references/minitest.md`.
- If unclear, default to RSpec and ask the user to confirm.
SKILL.md โ€” Framework Selection

Bundled Resources

Beyond the SKILL.md, you can bundle scripts, reference docs, and asset files. These make your skill a self-contained system rather than a set of instructions.

The Three Resource Types

1

Scripts (scripts/)

Executable code for deterministic tasks. Shell scripts, Ruby scripts, Python scripts. Claude can run these directly via bash without loading their content into context โ€” great for large generators.

2

References (references/)

Documentation, API references, framework guides, decision tables. Claude reads these when it needs them. Ideal for content that's too long for the SKILL.md body.

3

Assets (assets/)

Template files, icons, fonts, boilerplate code files. Claude can copy these into the user's project or use them as starting points.

Script Pattern Example

Scripts are powerful for scaffolding workflows. Instead of having Claude generate code step by step, it can run a script:

# scripts/scaffold_resource.sh
#!/bin/bash
# Usage: ./scaffold_resource.sh ResourceName
RESOURCE=$1
LOWER=$(echo "$RESOURCE" | tr '[:upper:]' '[:lower:]')

# Generate model
rails generate model "$RESOURCE"

# Create service directory
mkdir -p "app/services/${LOWER}"

# Create spec directory
mkdir -p "spec/services/${LOWER}"

echo "โœ“ Scaffolded $RESOURCE"
scripts/scaffold_resource.sh

In SKILL.md, reference it:

## Scaffolding a New Resource

Run the scaffold script to set up the file structure:

```bash
bash scripts/scaffold_resource.sh ResourceName
```

Then follow the convention below to fill in the generated files.
SKILL.md โ€” Script Reference

Asset Template Pattern

# assets/service_object_template.rb.erb
class <%= class_name %>
  Result = Struct.new(:success?, :data, :error, keyword_init: true)

  def initialize(<%= args %>)
<% args.split(',').each do |arg| %>
    @<%= arg.strip %> = <%= arg.strip %>
<% end %>
  end

  def call
    # TODO: implement
    Result.new(success?: true, data: nil)
  end

  private

  attr_reader <%= args.split(',').map { |a| ":#{a.strip}" }.join(', ') %>
end
assets/service_object_template.rb
๐Ÿ’ก Keep Scripts Focused

Scripts work best for deterministic operations (file creation, directory setup, bash commands). For anything that requires judgment โ€” like deciding what goes in a service object โ€” keep the logic in the SKILL.md instructions where Claude can reason about it.

Reference File Best Practices

  • Name files descriptively: rspec-request-specs.md not testing.md
  • Add a TOC at the top of any reference file over 300 lines
  • Include line numbers in your TOC so Claude can jump to sections
  • Write reference files as "here's the pattern" not "here's how Claude should think"
  • Include copy-paste-ready code blocks โ€” not prose descriptions of code

Rails-Specific Skills

Rails apps have well-defined layers and conventions that make for excellent skills. Here are the most high-value skills to build for a typical Rails project.

High-Value Rails Skills to Write

๐Ÿ”ง Service Objects

Define your naming convention, Result object structure, file location, and error handling approach. This is usually the highest-ROI skill for any Rails team.

๐Ÿงช RSpec Patterns

Factory structure, shared examples, describe/context/it conventions, what to test in models vs request specs vs unit specs.

โšก Hotwire / Turbo Patterns

Turbo Frame usage, Turbo Stream formats, Stimulus controller conventions, how to handle forms and modals.

๐Ÿ—„๏ธ Migration Patterns

Reversible migrations, safe column removal, index naming, foreign key conventions, null constraints.

๐Ÿ” Authorization Patterns

Your Pundit policy structure (or CanCanCan, or whatever you use), how policies are organized, what gets checked where.

๐Ÿ“ฌ Background Jobs

Sidekiq class structure, retry behavior, idempotency expectations, how jobs relate to service objects.

A Complete Rails Migration Skill

Here's a fully worked example of a migration skill:

---
name: rails-migrations
description: Write safe, reversible ActiveRecord migrations for Rails
  apps. Use whenever the user asks to add a column, remove a
  column, create a table, add an index, rename a column, or
  change a schema in any way. Also trigger for "update the
  schema", "need a migration", or "add field to model".
---

# Rails Migrations

Generate safe, reversible, zero-downtime-friendly migrations.

## Conventions

- Always use `change` method (reversible) unless you need `up`/`down`
- Name migrations descriptively: `AddStatusToOrders`, not `UpdateOrders`
- Always add `null: false` with a `default:` when adding columns to existing tables

## Adding a Column

```ruby
class AddStatusToOrders < ActiveRecord::Migration[7.1]
  def change
    add_column :orders, :status, :string, null: false, default: "pending"
    add_index :orders, :status
  end
end
```

## Removing a Column

ALWAYS use `ignored_columns` in the model first, deploy, then remove:

```ruby
# Step 1: Add to model (deploy first)
class Order < ApplicationRecord
  self.ignored_columns = ["deprecated_field"]
end

# Step 2: After deploy, create this migration
class RemoveDeprecatedFieldFromOrders < ActiveRecord::Migration[7.1]
  def change
    remove_column :orders, :deprecated_field, :string
  end
end
```

## Constraints

- Never add NOT NULL column without a default to a populated table
- Never use `execute` for data migrations โ€” use a separate Rake task
- Never add an index without `algorithm: :concurrently` on large tables in production
Full Migration SKILL.md

Project-Specific Customization

The best skills are tailored to your specific project. Add a section that captures your project's unique decisions:

## This Project's Conventions

- We use UUIDs as primary keys (`id: :uuid` in all create_table calls)
- All timestamps use `timestamptz` via the `ar-timestamptz` gem
- Soft-delete columns are named `discarded_at` (we use Discard gem)
- Enum columns use `integer` type, not `string`
Project-Specific Section
๐Ÿ’ก Start With One Skill, Not Ten

Don't try to capture your entire codebase in skills on day one. Pick your most painful inconsistency โ€” the pattern Claude gets wrong most often โ€” and write one skill for it. Then add more as you notice Claude going off-script.

Common Pitfalls

These are the mistakes that make skills not work โ€” either Claude ignores the skill, or follows it inconsistently. Knowing them in advance will save you a lot of debugging.

Pitfall 1: The Vague Description

โŒ Problem

Description is too short or generic: "Helps with Rails development". Claude sees dozens of other skills, all of which also look like they could apply. Your skill never triggers.

โœ… Fix

Be specific about the task type, include synonym phrases the user might say, and explicitly list the contexts that should trigger the skill. Aim for 60โ€“120 words in your description.

Pitfall 2: Principles Instead of Rules

โŒ Problem

"Follow clean architecture principles and keep services focused." Claude agrees with this in the abstract but interprets it differently each time. You get inconsistent output.

โœ… Fix

Replace every abstract principle with a concrete, verifiable rule. "Keep services focused" โ†’ "Each service class exposes exactly one public method: .call. If you need two operations, create two classes."

Pitfall 3: No Negative Constraints

โŒ Problem

You describe what to do, but not what to avoid. Claude reverts to its defaults for the things you didn't mention โ€” which are often the things you care most about.

โœ… Fix

Add a "Constraints" or "Do Not" section. Think: what does Claude do wrong when you don't have this skill? Those are your constraints. "Never use callbacks for business logic. Never rescue StandardError."

Pitfall 4: SKILL.md Too Long

โŒ Problem

800-line SKILL.md covering every edge case. The key rules get buried. Claude weights early content more heavily โ€” critical instructions at line 700 get less attention than at line 20.

โœ… Fix

Keep SKILL.md under 500 lines. Move detailed edge-case docs to references/. Put the most critical rules first. Use progressive disclosure โ€” common case in the body, special cases in reference files.

Pitfall 5: Overlapping Skills

โŒ Problem

You have a "Rails services" skill and a "Rails business logic" skill with overlapping descriptions. Claude reads both and gets conflicting instructions, or reads neither because it's unclear which applies.

โœ… Fix

Give each skill a clearly distinct domain. If two skills overlap, merge them into one or add exclusion language: "This skill does NOT cover background jobs โ€” for that, see the rails-jobs skill."

Pitfall 6: Writing for Yourself, Not for Claude

โŒ Problem

The SKILL.md is written like documentation for a human developer โ€” narrative prose explaining the history of a decision. Claude has to parse it to find the actionable parts.

โœ… Fix

Write for Claude as the reader: use imperative voice ("Do X. Don't do Y."), put templates in code blocks, and eliminate narrative. You can add a "Background" section at the bottom for human readers, but keep it separate from the instructions.

Quick Diagnostic Checklist

If your skill isn't working, check these things in order:

  • Does the description include specific trigger phrases the user actually says?
  • Does the SKILL.md body have concrete templates, not just abstract principles?
  • Is there a "Do Not" or "Constraints" section?
  • Are all file paths and naming conventions explicitly stated?
  • Is the SKILL.md under 500 lines with critical content near the top?
  • Is there any ambiguity that would allow multiple interpretations?

Full Examples

Two complete, copy-paste-ready skills you can install immediately or use as templates for your own.

Example 1: Rails Service Objects Skill

---
name: rails-service-objects
description: Generate service objects and operation classes for Rails apps.
  Use this skill whenever the user asks to create a service object,
  extract controller logic into a service, add a business logic class,
  or build an operation/command/interactor. Also trigger for any
  mention of "service layer", "PORO service", "fat controller", or
  "extract this into a class".
---

# Rails Service Objects

Generate single-responsibility service objects for encapsulating
business logic outside of models and controllers.

## Convention

| Attribute | Rule |
|-----------|------|
| Location  | `app/services/` โ€” subdirectory if domain-grouped |
| Naming    | VerbNoun: `CreateOrder`, `SendInvoice`, `CancelSubscription` |
| Interface | Single public method: `.call` or `#call` |
| Return    | `Result` struct, not raw values |
| Spec path | `spec/services/` mirroring `app/services/` |

## Template

```ruby
# app/services/verb_noun.rb
class VerbNoun
  Result = Data.define(:success, :value, :error) do
    def success? = success
    def failure? = !success
  end

  def self.call(...) = new(...).call

  def initialize(required_arg:, optional_arg: nil)
    @required_arg = required_arg
    @optional_arg = optional_arg
  end

  def call
    validate!
    result = perform
    Result.new(success: true, value: result, error: nil)
  rescue MyApp::Error => e
    Result.new(success: false, value: nil, error: e.message)
  end

  private

  attr_reader :required_arg, :optional_arg

  def validate!
    raise MyApp::Error, "required_arg is blank" if required_arg.blank?
  end

  def perform
    # core logic here
  end
end
```

## Usage (in controller)

```ruby
def create
  result = CreateOrder.call(user: current_user, params: order_params)

  if result.success?
    redirect_to result.value, notice: "Order created"
  else
    flash.now[:error] = result.error
    render :new, status: :unprocessable_entity
  end
end
```

## RSpec Template

```ruby
# spec/services/verb_noun_spec.rb
RSpec.describe VerbNoun do
  describe ".call" do
    subject(:result) { described_class.call(**params) }

    let(:params) { { required_arg: "value" } }

    context "when successful" do
      it { is_expected.to be_success }
      it { expect(result.value).to eq(expected_value) }
    end

    context "when required_arg is blank" do
      let(:params) { { required_arg: "" } }
      it { is_expected.to be_failure }
      it { expect(result.error).to include("blank") }
    end
  end
end
```

## Constraints

- Never put service logic in model callbacks or controller actions
- Never rescue `StandardError` โ€” rescue specific exception classes
- Never call `save!` on AR objects inside a service; return data and let
  the caller persist if needed (keeps services testable without DB)
- Never add more than one public method โ€” split into multiple services
rails-service-objects/SKILL.md

Example 2: General Coding Convention Skill

---
name: project-conventions
description: Project-specific coding conventions for this codebase.
  Always use this skill when writing any code for this project โ€”
  it defines the naming, structure, style, and tooling choices
  that override Claude's defaults. Trigger for any code generation,
  refactoring, or editing task in this repository.
---

# Project Conventions

These conventions override Claude's defaults for all code in this repo.

## Ruby Style

- Ruby 3.3+, Rails 7.2+
- Frozen string literals on all new files: `# frozen_string_literal: true`
- Use `Data.define` for value objects (Ruby 3.2+), not `Struct`
- Prefer pattern matching (`case/in`) over chains of `if/elsif`
- `private` methods alphabetically sorted

## Naming

- Models: singular noun (`Order`, `User`)
- Controllers: plural resource (`OrdersController`)
- Services: VerbNoun (`CreateOrder`)
- Jobs: NounVerb + `Job` suffix (`OrderCreatedJob`)
- Concerns: adjective or noun (`Discardable`, `Auditable`)

## Testing

- RSpec only (no Minitest)
- FactoryBot for fixtures โ€” never `Model.create` in specs directly
- All factories in `spec/factories/` โ€” one file per model
- No `let!` โ€” use `let` and `before` instead
- Descriptive `it` blocks: use present tense ("returns X", not "should return X")

## Tooling

- Rubocop with standard gem โ€” never disable cops inline
- Brakeman for security โ€” fix all warnings before merging
- Bundler Audit in CI

## What NOT to Do

- No `puts` or `p` โ€” use `Rails.logger`
- No `rescue Exception` โ€” ever
- No inline SQL โ€” use AR query interface or Arel
- No `Time.now` โ€” use `Time.current` (timezone-aware)
- No `Date.today` โ€” use `Date.current`
project-conventions/SKILL.md

Testing & Improving Skills

A skill draft is just a hypothesis. The real work is figuring out where it breaks down and tightening the instructions until Claude consistently does exactly what you want.

How to Test a Skill

1

Write 5โ€“10 test prompts

Cover the main use case, edge cases, and tricky variations. Write prompts the way users would actually phrase them โ€” not like test cases.

2

Run each prompt with the skill installed

Don't tell Claude to use the skill โ€” if it's working, it should trigger automatically based on the description.

3

Check: did it trigger?

If Claude didn't use your skill when it should have, the description needs more trigger phrases or more specificity. If it triggered when it shouldn't, broaden the exclusions.

4

Check: is the output correct?

Verify naming, file paths, template structure, constraints. Make a list of everything wrong.

5

Update the skill, repeat

Each iteration, fix the most common failure mode. Don't try to fix everything at once.

Diagnosing Failures

SymptomLikely CauseFix
Skill never triggers Description too vague or missing trigger phrases Add specific phrases, task types, and contexts to description
Skill triggers for wrong things Description too broad Add exclusion language: "Does NOT apply to X"
Wrong naming / file paths Convention section not explicit enough Add a table with explicit rules, or add a constraints section
Template not followed Instructions too prose-heavy Move to imperative voice, put template in a code block
Inconsistent output Ambiguous instructions, multiple valid interpretations Add explicit negative constraints, remove any ambiguity
Ignores edge cases Edge cases not covered in skill Add a Variations or Edge Cases section

The Improvement Loop

# Skill improvement is a loop, not a one-shot

while skill_not_good_enough:
  run_test_prompts(skill)
  failures = identify_failures()

  if failures.include?(:trigger_failure):
    improve_description()
  
  if failures.include?(:wrong_output):
    improve_instructions()
    add_constraints()
    clarify_template()
  
  if skill.body.length > 500:
    extract_to_reference_files()
Pseudocode
๐Ÿ’ก The "One Failure at a Time" Rule

When you find multiple things wrong, fix the most common failure mode first. Re-test before fixing the next one. Fixing everything at once makes it impossible to know which change helped.

Evolving Skills Over Time

Your skill should evolve as your codebase and preferences evolve. Good triggers to update a skill:

  • You catch Claude doing something wrong that it keeps repeating
  • You make a project-wide refactor that changes a convention
  • You upgrade a gem and the API or patterns change
  • A teammate says "Claude always generates the tests wrong"
  • You add a new layer to your architecture (e.g., start using presenters)
๐Ÿ’ก Keep Skills in Version Control

Store your skills/ directory in your project repo. This way the team shares the same skills, skill updates are reviewed in PRs, and skill history is tracked alongside the code it governs.

Quick Reference

Everything you need on one page. Bookmark this section and use it while writing or reviewing skills.

SKILL.md Checklist

  • Description: 60โ€“120 words, includes what it does + when to trigger + synonym phrases
  • Name: Stable kebab-case identifier, won't change
  • Intro: One sentence saying what this skill produces
  • Convention section: Explicit naming, file path, structure rules
  • Template: Copy-paste-ready code block (for code skills)
  • Constraints: At least 3 "never do X" rules
  • Edge cases: Variations section for common alternatives
  • Length: Under 500 lines, critical content in first 200

Description Formula

description: [WHAT it does in one clear sentence.]
  [WHEN to trigger: specific task types and contexts.]
  [ALSO trigger for: synonym phrases and natural language variants.]
Formula

Instruction Writing Rules

RuleInstead ofWrite
Imperative voice"Services should be...""Put services in app/services/"
Concrete over abstract"Keep methods short""Methods max 10 lines, max 2 args"
Explicit paths"In the services folder""`app/services/` โ€” subdirectoried by domain"
Templates in code blocksProse description of structureActual Ruby/JS/etc. code to copy
Negative constraints(nothing about what NOT to do)"Never rescue StandardError. Never..."

File Structure Reference

my-skill/
โ”œโ”€โ”€ SKILL.md               # Required. Main instructions.
โ”œโ”€โ”€ scripts/               # Optional. Runnable shell/ruby scripts.
โ”‚   โ””โ”€โ”€ setup.sh
โ”œโ”€โ”€ references/            # Optional. Large docs Claude reads on demand.
โ”‚   โ”œโ”€โ”€ framework-a.md
โ”‚   โ””โ”€โ”€ framework-b.md
โ””โ”€โ”€ assets/                # Optional. Template files, boilerplate.
    โ””โ”€โ”€ template.rb
Structure

Trigger Failure Diagnosis

ProblemQuick Fix
Skill never triggersAdd explicit trigger phrases to description
Triggers for wrong requestsAdd "Does NOT apply to X" exclusions
Wrong output structureAdd or improve the template code block
Wrong file paths/namesAdd a Convention table with explicit paths
Ignores constraintsAdd a "Never do X" constraints section
Inconsistent outputLook for ambiguous instructions and make them specific

Top Rails Skills to Write

rails-service-objects rails-migrations rails-rspec-patterns rails-factories hotwire-turbo stimulus-controllers sidekiq-jobs pundit-policies project-conventions


The Golden Rules

1. Descriptions Trigger, Bodies Instruct

Everything about "when to use" goes in the description. Everything about "how to do it" goes in the body. Never mix them.

2. Show the Template

A code template is worth ten paragraphs of prose. Always include at least one copy-paste-ready example for code-generation skills.

3. Constrain the Defaults

Claude will fall back to its defaults for anything you don't mention. The most important part of a skill is often the "Do NOT" section โ€” it's where you override those defaults.

4. Iterate, Don't Perfect

Ship a 70% skill and improve it as you catch failures. A skill in production catching real failures is more valuable than a perfect skill that took three weeks to write.