All Articles Claude Code

Advanced Skill Patterns: Conditional Logic and Branching

You've mastered the basics. You can write skills that perform single actions reliably.

You’ve mastered the basics. You can write skills that perform single actions reliably. But real work demands intelligence—skills that adapt to their context, that branch based on what they find, that make smart decisions about how to proceed. You need conditional logic that actually works in production.

Most Claude Code documentation shows you the happy path: “When condition X, do Y.” But what about when you’re working with unfamiliar codebases? When you need to detect whether a project uses TypeScript or Python, REST or GraphQL, Dockerized or not? When you need to inspect the environment, make decisions, and adjust your approach accordingly?

This is where advanced skill patterns diverge from tutorials. We’re talking about skills that inspect their environment, make branching decisions, and adapt their instruction payload to the actual project context. It’s the difference between a generic tool and a context-aware system. And frankly, it’s what separates basic automation from production-grade intelligence.

By the end of this article, you’ll understand how to write skills that detect project context, branch to different instruction paths based on what they find, invoke conditional tool chains, and intelligently adapt to multiple file types, frameworks, and project structures. You’ll have concrete, battle-tested patterns you can copy directly into your own skills.

The Problem: One-Size-Fits-All Skills Don’t Scale

Let’s get specific. Imagine you’re building a “code review” skill. On the surface, it’s simple:

Review this code for quality issues.

But the moment it hits real projects, context demands sophistication. You’re dealing with multiple dimensions of variation:

  • Language detection: JavaScript needs different review rules than Rust. You can’t use the same linting concerns for both. A TypeScript file demands type safety checks. A Python file demands docstring conventions. A Go file demands interface segregation principles. Each language has idioms, conventions, and anti-patterns. A generic code review skill ignores all of this. A sophisticated one adapts to the language’s culture.

  • Framework detection: A React codebase needs different patterns than an Express backend. You can’t review them identically. React has hook rules, suspense boundaries, and concurrent rendering concerns. Express has middleware ordering, error handling patterns, and route organization. A Vue skill should emphasize template reactivity and component lifecycle. An Angular skill should focus on dependency injection and RxJS patterns. Generic advice is useless or counterproductive. You need framework-specific expertise.

  • Maturity detection: A fifty-line prototype gets different scrutiny than a fifty-thousand-line production system. Your tone and standards need to shift. A startup’s two-month-old codebase expects different conventions than a five-year-old banking platform. The former needs speed and flexibility. The latter needs stability and maintainability. A skill that treats them identically is missing crucial context.

  • Dependency detection: If the project uses TypeScript, you review type safety differently than untyped JavaScript. You need to know what tools are available. If it uses React, reference the React linting rules they’ve configured. If it uses FastAPI, understand their async patterns. If it uses database-specific features, know which database they’re using.

  • Style detection: If the project already uses Prettier, don’t suggest reformatting—that’s redundant. If it uses ESLint with specific rules, reference those rules. Respect the existing configuration instead of suggesting a different approach.

A basic skill ignores all of this. It gives the same canned advice to every project. An advanced skill detects it, adapts, and branches its instructions accordingly. The difference in utility is massive. You’re not just being more helpful; you’re being context-aware enough to be genuinely useful rather than annoying.

Here’s the kicker: branching isn’t just about different instructions. It’s about different tool invocations, different data gathering strategies, and different validation approaches. You need conditional logic in the tool layer, not just the instruction layer. You might invoke a bash command for one project structure and a Python script for another. The data you gather changes. The verification steps change. The success criteria change. Building this correctly requires thinking about the entire execution pipeline, not just the final prompt.

Core Concept: Context Detection and Instruction Adaptation

The architecture of an advanced conditional skill follows this pattern:

1. INPUT: Task description + file path
2. DETECTION PHASE: Inspect environment
   ├── Detect language/framework
   ├── Detect build system
   ├── Detect linting/formatting config
   ├── Detect project maturity
   ├── Detect existing conventions
   └── Read directory structure
3. DECISION PHASE: Choose instruction path
   ├── Build conditional instruction set
   ├── Select relevant tools
   ├── Gather context-specific data
   └── Compile final prompt payload
4. EXECUTION PHASE: Run with context-aware instructions
5. VALIDATION PHASE: Verify output matches detected context

The key insight: You’re not choosing between skill A or skill B. You’re building ONE skill that dynamically adapts its own instructions based on context. A single skill can intelligently handle five different languages, three different frameworks, and four different project structures. You avoid skill proliferation (five different skills for five different tech stacks) while maintaining high-quality, context-aware output. This is the power of advanced patterns—you get the best of both worlds: simplicity and sophistication.

Pattern 1: Language and Framework Detection

Let’s build a concrete skill that detects the project language and adapts its review criteria. This is the foundation for context-aware automation.

Here’s the essential structure:

---
name: smart-code-review
description: >
  Context-aware code review that adapts to project language, framework, and conventions.
  Detects language/framework automatically, applies appropriate standards.
---

## Your Task

You will perform a code review of: {file_path}

Your review must be intelligent and context-aware. Before you review the code itself,
you MUST first detect the project context.

## PHASE 1: Context Detection (Required - Do This First)

You are running a detection phase. Your job: gather intelligence about the project.

Perform EXACTLY these steps:

1. Read the file: `{file_path}`
2. Determine file extension (`.js`, `.ts`, `.py`, `.rs`, `.go`, etc.)
3. Read adjacent config files based on file type:
   - If JavaScript/TypeScript: package.json, .eslintrc, tsconfig.json, prettier.config.js
   - If Python: pyproject.toml, setup.py, .pylintrc, setup.cfg
   - If Rust: Cargo.toml, rustfmt.toml
   - If Go: go.mod, go.sum
4. Scan directory structure to gauge project scale
5. Detect framework/library presence

Output your detection report with specific findings.

## PHASE 2: Conditional Review Instructions

Based on your detection, choose the appropriate review criteria:

### If Language is JavaScript/TypeScript:
- Focus on type safety (if TypeScript with strict: true, enforce strict typing)
- React hooks rules (if React project: hook ordering, dependency arrays)
- Async/await patterns (consistency, proper error handling)
- Reference existing ESLint rules (don't suggest rules already configured)

### If Language is Python:
- Type hints (PEP 484 conventions)
- Docstring format (match what's used elsewhere)
- List comprehensions vs loops
- Error handling specificity
- Async patterns if using asyncio

### If Language is Rust:
- Ownership and borrowing correctness
- Unsafe block justification
- Error handling (Result vs panic)
- Trait usage appropriateness
- String handling (String vs &str)

### If Language is Go:
- Error handling patterns (explicit error returns)
- Interface segregation
- Goroutine and channel patterns
- Package organization and naming
- Testing conventions

## PHASE 3: Perform Review

Using the context you detected, perform the actual code review.
For each issue found, note line number, severity, specific suggestion, and why it matters in THIS project's context.

## PHASE 4: Format Output

Output a structured review with context clearly stated.

Let’s break what’s happening here:

  1. Detection Phase: The skill reads the file, surrounding config, and directory structure to build a context model. It looks for evidence of project maturity, build systems, and existing conventions. This phase is purely informational—gather data without deciding anything yet. The detection report becomes the basis for all decisions that follow.

  2. Conditional Branches: Multiple “if language” blocks with different review criteria for each, tailored to each language’s idioms and concerns. Each branch is specific, not generic. You’re not giving the same advice to TypeScript and Python; you’re giving language-appropriate advice to each.

  3. Adaptive Instructions: The review criteria change based on detected context (TypeScript gets type safety checks; Python gets docstring checks; Go gets interface segregation checks). The skill respects what’s already in place and adapts around it. You’re not imposing standards; you’re working with them.

  4. Tool Awareness: The skill references existing configs and avoids contradicting them. If the project already has ESLint configured, reference those rules, don’t suggest different ones. This prevents the annoying situation where tools fight each other.

  5. Maturity Scaling: The depth and focus of review adapt to project scale. Don’t suggest architectural patterns to a two-month-old prototype. Don’t waste time on naming conventions in a one-off script. You’re adjusting your standard to the context.

This single skill now handles JavaScript, Python, Rust, and Go intelligently. It won’t suggest ESLint rules for a Python project. It won’t lecture about Goroutines in a Go repo that’s clearly avoiding concurrent patterns. It respects context and adapts accordingly. This is the power: one skill, infinite adaptability.

Pattern 2: Conditional Tool Invocation

Sometimes you need different tools for different contexts. This requires branching at the tool level, not just instruction level. You might need to call npm for one project, pip for another, cargo for a third. This is critical for automation at scale because you’re not just giving different advice—you’re actually invoking different tools.

The essential structure:

---
name: format-and-validate
description: >
  Detect project language and build system, then invoke appropriate linting/formatting tools.
---

## PHASE 1: Detect Build System and Tools

Read these files in order of preference:

1. `package.json` - If exists, this is JavaScript/TypeScript
2. `pyproject.toml` or `setup.py` - If exists, this is Python
3. `Cargo.toml` - If exists, this is Rust
4. `go.mod` - If exists, this is Go
5. `Makefile` - Check for lint/format targets as fallback

Output which tools were detected.

## PHASE 2: Conditional Tool Invocation

### Branch A: If package.json exists (JavaScript/TypeScript)

1. Check if Prettier is installed
   - If yes: Run `npm run format` or `prettier --write {path}`
   - If no: Run `npx prettier --write {path}`
2. Check if ESLint is installed
   - If yes: Run `npm run lint` or `eslint {path} --fix`
3. If TypeScript (tsconfig.json exists):
   - Run `tsc --noEmit` to check types
4. If Jest or Vitest detected:
   - Run `npm run test`

### Branch B: If pyproject.toml exists (Python)

1. Detect Python version and package manager
2. Check if Black is configured
   - If yes: Run `black {path}`
   - If no: Run `autopep8 --in-place {path}`
3. Check if isort is configured
   - If yes: Run `isort {path}`
4. Run `pylint {path}` or `flake8 {path}`

### Branch C: If Cargo.toml exists (Rust)

1. Run `rustfmt {path}`
2. Run `cargo clippy --all-targets`
3. Run `cargo check`

### Branch D: If go.mod exists (Go)

1. Run `gofmt -w {path}`
2. Run `go vet ./...`
3. Run golangci-lint if available

### Branch E: No build tool detected

Output guidance for configuring the project.

## PHASE 3: Validate Success

After running tools, report which succeeded, which failed, and suggested next steps.

Notice the structure: you have Detection: Read the build system files first to understand what’s available. This is your source of truth. Branching: Multiple distinct execution paths based on what was found. Each path is a complete strategy. Tool Invocation: Each branch runs different commands appropriate to that language/ecosystem. You’re using the right tool for the job. Fallback: If nothing is detected, provide helpful guidance rather than failing silently. You’re always helpful, even when things go wrong.

This pattern is crucial for skills that integrate with existing build systems. You can’t assume npm. You can’t assume pip. You must detect what’s actually available and use it intelligently. If you try to run npm commands in a Python project, you’ve failed. A good skill prevents this through detection first, branching second. It respects the fact that different ecosystems have different tools and different ways of doing things.

Pattern 3: Multi-Step Branching with State Accumulation

Some tasks require multiple decisions across phases, with state accumulating as you go. Here’s a skill for automated migration that makes decisions at each stage based on earlier findings:

The essential structure includes:

  1. PHASE 1: Audit Current State – Scan comprehensively, count files, detect patterns, identify risk factors, estimate effort. Gather all the data you need to make a strategy decision. This is pure information gathering; no decisions yet.

  2. PHASE 2: Decide Migration Strategy – Based on audit, branch to strategy (Direct Upgrade / Phased Migration / Parallel Track). Don’t make this decision until you have full information from Phase 1. The strategy flows from the data.

  3. PHASE 3: Generate Checklist – Create prioritized checklist based on strategy. Different strategies produce different checklists. You’re building the plan to fit the detected reality, not forcing reality to fit a pre-made plan.

  4. PHASE 4: Execute with Decision Points – Execute steps, verify, ask for approval before continuing. Make decisions with humans at critical junctures. This isn’t fire-and-forget; this is collaborative.

This pattern demonstrates:

  • State Accumulation: Early phases (audit) inform later decisions (strategy selection). Information flows downward through phases. Each phase builds on what came before.
  • Branching: Multiple strategy paths based on detected risk. You don’t pick one strategy upfront; you detect reality first, then choose intelligently.
  • Decision Points: Required human approval between phases. The skill gathers information, proposes options, waits for human decision. You’re not removing humans; you’re making them more effective.
  • Conditional Execution: Different steps for different strategies. A phased migration has different steps than a direct upgrade. You’re respecting that reality matters.
  • Rollback Safety: Always keeping revert option available. You never paint yourself into a corner. Safety first.

Pattern 4: File Type Adaptation

Some skills need to adapt not just to language, but to specific file types and their conventions. Documentation should look different for a React component than for a utility function. Test files have different patterns than source files. Configuration files have their own idioms. This pattern handles that with precision:

The essential structure:

## PHASE 1: Detect File Type

Read {path} and determine:
- Is this a function/method/class/module/component/hook?
- What language and framework if applicable?
- Public API or internal implementation?

## PHASE 2: Detect Documentation Style

Scan the codebase for existing documentation patterns:
- Check 5-10 other files of same type for documentation examples
- Identify preferred style
- Note any custom conventions

## PHASE 3: Generate Context-Appropriate Docs

For TypeScript/JavaScript → JSDoc style
For Python → Google style docstrings
For Rust → triple-slash comments
For React → TSDoc + component-specific fields

## PHASE 4: Validate Against Existing Style

Compare your generated documentation against 3-5 existing examples
Adjust to match local conventions exactly

This pattern shows:

  • Detection: File type and language-specific. You’re not just detecting language; you’re understanding the file’s role. Is this a test? A configuration? A public API? Each has different documentation needs.
  • Style Analysis: Scanning for existing conventions. You learn what the project actually does, not what standards say it should do. The project’s style is your source of truth.
  • Branching: Multiple different doc template formats. Each language/framework has its own best practices. You respect those.
  • Validation: Comparing against existing patterns to ensure consistency. Your output should be indistinguishable from what the team already writes. This is the ultimate success metric: indistinguishability.

Pattern 5: Intelligent Tool Composition with Fallbacks

When a skill needs to invoke other skills or tools conditionally, with fallback options for when primary approaches fail:

## PHASE 1: Detect Testing Framework

Check for, in order:
- `package.json` scripts.test
- `pytest.ini` or `pyproject.toml`
- `Cargo.toml` → Rust built-in tests
- `go.mod` → Go testing
- `Makefile` → custom test target
- Directory structure for fallback detection

## PHASE 2: Conditional Execution with Fallbacks

### Primary Path: Run Framework Native Tests
### Secondary Path: Fallback to Auto-Detection
### Tertiary Path: Manual Test Discovery

## PHASE 3: Report Results

This demonstrates:

  • Primary/Secondary/Tertiary Paths: Graceful degradation. If the primary approach fails, you have backups. You never leave the user hanging.
  • Fallback Strategy: If detection fails, try alternatives. You never give up; you iterate. Every failure is an opportunity to try something else.
  • Tool Composition: Invoking framework-specific commands. You leverage what’s already in place rather than reinventing.
  • Detailed Reporting: Clear output for debugging. Users see what you tried and why you chose your path. This transparency builds trust.

Key Principles for Advanced Patterns

As you build conditional, branching skills, keep these principles in mind:

1. Always Detect Before Deciding

Never assume context. Always inspect the environment first. Detection is cheap; wrong decisions are expensive. A fifty-millisecond detection phase saves hours of debugging if a decision is wrong. Invest upfront in gathering information. This is the fundamental rule that makes everything else work.

2. Document Decision Criteria

In your skill, explicitly state what you’re detecting and why. Future you (and your team) will appreciate it when a skill behaves unexpectedly. Make the decision logic visible so debugging is possible. Comments matter here.

3. Provide Clear Fallbacks

If detection fails, what’s the fallback? Default behavior should be safe and informative, never silent failure. Tell users what you’re trying and what failed. This transparency builds trust and makes debugging easier.

4. Respect Existing Conventions

If a project already has linting config, reference it. Don’t suggest alternatives. Respect the project’s choices. Consistency within a codebase is more valuable than “best practices.” You’re not here to impose standards; you’re here to work with what exists.

5. Use Clear Decision Trees

Structure your conditionals as clear decision trees. “If X, then A; else if Y, then B; else C.” Make the logic followable so others can understand and maintain it. Complex nesting becomes unreadable; keep it linear when possible.

6. Separate Detection from Action

Perform all detection in Phase 1, output a report, then execute in Phase 2. This separation makes debugging easier and creates natural decision points. You can review what was detected before committing to a path.

7. Include Verification

After branching to an execution path, verify it worked. If not, stop and report clearly rather than silently failing. Verification isn’t optional; it’s how you maintain trust.

8. Accumulate State Across Phases

Information gathered in Phase 1 informs Phase 2 decisions. Use output from early phases in later branching. Don’t detect the same thing twice. This efficiency compounds across multiple invocations.

Real-World Application: Building Adaptive Skills

When you combine these patterns, you get production-grade skills that work intelligently across diverse codebases. A single skill can:

  • Detect your project’s language, framework, and maturity
  • Identify existing linting and formatting configurations
  • Recognize your team’s conventions and preferences
  • Adapt its approach based on what it finds
  • Reference existing standards rather than suggesting alternatives
  • Validate its output against detected conventions

This is how you build skills that scale. Not by trying to handle every possible case in a single monolithic prompt, but by building intelligent detection phases that inform adaptive branching. You’re building a system that gets smarter the more you use it because it learns your project’s patterns.

Conclusion

Advanced skill patterns separate “I can code” from “I can architect.” The skills in this article—context detection, intelligent branching, tool composition, fallback handling, state accumulation across phases—are what allow you to write production-grade automation.

Start with detection. Always detect before deciding. Build clear decision trees. Respect existing conventions. Verify your decisions. Accumulate state across phases. Chain tools intelligently.

These patterns aren’t theoretical. They’re battle-tested in production codebases with mixed languages, multiple frameworks, and years of accumulated conventions. They work because they respect reality: the code you’re working with is never a blank slate. It has history, conventions, and context. Your skills need to understand that context and adapt accordingly.

That’s what separates generic tools from production intelligence.

Build skills that respect codebases. Build skills that learn what a project values. Build skills that work with teams, not against their conventions. That’s the difference between a tool and a truly capable system.

Real-World Implementation: Advanced Patterns in Practice

When you move from theory to implementation, you discover nuances that tutorials don’t cover. One of the biggest is state persistence across invocations. Your detection phase gathers information, but what if Claude Code needs to invoke the same skill twice? Detecting everything again is wasteful. Store intermediate state—the detected language, the identified framework, the configuration files found—somewhere accessible to the next invocation. Use memory files or environment variables to pass this information forward without re-detecting.

Another challenge is handling ambiguous detection results. Your code might find evidence of both TypeScript and Python in the same project (a monorepo with a Python backend and TypeScript frontend). Which one should your skill adapt to? The answer is: both, separately. Implement conditional branching at the file type level, not just the project level. When the skill receives a JavaScript file, review it as JavaScript. When it receives a Python file, review it as Python. This fineness of granularity is what makes skills genuinely useful in polyglot environments.

You’ll also encounter version mismatches. The linting tool installed in package.json is ESLint v7, but the project’s tsconfig.json targets features from v9. Which rules should you suggest? The safest approach is to reference what’s actually installed, not what documentation says best practices are. Query the installed version explicitly, then adapt your guidance. This humility—respecting what’s actually there rather than imposing ideals—is what makes skills trustworthy.

Advanced Debugging: When Branching Goes Wrong

Despite your best efforts, conditional skills sometimes branch to the wrong path. Your TypeScript detector mistakes a Python project with .ts file extensions for test utilities as a TypeScript project. Or your build system detector finds a Makefile but misses the package.json that’s the actual build authority. These failures are frustrating because they’re silent—the skill runs, branches, and gives wrong advice.

Implement comprehensive logging. Every detection decision should be logged to your memory system. “Detected language: Python (based on pyproject.toml)”. “Build system: Poetry (found poetry.lock with 247 dependencies)”. “Framework: Django (imported in 12 files)”. This logging is your debugging superhero. When a user reports “why did the skill suggest ESLint for my Python project?”, you can trace through the logs and see exactly where the detection failed.

Also build in confidence scoring. Not every detection is equally certain. Finding a tsconfig.json is nearly certain evidence of TypeScript. Finding a file with .ts in the name is medium confidence—it could be a test file with unusual naming. Finding the word “async” in a file is low confidence—lots of languages have async. When confidence is low, you have options: ask the user to confirm (“I detected Python. Is this right?”), or use a more conservative branching strategy that doesn’t rely on that signal alone.

Advanced Performance: Scaling Skills to Large Codebases

If your skill needs to scan 50,000 files to build a complete picture, you’re going to hit performance walls. Claude Code tools can time out. File I/O can overwhelm the system. Memory usage can spike.

Implement progressive detection. Start with high-confidence signals that are fast to check. Find package.json at the root? Check. Find .gitignore? Two seconds. Now you know language and ecosystem with 95% confidence. Only if that fails do you do more expensive checks—scanning every file for imports, parsing ASTs, etc. This approach gets you useful results fast while leaving expensive computation for when necessary.

Also implement sampling for large codebases. If a project has 100,000 lines of code in 500 files, you don’t need to read all of them. Read the first and last file in each major directory. Calculate statistics based on that sample. This works because code patterns are usually consistent—if 80% of your sample files use specific style patterns, it’s safe to assume 80% of your entire codebase does too. Sampling reduces I/O and CPU by orders of magnitude.

The Philosophy Behind Advanced Patterns

There’s a deeper philosophy underlying these patterns that’s worth understanding. Good automation doesn’t impose its will on existing systems—it adapts to them. Good skills don’t tell teams “here’s how you should do things,” they ask “here’s how I see you doing things, is that right?”

This philosophy explains why detection comes first. Why existing conventions are respected. Why branching is intelligent. Why fallbacks are graceful. It’s not about making the automation work perfectly in theory; it’s about making it work in the messy reality where code has history, teams have preferences, and best practices often conflict.

When you build skills with this philosophy, something remarkable happens: they become more useful, not less. Teams adopt them because they feel like they’re being understood, not overridden. Code reviews become faster because the automation respects how the team already works. New engineers onboard faster because they see automation reflecting their team’s actual practices, teaching by example.

That’s the hidden power of advanced skill patterns. You’re not just writing better code; you’re building systems that work with human teams instead of against them.

Putting It All Together: Building Your First Adaptive Skill

Let’s walk through building a complete skill from scratch that uses these patterns. You’re going to create a smart-migrate skill that detects your project’s current state and generates a migration plan tailored to that project, not to some generic template.

The beauty of this approach is that every invocation becomes smarter. The first time you run the skill on a new project, it gathers intelligence. The second time, it builds on what it learned. By the fifth run, the skill has become an expert in your specific project’s patterns and constraints.

Start by defining what you want to detect. For migration tasks, that’s usually: current version, dependencies, configuration style, test coverage, deployment method. Each of these is a signal that tells you something about the project’s maturity and readiness for migration.

Next, design your branching strategy. Don’t create one migration path. Create three: conservative (takes longer, lower risk), standard (balanced approach), and aggressive (fast but requires more testing). Your detection phase identifies which path is appropriate. A five-year-old production system with high test coverage? Standard or conservative. A new startup codebase with 30% coverage? Maybe aggressive is too risky.

Then build your fallback chain. What if you can’t detect the version number? What if there’s no config file? For each unknown, define a safe assumption and a way to verify it later. Document these assumptions so the person running the skill understands what you assumed and can correct you if you’re wrong.

Finally, add observability. Log every detection decision. Log which branch you chose and why. Log what assumptions you made. This transparency is your debugging superpower when something goes wrong.

When you follow these patterns, something remarkable happens. Your skill becomes resilient to unexpected inputs. It handles edge cases gracefully. It teaches the user what it’s doing. It respects existing practices rather than imposing new ones. It’s not a generic tool anymore—it’s a knowledgeable collaborator that understands your specific context.

Learning from Failure: Debugging Advanced Skills

Even with careful planning, advanced skills fail sometimes. A detection logic assumes something that isn’t true. A branching condition triggers the wrong path. An assumption about a tool’s behavior turns out to be wrong. When this happens, your logging becomes your detective.

Build a debugging habit: when a skill behaves unexpectedly, first read the logs. What did it detect? What path did it choose? What assumptions did it make? Often the answer is right there. The skill detected language as Python when it should have detected Go. Or it assumed the project used npm when it actually used yarn. Or it looked for a config file in the wrong location.

Once you understand what went wrong, you have a choice: fix the skill’s detection logic, or document the edge case and teach the user how to handle it. Sometimes the best fix is just adding a question: “I detected Python. Is that correct?” This collaborative approach respects that detection is always probabilistic, never certain.

The real value of advanced patterns is that they make failure modes visible and fixable. A basic skill that always uses the same approach can fail mysteriously. An advanced skill that logs its decisions can be debugged, understood, and improved. That’s worth the extra complexity.

Scaling Conditional Logic Without Losing Readability

As your skills grow more sophisticated, you might worry that all these branches and conditions will make the code unreadable. It doesn’t have to be. The key is organizing your detection and branching logic clearly.

Use separate functions for each detection concern. One function detects language. Another detects framework. Another detects maturity. Each returns a clear result. Then your main decision logic reads like a series of questions: “What language is this? What framework? How mature is it? Given those answers, what’s the right path?” This reads like an interview, not a spaghetti mess of nested conditionals.

You can also use configuration files to define branching decisions rather than hardcoding them. A YAML file might specify: “For Python projects with pytest, run this checklist. For Python projects with unittest, run that checklist.” This separates your detection logic from your decision logic, making both clearer.

Real-World War Stories: Lessons from Production Skills

Every skill that survives in production has war stories. These are the edge cases that weren’t in the documentation, the assumptions that broke under real-world conditions, the places where “theoretically correct” ran into “practically impossible.”

One common story: “We detected the language correctly, but we missed the framework.” A Node.js project might be using Express, Hapi, or Fastify. Each has different patterns and conventions. If you detect JavaScript but not the framework, your advice is generic and useless. The fix? More specific detection. Don’t just check for .js files. Check for telltale framework imports. Look at package.json structure. Examine the routing patterns in actual code.

Another story: “We chose the right path, but the user’s project doesn’t match our assumptions about that path.” You detected TypeScript with strict mode, so you assumed the project wants aggressive type checking. But this particular project has sections of legacy code that can’t be typed. Your suggestion breaks in that context. The fix? More conditional branches. “Strict TypeScript everywhere” vs. “Strict TypeScript in new code, loose elsewhere.” Ask the user which pattern matches their situation.

These stories aren’t cautionary tales. They’re invitations to build more sophisticated systems. Each one represents an opportunity to make your skill more useful, more context-aware, more collaborative.


-iNet

Free Discovery Call

Start With a Conversation, Not a Commitment

Every engagement begins with a free 30-minute discovery call. We'll map what's slowing your business down and tell you exactly what we'd fix first – no pitch deck, no obligation.