Writing Better Agent Skills: Best Practices from Real-World Engineering
DEV Community

Writing Better Agent Skills: Best Practices from Real-World Engineering

How to turn domain knowledge, workflows, scripts, and engineering rules into reliable capabilities for AI agents.

AI agents are becoming part of everyday software development. They can explore codebases, implement features, run tests, investigate incidents, and interact with external systems.

But there is a problem that becomes increasingly obvious as projects grow: giving an agent access to a codebase does not mean giving it the knowledge required to work correctly within that codebase.

The agent might understand React, Go, REST APIs, databases, and distributed systems. Yet it may not know your application's business rules, your team's architectural decisions, the behavior of a particular WebSocket connection, or the mistakes that have already caused production incidents.

This is where agent skills become valuable. I've been exploring ways to make AI agents more consistent across real engineering workflows. My biggest takeaway is that skills deserve much more attention than they usually receive.

A skill is not just a collection of instructions. It can become an executable part of your engineering process.

In this article, I'll explain how I approach skill design, how to organize domain knowledge, when to use scripts instead of repeatedly calling tools, and how to make sure agents actually follow the rules we define. The examples use the open Agent Skills format as a reference.

1. Understand what a skill is supposed to do

An Agent Skill is a reusable package of instructions and resources that teaches an agent how to perform a particular class of tasks.

A typical skill looks like this:

fund-analysis/
β”œβ”€β”€ SKILL.md
β”œβ”€β”€ scripts/
β”‚   └── fetch-fund-data.ts
β”œβ”€β”€ references/
β”‚   └── fund-domain.md
└── assets/
    └── report-template.md

The SKILL.md file is the entry point. It describes when the skill should be used and provides the instructions the agent needs to follow. Supporting scripts, references, and assets can provide additional capabilities.

The important distinction is that a skill should not merely describe a subject. It should help the agent perform a task correctly and consistently.

For example, a weak skill might say:

Follow our fund analysis best practices.

A useful skill would explain:

  • Which data sources are authoritative.
  • How fund identifiers map to internal database records.
  • Which fields are required before analysis can begin.
  • How to handle missing or stale data.
  • Which calculations are allowed and how to validate them.
  • Which scripts the agent should execute.
  • What the final output should contain.

The second version turns vague knowledge into an operational process.

2. Start with domain knowledge, not generic AI-generated instructions

One of the easiest mistakes is asking an LLM to create a skill from a title alone. For example:

Create a skill for working with our backend.

The resulting instructions might contain generic recommendations about writing clean code, handling errors, and following REST conventions. These are not necessarily wrong, but they may contribute very little to your project.

The real value comes from information the model cannot reliably infer.

Consider a financial application that handles investment funds. A domain-specific skill might need to explain:

  • The difference between TEFAS and BEFAS data.
  • Which identifiers are used by internal services.
  • How fund prices and historical values are represented.
  • Which calculations depend on business-day boundaries.
  • How cached data should be interpreted.
  • Which fields can be missing or delayed.
  • What the application must never assume about incomplete data.

The same principle applies to networking. A skill for WebSocket or socket-based integrations might document:

  • The expected connection lifecycle.
  • Authentication and subscription requirements.
  • Message formats and event types.
  • Reconnection and resubscription behavior.
  • Heartbeat and timeout rules.
  • Duplicate messages and ordering assumptions.
  • Cleanup requirements when a connection closes.

These details can be far more valuable than another page explaining what a WebSocket is.

Where should this knowledge come from? I would start with existing engineering artifacts rather than asking an LLM to invent the rules:

  • Architecture documentation and API specifications.
  • Existing code and configuration.
  • Incident reports and production runbooks.
  • Code review feedback.
  • Bug fixes and recurring agent mistakes.
  • Decisions made during implementation.
  • Test cases that capture important edge cases.

A particularly useful technique is to extract a skill after completing a real task with an agent. When you have to correct the agent, ask yourself: is this correction specific to the current task, or is it a reusable rule that should apply next time?

If it is reusable, it probably belongs in a skill, a project rule, a test, or a combination of these.

3. Treat skills as seriously as code

This is one of my strongest recommendations. Everything you put into a skill can influence the code an agent writes. A misleading instruction can produce a misleading implementation. An outdated API example can cause incorrect integrations. A missing edge case can become a recurring bug.

That makes skill quality an engineering concern, not merely a documentation concern.

Imagine a skill that tells an agent to use a shared HTTP client but fails to mention the project's authentication middleware. The agent might generate code that compiles, passes a basic test, and still violates the application's architecture.

Or imagine a skill that describes a socket connection but omits the resubscription procedure after reconnecting. The implementation may work during normal operation and silently stop receiving updates after a network interruption.

These are not just writing problems. They are failures in the instructions that guide implementation.

I would manage important skills with practices similar to those used for production code:

  • Version them in Git.
  • Review changes through pull requests.
  • Keep examples aligned with the actual implementation.
  • Validate scripts and commands before recommending them.
  • Test skills against representative tasks.
  • Record recurring failures and update the relevant instructions.
  • Remove outdated rules instead of accumulating contradictory ones.

A skill should evolve with the codebase. If a service changes its API contract, the related skill may need to change too. If a production incident reveals a missing constraint, the skill should capture the lesson where future agents will see it.

The goal is not to make skills long. It is to make their instructions dependable.

4. Organize skills around domains and coherent tasks

A skill library should reflect how work is actually performed. I prefer starting with domain-oriented capabilities and then defining the workflows within each domain.

For example:

.agents/
β”œβ”€β”€ skills/
β”‚   β”œβ”€β”€ fund-analysis/
β”‚   β”‚   β”œβ”€β”€ SKILL.md
β”‚   β”‚   └── scripts/
β”‚   β”œβ”€β”€ realtime-data/
β”‚   β”‚   β”œβ”€β”€ SKILL.md
β”‚   β”‚   └── scripts/
β”‚   β”œβ”€β”€ api-integration/
β”‚   β”‚   └── SKILL.md
β”‚   └── incident-investigation/
β”‚       └── SKILL.md
└── agents/
    β”œβ”€β”€ analyst.md
    β”œβ”€β”€ developer.md
    └── reviewer.md

This is an illustrative layout, not a requirement of the Agent Skills specification. Exact discovery paths and agent configuration vary between tools. The key is to establish clear boundaries.

A fund-analysis skill might own fund data interpretation and validation. A realtime-data skill might own connection lifecycle rules, subscriptions, and message handling. An api-integration skill might own the project's conventions for consuming external APIs.

Avoid creating one enormous skill that covers the entire application. Also avoid splitting every tiny instruction into a separate skill.

A useful rule of thumb: one skill should represent a coherent unit of work that can be reused independently. If two sets of instructions almost always need to be loaded together, consider whether they belong in the same skill. If one skill frequently activates for unrelated tasks, narrow its scope.

5. Make skill activation an explicit engineering decision

A skill is useful only if the agent knows when to use it. The description field in SKILL.md plays a critical role in skill discovery. It should clearly describe the task and the situations in which the skill applies.

For example, this is too vague:

---
name: realtime-data
description: "Helps with realtime data."
---

A more useful version would be:

---
name: realtime-data
description: >
  Use when implementing, debugging, or reviewing WebSocket and socket-based
  integrations in this application. Follow the project's connection lifecycle,
  authentication, subscription, reconnection, message validation, and cleanup rules.
---

The second description gives the agent a much clearer signal about when the skill is relevant. However, discovery alone is not enough for every workflow.

Put required skill rules in the relevant agent definition. In my experience, if a particular agent is expected to follow a skill for its core responsibilities, I would make that requirement explicit in the agent's own instruction file.

For example, a developer agent might contain:

## Required Skills
- Before modifying fund-related business logic, load and follow the `fund-analysis` skill.
- Before modifying socket connections, subscriptions, or reconnection behavior, load and follow the `realtime-data` skill.
- If a required skill is unavailable, report the problem rather than silently proceeding as if the skill had been applied.

This is an illustrative convention, not universal syntax. The actual invocation mechanism depends on the agent runtime. The principle is what matters: do not rely entirely on the model guessing when a critical skill should be used.

If your agent framework supports explicit skill invocation, use it. If it supports mandatory preconditions or workflow gates, use those for important tasks. If it only supports automatic discovery, test that discovery carefully.

There is also an important distinction between instructions and enforcement. A rule inside a skill tells an agent what it should do. A test, permission boundary, validator, or runtime policy can verify or constrain what it actually does. For critical requirements, combine both.

6. Give skills a clear workflow

A skill should guide the agent through a sequence of decisions and actions, not just present a list of facts.

For example, a fund analysis workflow could look like this:

Understand the request ↓ Identify the fund and required data ↓ Fetch data from the approved source ↓ Validate completeness and freshness ↓ Perform the requested calculation ↓ Verify the result ↓ Return the result with relevant limitations

A corresponding section in SKILL.md might look like:

## Workflow
1. Identify the requested fund and resolve its canonical identifier.
2. Fetch the required data using the approved script.
3. Validate that the response contains all required fields.
4. Check data freshness before calculating performance.
5. Never silently replace missing values with zero.
6. Run the calculation and validate the result.
7. Return the result with its date range and data limitations.

## Failure handling
- If the fund cannot be identified, ask for clarification.
- If the source is unavailable, report the failure.
- If the data is incomplete, do not present the result as complete.
- Do not invent prices or historical values.

Notice the difference between a general instruction such as β€œanalyze the fund carefully” and a sequence with clear inputs, validation steps, and failure conditions. The workflow should make it clear what the agent needs to accomplish and when it can consider the task complete.

Prefer clear defaults over a menu of alternatives. If a skill gives an agent five equivalent ways to accomplish a routine task, the agent must spend additional effort choosing among them. When one approach is the established project default, document it directly and mention alternatives only when they are genuinely needed.

Be strict where the process is fragile, such as a migration or an irreversible operation. Allow flexibility where several approaches are safe and equally valid.

7. Use scripts and CLIs to make skills executable

This is where skills become particularly interesting. A skill does not have to consist entirely of natural-language instructions. It can include executable scripts that perform predictable operations.

For example:

fund-analysis/
β”œβ”€β”€ SKILL.md
└── scripts/
    β”œβ”€β”€ fetch-fund.ts
    β”œβ”€β”€ validate-response.ts
    └── calculate-performance.ts

The skill explains the workflow. The scripts perform deterministic work.

This separation has several advantages:

  • The same operation can be executed consistently.
  • Input validation can happen before data reaches the model.
  • Large responses can be filtered or summarized.
  • Error handling can be implemented and tested in code.
  • The agent can receive structured output instead of raw payloads.
  • Repeated instructions can be replaced with reusable functionality.

Example: optimize the data before returning it to the agent. Imagine you have an MCP server that returns a large response containing thousands of records, extensive metadata, and fields that are irrelevant to the current task. Sending the entire response into the agent's context may increase token consumption and make the useful information harder to identify.

Instead, you could expose a CLI command that accepts filters and returns only the relevant fields:

fund-cli history \
  --fund ABC \
  --from 2026-01-01 \
  --to 2026-06-30 \
  --fields date,price \
  --format json

The exact command is illustrative. A real implementation would need to define supported flags, validate dates and identifiers, and handle errors explicitly.

The output might be:

{ "fund": "ABC", "currency": "TRY", "data": [ { "date": "2026-01-02", "price": 12.41 }, { "date": "2026-01-05", "price": 12.58 } ] }

The agent receives a smaller, task-specific response rather than an unnecessarily large payload. This is not only a token optimization.

Read on DEV Community ↗ ← Back to News

Comments

No comments yet. Start the discussion.