Skills are Code: Why Agentic Workflows Need to Be Treated Like Software
5 min read

Skills are Code: Why Agentic Workflows Need to Be Treated Like Software

Skills should be treated as actual units of code: reusable, maintainable, and version-controlled, not static, copy-pasted text files.

Table of Contents

You spent three hours watching an AI agent hallucinate npm scripts and overwrite database migrations because someone pasted a 4,000-word prompt into a root-level .md file. We keep treating agent “skills” like glorified text dumps. Copying instructions across repositories, dropping giant system prompts into .cursor/rules or .github/copilot-instructions.md, and hoping the LLM gets it is the modern software equivalent of FTPing unversioned PHP scripts straight to production in 2004.

If agentic workflows are going to handle production workloads without hemorrhaging API budget or bricking repos, we have to start treating skills as actual units of code.

The Anatomy of a Skill: Modules, Not Text Dumps

A skill is not a vibe. A skill is a reusable competency with a defined input interface, execution contract, and scope bounds.

When you build a proper agent framework, a skill looks like a clean software module:

.agents/
└── skills/
    ├── hugo_content_creation/
    │   ├── SKILL.md
    │   ├── scripts/
    │   └── references/
    └── docker_deployment/
        └── SKILL.md

Inside SKILL.md, the frontmatter acts as a manifest header that the host runtime parses before the model even reads the body:

---
name: "hugo_content_creation"
description: "Helps write engaging, correctly formatted blog and project posts for a Hugo static site."
---

The header registers the skill in the agent’s index. The body stays dormant on disk until the runtime decides the user’s intent matches description. That separation between registration metadata and execution body is what turns prompt engineering into modular software architecture.

Token Economics & Progressive Disclosure

Shoving 50 different instructions into an agent’s system prompt on turn zero is financial and architectural self-sabotage.

Do the arithmetic on context bloat:

  • 25 custom team rules at 1,500 tokens each = 37,500 tokens.
  • 10 tool reference docs at 3,000 tokens each = 30,000 tokens.
  • Total baseline overhead before the user even types a prompt: 67,500 tokens.

At current Claude 3.5 Sonnet or GPT-4o rates, every single back-and-forth iteration burns tens of thousands of tokens just re-reading static instructions. Even worse, attention heads in modern transformer architectures suffer from middle-of-context degradation. When your system prompt looks like an unedited encyclopedia article, the model starts ignoring critical edge-case instructions near line 400.

Progressive disclosure solves both problems. The runtime exposes only the lightweight YAML frontmatter (~50 tokens per skill) during initial tool selection. Only when the model decides it needs hugo_content_creation does the runtime fetch SKILL.md into active memory.

graph TD
    UserReq["User Prompt: 'Write a blog post about Hugo'"] --> Runtime["Agent Runtime Index"]
    Runtime -->|Scans 50-token manifests| Match{"Match Found?"}
    Match -->|Yes| LoadSkill["Inject SKILL.md into Context"]
    Match -->|No| Fallback["Execute with Standard System Prompt"]
    LoadSkill --> Execution["Execute Task with Scoped Rules"]

You keep context lean, lower latency, and stop paying $4.50 every time your agent runs a simple status command.

Central Registries, Lockfiles, and Team Governance

Copy-pasting .cursor/rules files across twelve microservices means twelve different developer machines now run slightly drifted versions of your deployment guidelines. Developer A updated the Docker syntax in service-auth, but Developer B is still using a broken command from three months ago in service-payments.

Skills require central package management. Just like package.json or Cargo.toml, your team needs a single source of truth:

{
  "entries": [
    { "path": ".agents/skills" }
  ],
  "inherits": [
    { "path": "[email protected]:my-org/shared-agent-skills.git//skills.json" }
  ],
  "exclude": ["legacy_deploy_skill"]
}

When a core architectural pattern changes, you update the skill once in the organization registry, version it semantically (v1.4.2), and pull it down across your repositories via CI/CD pipelines. No drift, no copy-paste decay, and no rogue developer prompts floating around uncommitted.

The Model YOLO Trap

A skill that works flawlessly on Claude 4.8 Opus will frequently blow up when executed on a smaller or local model like DeepSeek V3.4 128B or Llama 3 8B.

Smaller models suffer from instruction drift when faced with ambiguous markdown headers, loose formatting guidelines, or complex tool-use loops. Where Opus infers intent between the lines, a small model takes instructions hyper-literally or panics and writes hallucinated shell scripts.

Treating skills like code means authoring defensive instructions:

  • Use explicit enumeration over vague instructions.
  • Enforce strict negative constraints (“Do NOT call tool X before tool Y”).
  • Define exact JSON/YAML schemas for inputs and outputs.
  • Test skill compatibility across every model tier in your deployment matrix.

Testing Skills Beyond “Vibes”: Deterministic Evals

If your test suite for an agent skill consists of running three manual prompts, saying “looks pretty good to me,” and merging to main, you do not have software: you have a gamble.

Skills require automated evaluation pipelines.

# Example evaluation assertion script
def test_hugo_skill_output(generated_markdown: str):
    # Rule 1: No Unicode em-dashes allowed
    assert "—" not in generated_markdown, "Skill failed anti-AI punctuation rule: em-dash found"
    
    # Rule 2: Valid YAML frontmatter structure
    assert generated_markdown.startswith("---"), "Missing leading frontmatter delimiter"
    
    # Rule 3: Required metadata fields present
    frontmatter = parse_yaml_frontmatter(generated_markdown)
    for field in ["title", "date", "summary", "tags"]:
        assert field in frontmatter, f"Missing required frontmatter key: {field}"

Implement two tiers of evals:

  1. Static Analysis & Linting: Run markdown linters and regex schema checkers in GitHub Actions against every pull request that touches .agents/skills/. Verify YAML frontmatter syntax, check for banned phrases or forbidden patterns, and validate relative file links.
  2. Deterministic Task Evals: Execute the skill against benchmark prompt inputs in an isolated container. Assert that the agent calls the correct tool sequence, outputs valid syntax, and passes unit tests on the resulting git diff.

Stop Pasting, Start Engineering

Prompt engineering was a fun hobby for 2023. Building reliable agentic systems in 2026 demands actual software engineering discipline.

If it lives in your codebase, influences your application logic, and burns your compute budget, it is code. Give it a manifest, version it in git, manage its context budget, and test it in CI.


phreck

About phreck

Hacker. Builder. AI Enthusiast.