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.
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.
You send a message
"Add a service object for Stripe billing to my Rails app"
Claude scans skill descriptions
It reads the short description of each installed skill to decide which are relevant.
Claude reads matching SKILL.md files
The full instruction body is loaded into context โ conventions, patterns, examples.
Claude executes with your patterns
Output follows the exact conventions you defined, not Claude's defaults.
What Skills Are Good For
| Great Use Case | Why It Works | Example |
|---|---|---|
| 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
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.
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.
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
| Field | Required? | Purpose |
|---|---|---|
name | โ Yes | Identifier. Use kebab-case: rails-factories |
description | โ Yes | Triggering signal โ the most critical field |
compatibility | โ Rarely | Required tools, node version, etc. |
license | โ Optional | For shared/public skills |
| Body content | โ Yes | The actual instructions |
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 templatesStructure
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
description: Helps with Rails development.
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.
description: A skill for creating RSpec
tests in Rails projects.
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
| Pattern | Example 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" |
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.
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.
Follow clean code principles.
Use meaningful names.
Keep methods short.
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 decideSKILL.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
| Section | Content | Required? |
|---|---|---|
| Intro paragraph | One sentence: what this skill produces | โ |
| Convention | Naming, location, structure rules | โ |
| Template | Actual code to follow | โ for code skills |
| Constraints | What NOT to do | Recommended |
| Variations | Common edge cases | When relevant |
| Examples | Before/after, usage in context | Optional but powerful |
| Notes | Gotchas, further reading, decisions | Optional |
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 patternsProgressive Disclosure Pattern
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 referenceDomain 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
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.
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.
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(', ') %> endassets/service_object_template.rb
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.mdnottesting.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 productionFull 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
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
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.
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
"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.
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
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.
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
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.
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
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.
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
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.
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 servicesrails-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
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.
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.
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.
Check: is the output correct?
Verify naming, file paths, template structure, constraints. Make a list of everything wrong.
Update the skill, repeat
Each iteration, fix the most common failure mode. Don't try to fix everything at once.
Diagnosing Failures
| Symptom | Likely Cause | Fix |
|---|---|---|
| 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
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)
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
| Rule | Instead of | Write |
|---|---|---|
| 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 blocks | Prose description of structure | Actual 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.rbStructure
Trigger Failure Diagnosis
| Problem | Quick Fix |
|---|---|
| Skill never triggers | Add explicit trigger phrases to description |
| Triggers for wrong requests | Add "Does NOT apply to X" exclusions |
| Wrong output structure | Add or improve the template code block |
| Wrong file paths/names | Add a Convention table with explicit paths |
| Ignores constraints | Add a "Never do X" constraints section |
| Inconsistent output | Look 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
Everything about "when to use" goes in the description. Everything about "how to do it" goes in the body. Never mix them.
A code template is worth ten paragraphs of prose. Always include at least one copy-paste-ready example for code-generation skills.
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.
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.