Writing Agent Skills: Lessons Learned and Best Practices from Production Use
Published Updated 13 min read
Agent SkillsAI AgentsAI EngineeringSoftware Architecture

Introduction: Agent Skills as Engineered Systems
Agent Skill development is not merely documentation writing. Although a Skill is primarily expressed in natural language, it is an engineered system with measurable runtime effects. It directly influences how an AI agent behaves, including tool selection, context consumption, token costs, retrieval behavior, factual reliability, and reproducibility.
A poorly designed Skill can produce correct output while increasing inference costs or introducing security risks. Authoring production-grade Agent Skills must therefore be approached simultaneously as an instruction-design, performance-engineering, and security-engineering problem.
This article is an experience report. It distills practical lessons learned from designing, operating, and refining Agent Skills in production across heterogeneous AI coding tools such as Claude Code and GitHub Copilot. Some of these observations are corroborated by findings from published studies, which are cited at the relevant points. The principles should therefore be understood as engineering observations informed by both practical experience and existing research, rather than as universal claims about agent behavior.
Design Principle #1: Optimize Frontmatter for Skill Discovery
Agent Skills rely on progressive disclosure. At startup, a compatible client loads the name and description fields from each Skill’s YAML frontmatter into the context. The full SKILL.md body is read only when the agent determines that the Skill is relevant to the current task.
The Agent Skills specification constrains the description field to at most 1,024 characters and defines its purpose in one sentence:
Describes what the skill does and when to use it.
In practice, I repeatedly observed that Skills generated directly by AI coding assistants often dedicate most of the available description space to explaining what the Skill does. In contrast, activation conditions—the part of the description that explains when the Skill should be used—are frequently under-specified.
Consider the generated example in Listing 1.
---
name: java-static-code-analysis
description: >-
Runs Checkstyle, PMD, and SpotBugs over the Java sources with the
project's rule sets, aggregates the findings into one report grouped by
severity and rule, explains each rule with its rationale, and links every
finding to the affected class and line. Use this skill when source files
have been modified.
---A generated description. Nearly every line explains what the Skill does, down to the names of the analysers it runs. The activation condition appears only at the end and remains relatively broad compared with the Java-specific nature of the Skill.
Listing 2 shows a more effective version.
---
name: java-static-code-analysis
description: >-
Performs static code analysis on Java source code. Use after Java source
files are created, modified, or reviewed, and before reporting a task
complete; also when asked to check code quality, find code smells, or look
for rule violations. Analysis only; never edits code.
---The same Skill, described for discovery. The description immediately establishes the capability and domain, followed by the activation conditions and trigger terms; the capability summary focuses on the core function rather than enumerating the underlying analysers, which are less important for initial discovery in this example; and the last line draws a behavioral boundary: the Skill performs analysis but does not edit code.
Because the description is the primary signal available during discovery, its wording directly affects whether a Skill is considered relevant. A useful rule of thumb is to optimize descriptions for discovery while preserving enough information to distinguish them from other Skills (see Design Principle #2: Make Skill Descriptions Distinguishable).
The official guidance supports the underlying rationale: descriptions should be concise, clearly scoped, and front-load the key use case and trigger words needed for reliable matching (see how ChatGPT and Codex use skills(opens in a new tab)).
Design Principle #2: Make Skill Descriptions Distinguishable
In production use with Claude Code (Opus 5 and Fable 5), I observed that Skills with identical or highly similar descriptions were not always invoked together when both were relevant to a task.
Consider the pair of descriptions in Listing 3.
# accessibility-checker/SKILL.md
name: accessibility-checker
description: >-
Use this skill every time you have just created, edited and/or reviewed
FRONTEND source files, BEFORE presenting the result as done.
---
# seo-checker/SKILL.md
name: seo-checker
description: >-
Use this skill every time you have just created, edited and/or reviewed
FRONTEND source files, BEFORE presenting the result as done.Two Skills whose descriptions expose the same trigger conditions and nothing else. At discovery time, only their names differ.
To explore how Claude Code behaves when multiple Skills expose identical discovery signals, I intentionally assigned the same description to both Skills. In this configuration, only the seo-checker Skill was invoked, even though the trigger conditions of both Skills were satisfied. I asked Claude Code why the accessibility-checker Skill had not also been invoked. It explained that the two Skills had identical descriptions and that it had therefore selected only one. When I asked how to ensure that both Skills were always invoked, Claude Code suggested implementing a dedicated hook.
This explanation should not be interpreted as documentation of Claude Code’s internal Skill-selection algorithm. It is an observation from my testing and the explanation provided by the model in that particular interaction. Nevertheless, it motivated the change illustrated in Listing 4.
# accessibility-checker/SKILL.md
name: accessibility-checker
description: >-
Checks source code for accessibility compliance. Use after frontend
source files are created, modified, or reviewed, and before reporting
the task complete. Covers accessibility checks and WCAG-related issues.
---
# seo-checker/SKILL.md
name: seo-checker
description: >-
Checks source code for SEO best practices. Use after frontend source
files are created, modified, or reviewed, and before reporting the task
complete. Covers metadata, structured data, crawlability, and other SEO
issues.The same two Skills with distinguishable descriptions: identical timing, different purpose, and capability summaries that describe what each Skill covers.
The descriptions now provide the model with both timing and purpose:
- one Skill addresses accessibility;
- one Skill addresses SEO;
- both can be relevant to the same change.
Following Design Principle #1 helps make individual Skills discoverable. Design Principle #2 complements it by ensuring that Skill descriptions remain distinguishable from one another when multiple Skills are relevant to the same task. This principle is derived from the observations described above and from the documented role of descriptions in Skill selection. Anthropic notes that descriptions are critical for Skill selection and are used to choose the appropriate Skill from potentially hundreds of available Skills (see Skill authoring best practices(opens in a new tab)).
Design Principle #3: Keep Each Skill Focused on One Job
The Single Responsibility Principle applies to Agent Skills just as it does to software components in a well-structured architecture. A useful interpretation of the principle is that a component should have one reason to change. For an Agent Skill, this means that a Skill should focus on a single task or concern rather than combining responsibilities that evolve independently.
This also connects to Parnas’s principle of information hiding: modules should be decomposed around knowledge that is likely to change independently, so that changes in one part do not unnecessarily propagate to others. The same reasoning applies to Skills. An SEO policy may change independently of an accessibility standard, while an Open Graph requirement may change independently of both. Combining these concerns in one Skill couples responsibilities that have different reasons to change.
During experimentation with Skills that attempted to cover multiple responsibilities, I observed several practical drawbacks:
- More likely to contain conflicting instructions
- More difficult to maintain as different concerns evolved over time
- More difficult to optimize because unrelated instructions and reference material consumed context together
These effects are not simply a consequence of file size. The underlying problem is coupling: unrelated responsibilities compete for the same instructions, context, and maintenance lifecycle.
The same idea appears in official guidance. OpenAI recommends treating Skills as composable building blocks and notes that:
Skills often work best as small building blocks you can mix and match, rather than one massive end-to-end skill.
The Agent Skills documentation makes the same point in terms of scope. It describes a Skill as a coherent unit of work that composes well with other Skills, and it warns in both directions: Skills scoped too narrowly force several Skills to load for a single task, while Skills scoped too broadly become hard to activate precisely (see Best practices for skill creators(opens in a new tab)).
A simpler heuristic can help identify this during design. When a Skill’s SKILL.md repeatedly requires terms such as and, or, if, otherwise, or depending on to coordinate its instructions, this may indicate that the Skill is combining multiple responsibilities. This is only a heuristic; the stronger test is whether the Skill has multiple independent reasons to change.
Prefer focused Skills such as:
seo-checkeraccessibility-checkeropen-graph-validator
over a monolithic Skill such as:
frontend-quality-assurance
The smaller Skills can then be composed when multiple concerns are relevant. An agent can invoke an SEO Skill, an accessibility Skill, and an Open Graph validation Skill independently rather than loading and reasoning through unrelated responsibilities contained in a single Skill.
This separation improves independent maintenance while remaining composable at the workflow level. Decompose Skills when their responsibilities, knowledge, or stakeholders evolve independently, and compose them at the workflow level when multiple concerns are required.
This follows the same underlying idea as the Unix philosophy: do one thing and do it well.
Design Principle #4: Apply Progressive Disclosure Systematically
One of the most important architectural patterns in Agent Skill design is progressive disclosure: instead of loading every document by default, load specific references only when they are contextually necessary.
Loading excessive information consumes more context, increasing token usage and latency while reducing the context available for the task itself. More importantly, additional context can make it harder for models to reliably locate and use relevant information. Liu et al. (2024) found that language-model performance can degrade substantially as the position of relevant information changes within long contexts, with performance often lowest when relevant information appears in the middle of the context. These findings provide empirical support for minimizing unnecessary context and placing critical information where it is more reliably accessible.
Hallucination risk is a separate potential consequence of unnecessary or distracting context and should not be treated as a direct consequence of context length alone.
Provide only enough information for the model to decide whether additional material is required. A practical pattern is to keep the reference material in its own directory beside SKILL.md:
- my-skill/
- SKILL.mdInstructions and the routing index
- references/
- api-specification.md
- architecture-principles.md
- coding-standards.md
- domain-model.md
The SKILL.md body should contain a lightweight routing index: list the available reference files together with a one-line description of their contents and the conditions under which each should be read, as in Listing 5.
## References
- references/api-specification.md
Endpoint contracts, payloads, and error semantics.
Read when implementing API integrations.
- references/architecture-principles.md
Layering rules and the decisions behind them.
Read when making architecture decisions.
- references/coding-standards.md
Naming, formatting, and review conventions.
Read before generating source code.
- references/domain-model.md
Entities, invariants, and their relationships.
Read when modeling the domain.A routing index in the Skill body. Each entry gives the relative path of a reference file, summarizes its contents in one line, and states the condition under which the agent should read it.
Keep references one level deep from SKILL.md. Avoid chains in which one reference file points to another reference file that must also be loaded. A shallow retrieval hierarchy makes dependencies explicit and keeps the Skill’s information structure predictable. For longer reference files, include a table of contents near the beginning so the agent can locate relevant sections efficiently. Figure 1 shows the resulting shape, and the chain it is designed to exclude.
This pattern resembles how llms.txt(opens in a new tab) files guide retrieval. The model first decides what information is needed and only then loads additional relevant documents.
The result is a system that uses context more selectively and can scale better as the Skill’s reference material grows.
Design Principle #5: Design for Graceful Degradation
AI runtimes frequently expose vendor-specific capabilities that are unavailable elsewhere. The challenge is not using these capabilities; it is ensuring that a Skill continues to function correctly when they are absent. The following example illustrates the principle using Claude Code’s context: fork frontmatter field.
Claude Code provides the vendor-specific frontmatter field context: fork to run a Skill in an isolated sub-agent, the mechanism the documentation-research Skill in A Vectorless RAG Architecture for Online Documentation relies on. The risk is not using this capability; it is making the Skill depend on it without a fallback. Listing 6 shows such a frontmatter.
---
name: documentation-research
description: >-
Use this skill to answer questions about the official Claude Code
documentation.
context: fork
agent: Explore
---Vendor-specific frontmatter. On Claude Code, the context and agent fields run the Skill in
a forked sub-agent; neither field is part of the portable specification.
The Skill body can provide a portable fallback, shown in Listing 7.
# Claude Code documentation
0. Precondition — isolation. If you can see the conversation history,
delegate steps 1–3 to a read-only sub-agent and report only its answer;
if no sub-agent is available, continue in place and accept the context
cost. If you cannot see the history, you are already isolated: continue.
1. Fetch the documentation index: https://code.claude.com/docs/llms.txt
2. Select the smallest set of pages that answers the question.
3. Fetch only those pages, then answer and cite them.The Skill body with a portable fallback. Step 0 tests an observable runtime condition, whether the conversation history is visible, instead of asking whether a frontmatter field was honored, and it ends in a degraded but correct path when no sub-agent exists either.
Vendor-specific frontmatter should therefore be treated as an optional optimization: useful when supported, but never a hidden dependency for the core workflow.
Design Principle #6: Use Determinism Where Appropriate
A mistake is applying the determinism test to a Skill as a whole rather than to its individual steps. Most of a Skill may be instruction-driven, while specific operations should be deterministic when their correctness can be mechanically established.
For example, instructing a model to “validate generated JSON against this schema” may produce confident but incorrect claims of compliance. Replacing that step with a deterministic validator moves the failure from implicit to explicit: malformed output is detected by the validator before it reaches the downstream system. The model generates the JSON; the validator determines whether it conforms to the schema.
This distinction is also consistent with research on intrinsic LLM self-correction. Huang et al. (2024) found that LLMs struggle to reliably improve their reasoning through self-correction without external feedback, and that performance can sometimes degrade after self-correction. A deterministic validator provides external feedback by delegating the correctness decision to an independent, mechanically checkable procedure.
Deterministic execution can also provide an efficiency benefit independent of reliability. When an operation can be delegated to an executable tool, the model does not need to read and reason over the tool’s implementation. The source code remains outside the model’s context; the model supplies the inputs and receives the tool’s output. This can reduce context consumption compared with asking the model to inspect a large body of source material and reproduce a deterministic transformation through natural-language reasoning.
For example, I encountered a production workflow that removed unwanted frontend artifacts from large websites. Asking the model to perform the cleanup directly required loading and reasoning over large HTML, CSS, and JavaScript inputs. A more efficient design was to invoke a cleanup tool with the website’s URL. The model decided when to run the operation, while the deterministic transformation occurred outside the context window.
A reliable validation workflow should also be bounded. After generating the output, run the deterministic validator. If validation fails, allow the model to self-correct and retry within a defined retry budget k. If validation still fails after k attempts, stop the workflow and surface the validator’s output rather than allowing the model to declare success. Figure 2 traces the resulting workflow.
The important architectural boundary is therefore not between “AI” and “non-AI” Skills. It is between steps where probabilistic reasoning is appropriate and steps where correctness can be decided deterministically.
This architecture combines the flexibility of natural-language reasoning with deterministic verification and bounded failure handling. It makes correctness checks reproducible and prevents the model from becoming the final authority on decisions that can be verified deterministically.
Security and Trust Boundaries of Agent Skills
Treating Agent Skills as architectural components introduces an important security consideration: a Skill is not merely documentation. It can contain executable scripts, tool permissions, and reference material that is subsequently interpreted by the agent. The allowed-tools field can pre-authorize tool use for a Skill, making the Skill’s tool permissions part of its security boundary.
The references/ directory introduces a second trust boundary. Reference files are not merely passive documentation when they are loaded into an agent’s context: their contents become part of the instructions and information the model uses to determine its next actions. This makes reference material an injection surface. References should therefore be reviewed for unintended instructions, untrusted content, and conflicts with the Skill’s intended behavior.
Tool permissions require similar care. allowed-tools should not be interpreted as a complete least-privilege mechanism. It grants the specified capabilities to the Skill; it does not by itself establish a restrictive security boundary around every other capability available to the agent. Where stronger restrictions are required, they should be enforced through the agent’s permission configuration and, where supported, explicit tool restrictions such as disallowed-tools.
A practical review should therefore treat a Skill as a small software component with multiple attack surfaces. The appropriate security boundary is the complete Skill package and the capabilities it can exercise, not SKILL.md alone.
Conclusion: Treat Skill Authoring as Engineering
An Agent Skill is not executable code. Instead, it is a structured artifact that defines when a capability should be used, what responsibility it owns, and which instructions, references, and tools the agent may rely on. The primary implementation medium has shifted from programming languages to natural language, but many underlying engineering principles remain familiar. Single responsibility, progressive disclosure, modular composition, and deterministic verification are as important for Agent Skills as they are for traditional software systems.
The important difference is that a Skill is interpreted rather than executed. A specification written in natural language provides no guarantee that an agent will interpret it as intended. This is precisely why reliability-critical operations should be delegated to deterministic tools and verified programmatically where appropriate. The engineering discipline remains familiar, but the medium introduces a fundamentally different source of uncertainty.
Early Agent Skills are often scaffolded or fully generated by AI tools. The resulting artifacts may satisfy the syntactic requirements of the Skill specification while under-specifying activation conditions or embedding unnecessary assumptions about a particular agent runtime.
As AI agents become increasingly integrated into enterprise workflows, Agent Skills should therefore be treated as architectural system components rather than as documentation. Organizations that apply the same engineering rigor to Skill design that they apply to software systems can build agent workflows that are more reliable, maintainable, and predictable in production.
References
- Agent Skills (n.d.) Agent Skills specification. Available at: https://agentskills.io/specification(opens in a new tab) (Accessed: 5 September 2026).
- Agent Skills (n.d.) Best practices for skill creators. Available at: https://agentskills.io/skill-creation/best-practices(opens in a new tab) (Accessed: 16 September 2026).
- Anthropic (n.d.) Extend Claude with skills, Claude Code documentation. Available at: https://code.claude.com/docs/en/skills(opens in a new tab) (Accessed: 5 September 2026).
- Anthropic (n.d.) Skill authoring best practices, Claude Developer Platform documentation. Available at: https://platform.claude.com/docs/en/agents-and-tools/agent-skills/best-practices(opens in a new tab) (Accessed: 16 September 2026).
- Howard, J. (2024) The /llms.txt file. Available at: https://llmstxt.org/(opens in a new tab) (Accessed: 5 September 2026).
- Huang, J., Chen, X., Mishra, S., Zheng, H.S., Yu, A.W., Song, X. and Zhou, D. (2024) ‘Large Language Models Cannot Self-Correct Reasoning Yet’, International Conference on Learning Representations (ICLR 2024), Vienna, 7–11 May. Available at: https://arxiv.org/abs/2310.01798(opens in a new tab) (Accessed: 5 September 2026).
- Liu, N.F., Lin, K., Hewitt, J., Paranjape, A., Bevilacqua, M., Petroni, F. and Liang, P. (2024) ‘Lost in the Middle: How Language Models Use Long Contexts’, Transactions of the Association for Computational Linguistics, 12, pp. 157–173. Available at: https://doi.org/10.1162/tacl_a_00638(opens in a new tab) (Accessed: 5 September 2026).
- OpenAI (n.d.) Build skills, ChatGPT documentation. Available at: https://learn.chatgpt.com/docs/build-skills(opens in a new tab) (Accessed: 5 September 2026).
- OpenAI (2026) Using skills, OpenAI Academy. Available at: https://openai.com/academy/skills/(opens in a new tab) (Accessed: 16 September 2026).
Share this article