<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Dailos Rafael Díaz Lara</title>
    <description>The latest articles on DEV Community by Dailos Rafael Díaz Lara (@ddialar).</description>
    <link>https://dev.to/ddialar</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F444998%2Faa212574-8269-49cf-bc6e-6e37d759efce.jpeg</url>
      <title>DEV Community: Dailos Rafael Díaz Lara</title>
      <link>https://dev.to/ddialar</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ddialar"/>
    <language>en</language>
    <item>
      <title>AI - Foundations and best practices creating skills</title>
      <dc:creator>Dailos Rafael Díaz Lara</dc:creator>
      <pubDate>Sun, 13 Sep 2026 13:39:18 +0000</pubDate>
      <link>https://dev.to/ddialar/ai-foundations-and-good-practices-creating-skills-59m3</link>
      <guid>https://dev.to/ddialar/ai-foundations-and-good-practices-creating-skills-59m3</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/ddialar/ia-fundamentos-y-buenas-practicas-en-la-creacion-de-skills-3ed3"&gt;🇪🇸 Versión en español&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's talk about skills for AI agents, and we're doing it off the back of attending an online workshop given by &lt;a class="mentioned-user" href="https://dev.to/mouredev"&gt;@mouredev&lt;/a&gt; on skills a few days ago.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp3ikr260b9sc3xlpb2ka.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp3ikr260b9sc3xlpb2ka.png" alt=" " width="594" height="569"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🎥 &lt;a href="https://www.youtube.com/live/UGh6q6rKVM4?si=j4g-Hz56w0IjNu4q" rel="noopener noreferrer"&gt;Full video on YouTube&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The workshop was really interesting, so much so that I was left wanting more, so I went digging a bit deeper, not so much into what skills do or what they're for, but into the best practices for defining them.&lt;/p&gt;

&lt;p&gt;The result is this post, where I try to organize the main ideas I've been gathering and putting in order. I hope they're as useful to you as they've been to me.&lt;/p&gt;

&lt;p&gt;Without further ado... let's get started.&lt;/p&gt;

&lt;h1&gt;
  
  
  🤓 Introduction
&lt;/h1&gt;

&lt;p&gt;Skills are used to automate repetitive processes whose procedure can be described with a high degree of precision.&lt;/p&gt;

&lt;p&gt;The information inside a skill has to be perfectly structured and fine-tuned so that the model has the least possible chance of making up results.&lt;/p&gt;

&lt;p&gt;The content of a skill can include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prompt&lt;/li&gt;
&lt;li&gt;Rules&lt;/li&gt;
&lt;li&gt;Tools&lt;/li&gt;
&lt;li&gt;MCPs&lt;/li&gt;
&lt;li&gt;Other skills&lt;/li&gt;
&lt;li&gt;etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When we start up an agent, it already knows which skills are available; however, skills can be invoked manually by the developer (&lt;strong&gt;explicit invocation&lt;/strong&gt;), or automatically by the agent itself (&lt;strong&gt;implicit invocation&lt;/strong&gt;), once we've made it aware that they exist.&lt;/p&gt;

&lt;p&gt;Every skill we use gets loaded into the context of the current session, so it's important to control excessive use when they're not needed.&lt;/p&gt;

&lt;h1&gt;
  
  
  💻 Creating Skills
&lt;/h1&gt;

&lt;h2&gt;
  
  
  📍 Skill location
&lt;/h2&gt;

&lt;p&gt;We'll create skills inside the &lt;code&gt;./.agents/skills&lt;/code&gt; directory (if they're only for the current project), or in &lt;code&gt;~/.agents/skills&lt;/code&gt; (if they're global for every agent running on the machine).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🔎 NOTE&lt;br&gt;
This is the standard across the vast majority of agents, but just in case, it's always worth checking how a specific agent handles skills.&lt;br&gt;
For example, in Claude Code CLI, if we manually create a skill in our repo, it won't be detected even after restarting the agent. Why? Because for this agent they need to be created in &lt;code&gt;./.claude/skills&lt;/code&gt;. It's not a problem with the skill or its definition, but with where Claude Code looks for it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Each skill must be created inside a directory specific to that skill. Therefore, it's essential that the skill's directory name matches the name we give the skill itself, since that's the name by which the agent will invoke it.&lt;/p&gt;

&lt;p&gt;On top of that, a &lt;code&gt;SKILL.md&lt;/code&gt; file — containing the skill's definition — must &lt;strong&gt;always&lt;/strong&gt; exist inside that directory.&lt;/p&gt;

&lt;h2&gt;
  
  
  🎩 Skill header
&lt;/h2&gt;

&lt;p&gt;Every skill must have a header that describes it. This header is identified by the opening and closing &lt;code&gt;---&lt;/code&gt; characters typical of YAML.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ IMPORTANT&lt;br&gt;
Even though the skill is defined using Markdown, the content of the skill's header must be written in YAML.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The content of that header must include the following fields:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;name&lt;/code&gt;&lt;/strong&gt; (🔥 &lt;strong&gt;required&lt;/strong&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Must be between 1 and 64 alphanumeric characters, lowercase, in &lt;code&gt;kebab-case&lt;/code&gt; format.&lt;/p&gt;

&lt;p&gt;It cannot start or end with a hyphen, and it can never contain two or more consecutive hyphens (&lt;code&gt;--&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Finally, it must match the name of the directory where the skill is defined, since that's what's used to invoke the skill (&lt;code&gt;/skill-name&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;The regex for this name is: &lt;code&gt;^[a-z0-9]+(-[a-z0-9]+)*$&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;description&lt;/code&gt;&lt;/strong&gt; (🔥 &lt;strong&gt;required&lt;/strong&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Must be between 1 and 1024 characters.&lt;/p&gt;

&lt;p&gt;It has to be specific enough for the agent to be able to read its content and determine whether it matches what the user is looking for.&lt;/p&gt;

&lt;p&gt;To do that, the description should contain specific phrases, verbs, or concrete use cases, for example: &lt;code&gt;Use when the user asks to review code or optimize database queries&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;license&lt;/code&gt;&lt;/strong&gt; (optional)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Used to define the legal usage context of the skill or its usage permissions.&lt;/p&gt;

&lt;p&gt;When setting its value, it's considered good practice to follow the SPDX (&lt;code&gt;Short-form License Identifiers&lt;/code&gt;) standard.&lt;/p&gt;

&lt;p&gt;The most common values tend to be: &lt;code&gt;MIT&lt;/code&gt;, &lt;code&gt;Apache-2.0&lt;/code&gt;, &lt;code&gt;GLP-3.0&lt;/code&gt;, and &lt;code&gt;Proprietary&lt;/code&gt; or &lt;code&gt;Commercial&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;version&lt;/code&gt;&lt;/strong&gt; (optional)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Contains the string that defines the semantic versioning of the skill, for example: &lt;code&gt;1.0.0&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It's usually used to configure pipelines, deployment updates, or tracking within certain ecosystems.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;context&lt;/code&gt;&lt;/strong&gt; (optional) &lt;a href="https://dev.todefault%20%20raw%20`inline`%20endraw%20"&gt;&lt;code&gt;inline&lt;/code&gt; | &lt;code&gt;fork&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Lets us define how the skill runs in relation to the agent's main conversation thread.&lt;/p&gt;

&lt;p&gt;This aspect matters at the level of memory orchestration configuration and environment isolation when the skill is invoked.&lt;/p&gt;

&lt;p&gt;By default, if this field isn't defined, the skill runs in line with the ongoing conversation; however, there are values we can assign to this field to change that behavior:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;inline&lt;/code&gt; (default value)&lt;/p&gt;

&lt;p&gt;The skill runs directly within the current conversation window.&lt;/p&gt;

&lt;p&gt;The instructions, examples, and tools defined inside &lt;code&gt;SKILL.md&lt;/code&gt; are added directly to the current context, and the model retains them in full for use in later parts of the conversation.&lt;/p&gt;

&lt;p&gt;This setting is recommended when, upon starting multiple tasks that need to work together, the skill needs to know the entire history, tone, and specific references the user has made in order to operate correctly.&lt;/p&gt;

&lt;p&gt;With all that said, if we choose this option, we need to set guardrails at the description level so the skill knows exactly when it should step back to gather information and then return to where it left off.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;fork&lt;/code&gt; (sub-agent or isolated sandbox mode)&lt;/p&gt;

&lt;p&gt;In this mode, you could think of it as creating a metaphorical "child process," where the agent temporarily pauses the main conversation thread, copies the relevant information from the current state, and spins up an isolated sub-agent to process exclusively the task assigned by the skill.&lt;/p&gt;

&lt;p&gt;At the memory level, the sub-agent created doesn't read the entire memory or history of the main conversation; instead, it prevents the model from getting distracted from that conversation by avoiding drift caused by mixing in other queries.&lt;/p&gt;

&lt;p&gt;At the token consumption level, since it doesn't take the full main conversation history but rather a small, very specific section, we can reduce the volume of input tokens and get a cheaper, faster execution.&lt;/p&gt;

&lt;p&gt;Once the skill has completed its task, it gathers, cleans up, and distills a summary of the result and injects it into the main conversation thread before closing the "child process" in which it was operating.&lt;/p&gt;

&lt;p&gt;With all that said, if we choose this option, we need to be aware that the instructions must be fully self-contained, since the skill won't have access to the complete conversation history.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Comparison table&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature / Behavior&lt;/th&gt;
&lt;th&gt;
&lt;code&gt;context: inline&lt;/code&gt; (Default)&lt;/th&gt;
&lt;th&gt;&lt;code&gt;context: fork&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Work area&lt;/td&gt;
&lt;td&gt;Main conversation window&lt;/td&gt;
&lt;td&gt;Isolated sub-agent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;History visibility&lt;/td&gt;
&lt;td&gt;Full history preserved&lt;/td&gt;
&lt;td&gt;Hidden (or heavily restricted)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Token consumption&lt;/td&gt;
&lt;td&gt;High (grows with conversation length)&lt;/td&gt;
&lt;td&gt;Low (optimized for skill-specific data)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Output obtained&lt;/td&gt;
&lt;td&gt;Continuous as the conversation goes on&lt;/td&gt;
&lt;td&gt;A simple, structured summary&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Main use case&lt;/td&gt;
&lt;td&gt;Interactive assistant (e.g., copywriting)&lt;/td&gt;
&lt;td&gt;Heavy background work (e.g., code review)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;compatibility&lt;/code&gt;&lt;/strong&gt; (optional)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This field acts as an environment check, making sure the machine where the skill or execution engine is running has the hardware capabilities, software packages, or required LLM engine requirements needed to run the instructions defined inside the skill. For example, we can define the model's minimum reasoning requirements, as well as operating system constraints, CLI packages, or required binaries.&lt;/p&gt;

&lt;p&gt;If the system scans the skill and detects a compatibility issue, the skill is disabled or hidden to prevent runtime errors.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;compatibility&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;gt;=gpt-4o"&lt;/span&gt;
    &lt;span class="na"&gt;os&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;linux,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;darwin"&lt;/span&gt;
    &lt;span class="na"&gt;dependencies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;python&amp;gt;=3.10"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ffmpeg"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;allowed-tools&lt;/code&gt;&lt;/strong&gt; (optional) (also known as &lt;code&gt;allow-tools&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This field is defined by a list of values that strictly establish or limit which tools the agent is allowed to use when the skill runs.&lt;/p&gt;

&lt;p&gt;When defining the content of this property, you should keep the &lt;strong&gt;Principle of Least Privilege&lt;/strong&gt; in mind, so that if a skill needs to read local documentation, it shouldn't be allowed to execute terminal commands.&lt;/p&gt;

&lt;p&gt;When a skill that has this property defined is executed, the agent automatically suspends/disables any tool not specified in the list, so that even if the model hallucinated and tried to invoke one of the disallowed tools, the model's orchestrator would block it.&lt;/p&gt;

&lt;p&gt;One of the main advantages of this field is that it prevents excessive consumption caused by the unnecessary use of expensive tools or the execution of dangerous operations (files, databases, infrastructure, etc.) that aren't clearly authorized.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;allowed-tools&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;internet_browse&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;read_local_file&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;parse_json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;disable-model-invocation&lt;/code&gt;&lt;/strong&gt; (optional) &lt;a href="https://dev.todefault%20%20raw%20`false`%20endraw%20"&gt;&lt;code&gt;true&lt;/code&gt; | &lt;code&gt;false&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This field is used to configure the level of "awareness" the model has about whether the skill in question exists or not.&lt;/p&gt;

&lt;p&gt;When this field isn't defined, or is left at its default value (&lt;code&gt;false&lt;/code&gt;), when the agent starts up it will have the skill in its list of available skills, and if the user says &lt;code&gt;Check my server logs&lt;/code&gt;, the model will analyze the skill's description and, if it matches the goal of the request, it will start running the skill.&lt;/p&gt;

&lt;p&gt;If this option is set to &lt;code&gt;true&lt;/code&gt;, the model will completely ignore this skill, no matter how the agent is being used. The only way to use it is through explicit invocation of that skill, i.e., &lt;code&gt;/skill-name&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This setting is particularly useful for destructive workflows, ones that apply irreversible changes, or ones that carry high risk for the system, where a misinterpretation by the model could lead to accidental loss of valuable data or unauthorized system changes.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wipe-database-cache&lt;/span&gt;
  &lt;span class="na"&gt;disable-model-invocation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;metadata&lt;/code&gt;&lt;/strong&gt; (optional)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This field is an unstructured dictionary designed for development-specific tasks, where we can add additional information related to the skill.&lt;/p&gt;

&lt;p&gt;It acts as a communication bridge between different platforms, code editors, and frameworks that need a way to define additional information relevant to each one, without breaking the open standard for skill definitions.&lt;/p&gt;

&lt;p&gt;One of the main uses given to this field is to display the skill's information in a human-friendly format in applications or tools that manage skills.&lt;/p&gt;

&lt;p&gt;On the other hand, when skills aren't public, this field can be used for tracking their development at a corporate level.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;author&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Platform&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Security&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Team"&lt;/span&gt;
    &lt;span class="na"&gt;category&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DevOps&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;SRE"&lt;/span&gt;
    &lt;span class="na"&gt;icon&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;shield-alert"&lt;/span&gt;
    &lt;span class="na"&gt;cost_center&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;fintech-ops-99"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Some complete examples of skill headers could be the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# This example uses 'context: fork' so it runs in a secure thread, with low token consumption, to perform&lt;/span&gt;
&lt;span class="c1"&gt;# heavy backend-level validation. Also, since it runs autonomously, it can be invoked automatically by the&lt;/span&gt;
&lt;span class="c1"&gt;# model and can include the CLI tools needed to carry out its task.&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kubernetes-manifest-validator&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Use this skill when the user provides Kubernetes YAML files, Helm charts, or K8s deployment manifests and requests syntax validation, security linting, or API deprecation checks.&lt;/span&gt;
&lt;span class="na"&gt;license&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Apache-2.0&lt;/span&gt;
&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;2.4.1&lt;/span&gt;
&lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;fork&lt;/span&gt;
&lt;span class="na"&gt;compatibility&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;gt;=gpt-4o"&lt;/span&gt;
  &lt;span class="na"&gt;os&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;linux"&lt;/span&gt;
  &lt;span class="na"&gt;dependencies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;kubeconform&amp;gt;=0.6.0"&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;trivy&amp;gt;=0.45.0"&lt;/span&gt;
&lt;span class="na"&gt;allowed-tools&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;read_local_file&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;execute_terminal_command&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;write_local_file&lt;/span&gt;
&lt;span class="na"&gt;disable-model-invocation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;team&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SRE-Core"&lt;/span&gt;
  &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;staging-validation"&lt;/span&gt;
  &lt;span class="na"&gt;severity-tier&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;medium"&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="c1"&gt;# This example uses 'context: inline' so it runs within the agent's context window, since it needs access&lt;/span&gt;
&lt;span class="c1"&gt;# to the full conversation history. The tool restriction lets the model run the skill during the conversation.&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;market-competitor-analyzer&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Use this skill when the user asks for competitive intelligence, financial market trends, stock ticker comparisons, or landscape analysis regarding corporate competitors.&lt;/span&gt;
&lt;span class="na"&gt;license&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;MIT&lt;/span&gt;
&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1.0.3&lt;/span&gt;
&lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;inline&lt;/span&gt;
&lt;span class="na"&gt;compatibility&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;gt;=gpt-4-mini"&lt;/span&gt;
  &lt;span class="na"&gt;os&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;any"&lt;/span&gt;
  &lt;span class="na"&gt;dependencies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;python-yfinance&amp;gt;=0.2.0"&lt;/span&gt;
&lt;span class="na"&gt;allowed-tools&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;web_search&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;fetch_url_content&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;render_data_chart&lt;/span&gt;
&lt;span class="na"&gt;disable-model-invocation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;department&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Product-Strategy"&lt;/span&gt;
  &lt;span class="na"&gt;billing-code&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mkt-res-2026"&lt;/span&gt;
  &lt;span class="na"&gt;ux-icon&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;trending-up"&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# This skill involves the irreversible deletion of data, so it includes the 'disable-model-invocation: true'&lt;/span&gt;
&lt;span class="c1"&gt;# setting, making it only invokable by a human. Additionally, the 'compatibility' field restricts it to the&lt;/span&gt;
&lt;span class="c1"&gt;# specific database it can operate on.&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;production-database-purger&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Mandatorily hidden from automatic routing. This skill safely drops stale tables, truncates high-volume log schemas, and runs database vacuuming routines on production clusters during maintenance windows.&lt;/span&gt;
&lt;span class="na"&gt;license&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Proprietary&lt;/span&gt;
&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;4.0.0-rc1&lt;/span&gt;
&lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;fork&lt;/span&gt;
&lt;span class="na"&gt;compatibility&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;gt;=o1-preview"&lt;/span&gt;
  &lt;span class="na"&gt;os&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;linux,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;darwin"&lt;/span&gt;
  &lt;span class="na"&gt;dependencies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;postgresql-client-16"&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;aws-cli-v2"&lt;/span&gt;
&lt;span class="na"&gt;allowed-tools&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;execute_sql_query&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;fetch_vault_secret&lt;/span&gt;
&lt;span class="na"&gt;disable-model-invocation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;compliance-required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SOC2-Type-II"&lt;/span&gt;
  &lt;span class="na"&gt;requires-human-approval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;criticality&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;high"&lt;/span&gt;
  &lt;span class="na"&gt;slack-alert-channel&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;#prod-ops-logs"&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, it's worth noting that even though these fields may belong to a standard, not every agent understands them or uses the same naming to achieve the same goal. The following table shows which agent accepts which header field:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Header fields for SKILL.md supported by agent&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Header Field&lt;/th&gt;
&lt;th&gt;Claude Code CLI&lt;/th&gt;
&lt;th&gt;Claude.ai (Web)&lt;/th&gt;
&lt;th&gt;Claude API&lt;/th&gt;
&lt;th&gt;AutoGen Studio&lt;/th&gt;
&lt;th&gt;CrewAI Core&lt;/th&gt;
&lt;th&gt;LangGraph Engine&lt;/th&gt;
&lt;th&gt;OpenCode CLI&lt;/th&gt;
&lt;th&gt;Aider CLI&lt;/th&gt;
&lt;th&gt;CodeRabbit CLI&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;name&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;description&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;version&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;license&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;context&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅ &lt;em&gt;(mode)&lt;/em&gt;
&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;allowed-tools&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅ &lt;em&gt;(perms)&lt;/em&gt;
&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;disable-model-invocation&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅ &lt;em&gt;(disable)&lt;/em&gt;
&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;compatibility&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅ &lt;em&gt;(deps)&lt;/em&gt;
&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;metadata&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  🛢 Body of a skill (the &lt;strong&gt;&lt;code&gt;T-I-P-O&lt;/code&gt;&lt;/strong&gt; pattern)
&lt;/h2&gt;

&lt;p&gt;Once we've finished defining the skill's header, it's time for the body.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ IMPORTANT&lt;br&gt;
Unlike the header section, the body of a skill &lt;em&gt;is&lt;/em&gt; written using Markdown.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What we define here is a compendium of semantic directives, structured in a certain way, that the model is able to internalize as operational instructions.&lt;/p&gt;

&lt;p&gt;This block is decisive for whether the agent behaves as deterministically as possible, or starts having critical hallucinations during execution.&lt;/p&gt;

&lt;p&gt;Despite the importance of this block, since it's open source, there's no single, strict structure that universally forces us to define a skill's body in a particular way. That said, in enterprise environments there's a growing convergence toward using the pattern known as &lt;strong&gt;&lt;code&gt;T-I-P-O&lt;/code&gt;&lt;/strong&gt; (&lt;code&gt;Targets&lt;/code&gt;, &lt;code&gt;Inputs&lt;/code&gt;, &lt;code&gt;Procedure&lt;/code&gt;, and &lt;code&gt;Outputs&lt;/code&gt;), which is the de facto minimum accepted to guarantee a baseline of determinism in the model.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;# Target&lt;/code&gt; (or &lt;code&gt;# Objective&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Defines what this skill's execution is aiming for and what the expected final goal is.&lt;/p&gt;

&lt;p&gt;When an agent gets lost in a loop of tool calls, it re-evaluates its progress by comparing it against what's defined in this section.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;  # Target

  The absolute objective of this skill is to locate deprecated API endpoints inside the repository, upgrade them to the current SDK version, and ensure the test suite passes with zero errors.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;# Inputs&lt;/code&gt; (or &lt;code&gt;# Prerequisites&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Defines the exact variables, files, or data formats the agent must receive &lt;strong&gt;before&lt;/strong&gt; starting work.&lt;/p&gt;

&lt;p&gt;This section matters because it stops the agent from "guessing" or making up data; that way, if the current context doesn't contain these elements, the agent knows it should stop and ask for them.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;  # Inputs

  This skill requires two primary artifacts from the active workspace context:
&lt;span class="p"&gt;
  1.&lt;/span&gt; &lt;span class="sb"&gt;`legacy_endpoints.json`&lt;/span&gt; - A manifest listing the raw endpoints.
&lt;span class="p"&gt;  2.&lt;/span&gt; &lt;span class="sb"&gt;`current_sdk_spec.yaml`&lt;/span&gt; - The up-to-date OpenAPI schema reference.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;# Procedure&lt;/code&gt; (or &lt;code&gt;# Execution Steps&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Defines a numbered, sequential list that the agent must follow strictly, step by step.&lt;/p&gt;

&lt;p&gt;With this, we manage to break the model's reasoning down into manageable subtasks (Chain-of-Thought) while forcing the agent to follow numbered steps, reducing possible hallucinations in workflows that use multiple tools.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;  # Procedure
&lt;span class="p"&gt;
  1.&lt;/span&gt; Parse the &lt;span class="sb"&gt;`legacy_endpoints.json`&lt;/span&gt; file using the &lt;span class="sb"&gt;`read_file`&lt;/span&gt; tool.
&lt;span class="p"&gt;  2.&lt;/span&gt; For each endpoint listed, locate its definition in the codebase using &lt;span class="sb"&gt;`grep_search`&lt;/span&gt;.
&lt;span class="p"&gt;  3.&lt;/span&gt; Replace the deprecated syntax with the new methods specified in &lt;span class="sb"&gt;`current_sdk_spec.yaml`&lt;/span&gt;.
&lt;span class="p"&gt;  4.&lt;/span&gt; Run the local testing pipeline using &lt;span class="sb"&gt;`execute_terminal_command(command="npm test")`&lt;/span&gt;.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;# Outputs&lt;/code&gt; (or &lt;code&gt;# Expected Output Format&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Defines the output contract upon completion of the skill's execution.&lt;/p&gt;

&lt;p&gt;Here we indicate whether we want a JSON, a Markdown document, a Markdown code block, etc., as well as the exact structure of the final response.&lt;/p&gt;

&lt;p&gt;This way, the agent's outputs will be easily readable by other automated scripts or by the user, without containing filler text.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;  # Outputs

  Return exclusively a valid JSON block containing the compilation summary. Do not include conversational preambles.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Full example of a defined skill:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;api-migration-tool&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Use when the user requests an automated upgrade of legacy API endpoints.&lt;/span&gt;
&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1.0.0&lt;/span&gt;
&lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;fork&lt;/span&gt;
&lt;span class="na"&gt;allowed-tools&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;read_file&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;write_file&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;grep_search&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;execute_terminal_command&lt;/span&gt;
&lt;span class="na"&gt;disable-model-invocation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;

&lt;span class="gh"&gt;# Target&lt;/span&gt;

Migrate deprecated microservice API routing schemas to the v2 standard.

&lt;span class="gh"&gt;# Inputs&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; Workspace variable: &lt;span class="sb"&gt;`target_directory`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Source config file: &lt;span class="sb"&gt;`api_routing.conf`&lt;/span&gt;

&lt;span class="gh"&gt;# Procedure&lt;/span&gt;
&lt;span class="p"&gt;
1.&lt;/span&gt; Scan the &lt;span class="sb"&gt;`target_directory`&lt;/span&gt; for any &lt;span class="sb"&gt;`.conf`&lt;/span&gt; files.
&lt;span class="p"&gt;2.&lt;/span&gt; Cross-reference keys against the official version 2 documentation wrapper.
&lt;span class="p"&gt;3.&lt;/span&gt; Apply the structural rewrites into a new temporary branch.
&lt;span class="p"&gt;4.&lt;/span&gt; Validate the syntax integrity.

&lt;span class="gh"&gt;# Outputs&lt;/span&gt;

Provide a markdown table summarizing:
&lt;span class="p"&gt;
-&lt;/span&gt; The file paths modified.
&lt;span class="p"&gt;-&lt;/span&gt; The original lines of code.
&lt;span class="p"&gt;-&lt;/span&gt; The rewritten replacement chunks.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;🔎 NOTE&lt;br&gt;
As a final note regarding the body of a skill, when we're in a section where we implement an unordered list, we sometimes find that both the hyphen (&lt;code&gt;-&lt;/code&gt;) and the asterisk (&lt;code&gt;*&lt;/code&gt;) are used to mark a list item.&lt;br&gt;
While it's true that, computationally, it makes no difference to the model, cleanliness and order matter here from a DevEx standpoint, so the use of the hyphen (&lt;code&gt;-&lt;/code&gt;) is encouraged for unordered list items over any other character.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;These are the basic sections a skill should have. Beyond these, if our application's needs require defining additional sections, we're free to do so as long as it helps fine-tune the skill's use even further.&lt;/p&gt;

&lt;p&gt;Some additional sections beyond those proposed by the &lt;code&gt;T-I-P-O&lt;/code&gt; pattern are the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;# Guardrails &amp;amp; Safety Constraints&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Defines critical restrictions by listing absolute limits, forbidden behaviors, and areas the agent must never touch.&lt;/p&gt;

&lt;p&gt;This is the main line of defense against data destruction or security breaches.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;  # Guardrails &amp;amp; Safety Constraints
&lt;span class="p"&gt;
  -&lt;/span&gt; &lt;span class="gs"&gt;**NEVER**&lt;/span&gt; pass raw string variables directly into bash command lines without character escaping.
&lt;span class="p"&gt;  -&lt;/span&gt; Do not modify or read any files inside the hidden &lt;span class="sb"&gt;`.git/`&lt;/span&gt; or &lt;span class="sb"&gt;`.vault/`&lt;/span&gt; internal directories.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;code&gt;# User Verification Gates&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Establishes human approval checkpoints, explicitly defining which specific actions must mandatorily halt the agent's autonomous flow to require visual &lt;code&gt;"Ok"&lt;/code&gt; or manual confirmation from the user in the chat.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;  # User Verification Gates
&lt;span class="p"&gt;
  -&lt;/span&gt; &lt;span class="gs"&gt;**Trigger:**&lt;/span&gt; Prior to executing any database truncation or dropping an index.
&lt;span class="p"&gt;  -&lt;/span&gt; &lt;span class="gs"&gt;**Action:**&lt;/span&gt; Halt the script, render the specific SQL payload to the user, and ask: "Do you confirm the execution of this database migration? (y/n)".
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;code&gt;# Escalation Protocols&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This section prevents the agent from getting stuck trying to solve problems that exceed its permission capabilities, instructing it on when to give up and hand the case off to a human user.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;  # Escalation Protocols
&lt;span class="p"&gt;
  -&lt;/span&gt; If a connection timeout error occurs more than 3 consecutive times on port 5432, halt automation.
&lt;span class="p"&gt;  -&lt;/span&gt; Do not attempt to guess credentials. Output: &lt;span class="sb"&gt;`[CRITICAL] Network isolation detected. Escalating ticket to SRE team.`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;code&gt;# State Tracking &amp;amp; Memory Logging&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With this section we force the model to structure its thought process and internalize state changes in local variables before calling the next tool, solving memory loss in very long workflows.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;  # State Tracking &amp;amp; Memory Logging
&lt;span class="p"&gt;
  -&lt;/span&gt; Before modifying a file, open a &lt;span class="sb"&gt;`&amp;lt;state&amp;gt;`&lt;/span&gt; block to log the original file hash and line count.
&lt;span class="p"&gt;  -&lt;/span&gt; Maintain a rolling list of modified assets in your tool call parameters to avoid circular file edits.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;code&gt;# Chain-of-Thought Auditing&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here, what we do is force the agent to justify each action using specific XML tags (like &lt;code&gt;&amp;lt;thought&amp;gt;&lt;/code&gt;) before invoking terminal commands, which makes debugging and auditing the agent's behavior far easier.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;  # Chain-of-Thought Auditing
&lt;span class="p"&gt;
  -&lt;/span&gt; Every tool call must be preceded by a &lt;span class="sb"&gt;`&amp;lt;reasoning&amp;gt;`&lt;/span&gt; block containing:
&lt;span class="p"&gt;    1.&lt;/span&gt; Why this tool is necessary now.
&lt;span class="p"&gt;    2.&lt;/span&gt; The expected outcome of the invocation.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;code&gt;# Performance &amp;amp; Cost Optimization&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here we can prevent the agent from uncontrollably consuming the API budget (or exhausting the context window), by regulating the amount of text it can read or write in a single iteration.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;  # Performance &amp;amp; Cost Optimization
&lt;span class="p"&gt;
  -&lt;/span&gt; When parsing log files, use range parameters to inspect a maximum of 150 lines per tool call.
&lt;span class="p"&gt;  -&lt;/span&gt; Avoid re-reading large context files if the content was already logged in the active scratchpad.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;code&gt;# Compliance &amp;amp; Regulatory Standards&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This section matters when we're working with certain data, since it ensures that the deliverables generated by the agent (such as source code or data reports) comply with strict legal or organizational regulations for the sector (SOC2, GDPR, ISO), or from the company itself.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;  # Compliance &amp;amp; Regulatory Standards
&lt;span class="p"&gt;
  -&lt;/span&gt; All telemetry methods designed by this skill must completely sanitize PII (Personally Identifiable Information).
&lt;span class="p"&gt;  -&lt;/span&gt; Ensure encryption-in-transit configurations use TLS 1.3 as a baseline.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;code&gt;# Workspace Clean-up &amp;amp; Idempotency&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With this property we can guarantee the hygiene of the local system, ensuring the agent deletes its temporary execution files and that, if the skill runs twice in a row, the result is identical without duplicating data.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;  # Workspace Clean-up &amp;amp; Idempotency
&lt;span class="p"&gt;
  -&lt;/span&gt; Upon task completion or premature failure, execute an explicit cleanup step to delete &lt;span class="sb"&gt;`/tmp/cache_*.json`&lt;/span&gt;.
&lt;span class="p"&gt;  -&lt;/span&gt; Design every code refactor to be completely idempotent; running the skill twice must yield zero changes on the second run.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;code&gt;# Corporate Style &amp;amp; Terminology Glossaries&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here we can unify business terms and the agent's voice when it generates technical documentation, reports, or text responses aimed at end clients or company leadership.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;  # Corporate Style &amp;amp; Terminology Glossaries
&lt;span class="p"&gt;
  -&lt;/span&gt; Use the term "Client Workspace" instead of "Tenant Folder" across all markdown outputs.
&lt;span class="p"&gt;  -&lt;/span&gt; Keep tone formal and highly concise; eliminate words like "obviously", "simply", or conversational expressions.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;code&gt;# Diagnostic &amp;amp; Telemetry Footprints&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This property injects digital signatures and standardized logs into Git commits or the headers of files created by the agent, to uniquely identify which changes were made by the AI and which version of the skill was used.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;  # Diagnostic &amp;amp; Telemetry Footprints
&lt;span class="p"&gt;
  -&lt;/span&gt; Append this precise signature at the end of every modified file header:
    &lt;span class="sb"&gt;`/* Automated optimization applied via agent-skill: db-optimizer (v2.4.1) */`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🗃 More complex skills
&lt;/h2&gt;

&lt;p&gt;As we develop a skill, it can become increasingly complex, which makes our &lt;code&gt;SKILL.md&lt;/code&gt; file practically unworkable due to the amount of information, instructions, examples, or similar content it can contain. The most likely result is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;😰 loading all this content into an agent,&lt;/li&gt;
&lt;li&gt;🤯 early context saturation,&lt;/li&gt;
&lt;li&gt;🔥 increasing unnecessary token consumption, and&lt;/li&gt;
&lt;li&gt;💀 degrading the model's response.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The solution to this lies in a process called &lt;strong&gt;&lt;code&gt;Skill Atomization&lt;/code&gt;&lt;/strong&gt;, through which heavy logic, external resources, and referential examples are extracted from the skill, turning it into a &lt;em&gt;declarative orchestrator&lt;/em&gt;, keeping the file's content under 50 or 100 lines of text. This makes the skill's initialization speed within the agent very high and increases the system's scalability through independent reference updates.&lt;/p&gt;

&lt;p&gt;This atomization process is carried out through two actions: &lt;strong&gt;&lt;code&gt;Rigorous structuring&lt;/code&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;code&gt;Technical linking&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;Rigorous structuring&lt;/code&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Inside the directory where we've defined the &lt;code&gt;SKILL.md&lt;/code&gt; file, we start creating directories with semantically sensible names.&lt;/p&gt;

&lt;p&gt;Inside each of those directories, we create the corresponding files that will hold the information we want to extract from the original skill.&lt;/p&gt;

&lt;p&gt;The directory structure depends solely on the development team, but there is indeed a certain tendency to have certain established directories, which don't need to be implemented if our skill doesn't require them, but if we do, it's recommended to keep the same naming.&lt;/p&gt;

&lt;p&gt;An example of rigorous structuring could be this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  my-complex-agent-skill/
    ├── SKILL.md                 &lt;span class="c"&gt;# (Required   ) Main file (Orchestrator and Frontmatter)&lt;/span&gt;
    ├── scripts/                 &lt;span class="c"&gt;# (Optional   ) Executable code that offloads heavy logic from the LLM&lt;/span&gt;
    │   └── optimize_matrix.py   &lt;span class="c"&gt;#               Numerical computation/complex analysis script&lt;/span&gt;
    ├── assets/                  &lt;span class="c"&gt;# (Optional   ) Static data and validation schemas&lt;/span&gt;
    │   └── database_schema.json &lt;span class="c"&gt;#               Reference database structure&lt;/span&gt;
    ├── references/              &lt;span class="c"&gt;# (Optional   ) Style guides, manuals, or dense documentation&lt;/span&gt;
    │   └── code_style_guide.md  &lt;span class="c"&gt;#               Formatting rules the LLM only reads if needed&lt;/span&gt;
    └── examples/                &lt;span class="c"&gt;# (Optional   ) Few-Shot Example library (user stories)&lt;/span&gt;
        ├── standard_case.md
        └── edge_case_timeout.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;Technical linking&lt;/code&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now that we've extracted the excess information from our skill into independent sections of our directory structure, we need to link that content back inside the file that's left.&lt;/p&gt;

&lt;p&gt;For this we'll use &lt;strong&gt;explicit relative paths&lt;/strong&gt; to the content we want to reference. Agents are able to read these paths and, through the use of internal tools, can access the files &lt;strong&gt;on demand&lt;/strong&gt;, only when the procedure section requires it.&lt;/p&gt;

&lt;p&gt;With this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ we avoid loading all this content into an agent up front,&lt;/li&gt;
&lt;li&gt;✅ we avoid early context saturation,&lt;/li&gt;
&lt;li&gt;✅ we reduce unnecessary token consumption, and&lt;/li&gt;
&lt;li&gt;✅ we avoid early degradation of the model's response.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To do this, there are two commonly used patterns: &lt;strong&gt;&lt;code&gt;Direct relative link&lt;/code&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;code&gt;Footnote reference link&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;Direct relative link&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Used for immediate dependencies the agent must always inspect &lt;strong&gt;before&lt;/strong&gt; running a procedure.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Inputs&lt;/span&gt;

This skill requires the project context infrastructure to match the configuration rules specified in the core &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Database Architectural Reference Schema&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;./assets/database_schema.json&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;.
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;Footnote reference link&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Used for dependencies that are worth having linked but whose loading happens only in very specific situations, meaning the agent won't load these references into context unless absolutely necessary.&lt;/p&gt;

&lt;p&gt;Beyond that, they're also often used in very long procedures, pushing references to the end of the file and keeping the main text free of clutter.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Procedure&lt;/span&gt;
&lt;span class="p"&gt;
1.&lt;/span&gt; Pull the latest Docker manifest using the environment variables.
&lt;span class="p"&gt;2.&lt;/span&gt; Build the staging container and verify the cluster health check endpoints.
&lt;span class="p"&gt;3.&lt;/span&gt; In case the build triggers a pipeline schema violation, fetch the resolution steps immediately.

&lt;span class="gh"&gt;# Error Handling &amp;amp; Edge Cases&lt;/span&gt;
&lt;span class="p"&gt;
*&lt;/span&gt; If the server returns a 503 error, verify if your service mesh matches the internal corporate architecture layout.
&lt;span class="p"&gt;
---
&lt;/span&gt;
&lt;span class="gh"&gt;# Resource Footnotes / Lazy-Load References: ./assets/health_check_spec.json: ./references/pipeline_troubleshooting_guide.md: ./references/corporate_network_mesh_v2.md&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now then, how can we start externalizing a skill? Well, we can start by laying out the following steps:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Move scripts out of the prompt context (&lt;code&gt;/scripts&lt;/code&gt;)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Since explaining to an agent in natural language the operations a script must carry out is complex and consumes tokens unnecessarily, we can create a scripting file in a language of our choice that performs that operation.&lt;/p&gt;

&lt;p&gt;In the skill's header, within the &lt;code&gt;allowed-tools&lt;/code&gt; section, we'll grant execution permission for the &lt;code&gt;execute_terminal_command&lt;/code&gt; command and invoke our script from the skill's text.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="p"&gt;  ---&lt;/span&gt;
  name: my-custom-skill
  description: ...
  allowed-tools:
&lt;span class="p"&gt;    -&lt;/span&gt; execute_terminal_command
&lt;span class="p"&gt;  ---
&lt;/span&gt;
  # Procedure
&lt;span class="p"&gt;
  1.&lt;/span&gt; Do not compute matrix variances manually. Instead, trigger the native optimization script:
    &lt;span class="sb"&gt;`execute_terminal_command(command="python3 ./scripts/optimize_matrix.py --path=.")`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Externalize the example library (&lt;code&gt;/examples&lt;/code&gt;)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We need to be very careful with how we use the linked files in this section, since, generally speaking, because they contain extensive, concrete examples, they can take up a lot of space, raising both input and output token consumption if we misuse the examples.&lt;/p&gt;

&lt;p&gt;Ideally, each example should be its own independent file, so that linking to it allows loading one or another depending on the agent's needs.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;  # Expected Workflows

  Before formatting your final response, read and analyze the corresponding execution logs inside the example library based on the current workload:
&lt;span class="p"&gt;  -&lt;/span&gt; For standard microservice queries, read &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Standard Flow Case&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;./examples/standard_case.md&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;.
&lt;span class="p"&gt;  -&lt;/span&gt; For database connection timeouts, read &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Timeout Recovery Case&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;./examples/edge_case_timeout.md&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Load documentation on demand (&lt;code&gt;/references&lt;/code&gt;)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;API documentation, procedures, etc., can saturate an agent's context very quickly, and on top of that, keeping it updated would also require modifying the skill's content.&lt;/p&gt;

&lt;p&gt;If we extract that documentation into independent, isolated files, we can selectively load them only when needed.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;  # Error Handling &amp;amp; Edge Cases

  If a compilation error occurs due to typing differences, do not attempt to guess the syntax. Read the internal reference document &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Type Definition Manual&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;./references/code_style_guide.md&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; before attempting a second patch rewrite.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  🧐 Good (✅) and bad (❌) practices to follow when defining a skill
&lt;/h1&gt;

&lt;h2&gt;
  
  
  At the header level
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;Sync the name and the folder&lt;/strong&gt;: Keep the &lt;code&gt;name&lt;/code&gt; field written in kebab-case (lowercase with hyphens) and make sure it exactly matches the name of the containing folder to avoid indexing failures.&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Keep semantic descriptions tight&lt;/strong&gt;: Write the &lt;code&gt;description&lt;/code&gt; field using action verbs and specific trigger keywords (e.g., "Use when the user requests an API optimization"). This optimizes routing and prevents accidental activations.&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Apply the Principle of Least Privilege&lt;/strong&gt;: Declare exclusively, in &lt;code&gt;allowed-tools&lt;/code&gt;, the tools the skill strictly needs to achieve its goal, blocking access to dangerous system commands if they aren't required.&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Force human approval for critical tasks&lt;/strong&gt;: Set &lt;code&gt;disable-model-invocation: true&lt;/code&gt; on destructive or production skills (like deployments or database purges) to force the skill to only be activated via a slash command (&lt;code&gt;/&lt;/code&gt;) typed by a person.&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Specify environment dependencies&lt;/strong&gt;: Use the &lt;code&gt;compatibility&lt;/code&gt; field to list minimum versions of system binaries (e.g., python&amp;gt;=3.10, docker), so the framework halts execution before generating a terminal error.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;✅ &lt;strong&gt;Leverage metadata for governance&lt;/strong&gt;: Use the &lt;code&gt;metadata&lt;/code&gt; block systematically in corporate environments to record the owning team, cost center, and compliance identifiers (e.g., &lt;code&gt;compliance: SOC2&lt;/code&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;❌ &lt;strong&gt;Duplicate skill names&lt;/strong&gt;: Using the same &lt;code&gt;name&lt;/code&gt; field in different &lt;code&gt;SKILL.md&lt;/code&gt; files within the repository, which causes collisions and makes the orchestrator ignore components at random.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;❌ &lt;strong&gt;Create generic or ambiguous descriptions&lt;/strong&gt;: Writing descriptions like &lt;code&gt;description: "An AI assistant to help you write code"&lt;/code&gt;. This causes the LLM to activate the skill constantly for common tasks, saturating the context window.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;❌ &lt;strong&gt;Grant universal permissions out of laziness&lt;/strong&gt;: Declaring wildcards for tools or including terminal execution tools (&lt;code&gt;execute_terminal_command&lt;/code&gt;) in skills that only need to read data.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;❌ &lt;strong&gt;Ignore version control&lt;/strong&gt;: Leaving the &lt;code&gt;version&lt;/code&gt; field static at &lt;code&gt;1.0.0&lt;/code&gt; indefinitely, preventing CI/CD pipelines from verifying whether production agents are running the most recently validated behavior.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;❌ &lt;strong&gt;Confuse the role of &lt;code&gt;context: fork&lt;/code&gt;&lt;/strong&gt;: Configuring a skill as &lt;code&gt;context: inline&lt;/code&gt; when it needs to process thousands of lines of server logs, causing the main chat to fill up with noise and burn through the token budget.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;❌ &lt;strong&gt;Omit the &lt;code&gt;license&lt;/code&gt; field in shared skills&lt;/strong&gt;: Leaving the license field empty in shared internal repositories, exposing development teams to intellectual property compliance issues.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  At the body level
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;Adopt the standard &lt;code&gt;T-I-P-O&lt;/code&gt; structure&lt;/strong&gt;: Always organize the body of the document using the &lt;code&gt;# Target&lt;/code&gt;, &lt;code&gt;# Inputs&lt;/code&gt;, &lt;code&gt;# Procedure&lt;/code&gt;, and &lt;code&gt;# Outputs&lt;/code&gt; blocks to guide the model through a deterministic flow.&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Write imperative, numbered procedures&lt;/strong&gt;: Use numbered lists (1., 2., 3.) in the procedure section to force sequential, step-by-step reasoning (&lt;code&gt;Chain-of-Thought&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Standardize unordered bullets with hyphens&lt;/strong&gt;: Use only hyphens (&lt;code&gt;-&lt;/code&gt;) for lists of constraints, inputs, or tools. Reserve asterisks (&lt;code&gt;*&lt;/code&gt;) exclusively for bold (&lt;code&gt;**&lt;/code&gt;) or italics (&lt;code&gt;*&lt;/code&gt;), making it easier for syntax parsers to read.&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Externalize dense manuals via footnotes&lt;/strong&gt;: Apply lazy loading by moving paths to manuals or secondary guides to the bottom of the page (&lt;code&gt;[Reference 1]: ./references/guide.md&lt;/code&gt;), keeping the main flow free of filler text.&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Define strict output contracts&lt;/strong&gt;: In the &lt;code&gt;# Outputs&lt;/code&gt; section, specify the exact response format (e.g., a valid JSON schema or a Markdown table), and explicitly forbid conversational preambles like "Sure, here is your summary".&lt;/li&gt;
&lt;li&gt;&lt;p&gt;✅ &lt;strong&gt;State negative constraints assertively&lt;/strong&gt;: Dedicate an independent section to safety restrictions (&lt;code&gt;# Guardrails &amp;amp; Safety Constraints&lt;/code&gt;) and write prohibitions in uppercase and imperative form (e.g., "NEVER run recursive deletes").&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;❌ &lt;strong&gt;Mix bullet styles within the same block&lt;/strong&gt;: Randomly combining hyphens (&lt;code&gt;-&lt;/code&gt;) and asterisks (&lt;code&gt;*&lt;/code&gt;) within the same list, which can break context segmentation in certain parsing engines.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;❌ &lt;strong&gt;Write instructions as free-form narrative prose&lt;/strong&gt;: Writing the procedure as a long paragraph instead of a structured list. Models tend to skip secondary instructions when they're buried in dense blocks of text.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;❌ &lt;strong&gt;Embed extensive source code within the instructions&lt;/strong&gt;: Pasting complete Python or Bash scripts into the prompt body. This drastically degrades the model's attention and drives up execution costs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;❌ &lt;strong&gt;Assume the agent knows the current environment&lt;/strong&gt;: Writing procedures without first defining the &lt;code&gt;# Inputs&lt;/code&gt; section, causing the agent to try to guess file paths, variable names, or database environments.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;❌ &lt;strong&gt;Use ambiguous or conditional language&lt;/strong&gt;: Using phrases like "Please try to optimize the query if you think it is a good idea". Production agents require direct, deterministic instructions (e.g., "Analyze query latency using the EXPLAIN tool").&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;❌ &lt;strong&gt;Overload the skill with too many secondary goals&lt;/strong&gt;: Trying to make a single &lt;code&gt;SKILL.md&lt;/code&gt; file handle code analysis, cloud deployments, and database optimization simultaneously. If the scope grows, split it into independent skills.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  At the level of robustness, error handling, and scalability
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;Design clear escape routes for tool errors&lt;/strong&gt;: Dedicate a section to &lt;code&gt;# Error Handling&lt;/code&gt; detailing exactly what the agent should do if a tool returns an error, times out, or returns empty data.&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Delegate numeric and algorithmic processing to scripts&lt;/strong&gt;: Instead of asking the model to analyze a matrix or a giant JSON via prompts, write a native script in &lt;code&gt;/scripts&lt;/code&gt; and have the agent run it, processing only the output summary.&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Implement modular Few-Shot Examples&lt;/strong&gt;: Store complex interaction examples in independent files inside an &lt;code&gt;/examples&lt;/code&gt; directory and link to them on demand, avoiding saturating the agent's initial context.&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Verify the integrity of relative links via CI/CD&lt;/strong&gt;: Implement an automated script in your integration pipeline that validates that all references to &lt;code&gt;./assets/&lt;/code&gt;, &lt;code&gt;./scripts/&lt;/code&gt;, or &lt;code&gt;./references/&lt;/code&gt; inside your skills actually exist physically and aren't broken.&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Set halt conditions (&lt;code&gt;Halt Conditions&lt;/code&gt;)&lt;/strong&gt;: Explicitly instruct the agent to stop execution immediately and request human supervisor intervention if it encounters critical permission issues (e.g., &lt;code&gt;403 Unauthorized&lt;/code&gt;) or persistent network failures.&lt;/li&gt;
&lt;li&gt;✅ &lt;code&gt;Keep the skill's core under 100 configuration tokens&lt;/code&gt;: Design the main &lt;code&gt;SKILL.md&lt;/code&gt; as a lightweight, minimalist conductor that delegates to external resources, guaranteeing ultra-fast startups and optimal memory consumption.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;✅ &lt;strong&gt;Define project-level skills according to that project's needs&lt;/strong&gt;: When a skill is used in isolated projects, it's not advisable to extract it for global consumption, since any agent would load it regardless of whether it needs it for that particular repository or not. We should only create global skills, or promote a local skill to global, when we're 100% certain that skill will be used by every project.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;❌ &lt;code&gt;Allow infinite retry loops&lt;/code&gt;: Omitting contingency instructions for failures, which causes the agent to try running the same faulty tool over and over in an infinite cycle that burns through your API budget.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;❌ &lt;strong&gt;Hide system error messages&lt;/strong&gt;: Instructing the model to ignore terminal failures (e.g., &lt;code&gt;2&amp;gt; /dev/null&lt;/code&gt;). If the agent masks errors, diagnosing anomalous behavior in production environments becomes impossible.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;❌ &lt;strong&gt;Hardcode credentials, absolute paths, or secrets&lt;/strong&gt;: Writing passwords, API tokens, or absolute paths like &lt;code&gt;/Users/username/project&lt;/code&gt; in the body of the skill. This breaks the agent's portability across different systems and creates a critical security vulnerability.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;❌ &lt;strong&gt;Blindly trust long-term context memory&lt;/strong&gt;: Designing a procedure that depends on the agent remembering a piece of data provided at the start of the general chat, especially when operating in &lt;code&gt;context: inline&lt;/code&gt; configurations.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;❌ &lt;strong&gt;Validate changes using the production environment itself&lt;/strong&gt;: Allowing a code refactoring skill to apply direct modifications to the main branch (&lt;code&gt;main&lt;/code&gt;) without forcing the prior execution of the local unit test suite on an isolated branch.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;❌ &lt;strong&gt;Update external scripts without updating the skill's manual&lt;/strong&gt;: Modifying the input parameters of an automation script in &lt;code&gt;./scripts/&lt;/code&gt; but forgetting to update the corresponding tool-calling rules in the body of the &lt;code&gt;SKILL.md&lt;/code&gt; file, causing the agent to invoke commands with outdated syntax.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  👋 Final conclusions
&lt;/h1&gt;

&lt;p&gt;It's clear that if there's one thing that hasn't changed with AI's arrival in the world of development, it's that best practices are more necessary now than ever, and a clear sign of that is the special care we need to take when defining our skills.&lt;/p&gt;

&lt;p&gt;I hope this content has been useful to you. If you have any questions, feel free to reach out to me. Here are my profiles on &lt;a href="https://x.com/ddialar" rel="noopener noreferrer"&gt;X&lt;/a&gt;, &lt;a href="https://www.linkedin.com/in/ddialar" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;, and &lt;a href="https://github.com/ddialar" rel="noopener noreferrer"&gt;Github&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  🙏 Acknowledgments
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Of course, to &lt;a class="mentioned-user" href="https://dev.to/mouredev"&gt;@mouredev&lt;/a&gt; for taking the time to prepare and share the workshop that was the origin of everything you've read, if you've made it this far.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>skills</category>
      <category>development</category>
    </item>
    <item>
      <title>IA - Fundamentos y buenas prácticas en la creación de Skills</title>
      <dc:creator>Dailos Rafael Díaz Lara</dc:creator>
      <pubDate>Sun, 13 Sep 2026 13:28:00 +0000</pubDate>
      <link>https://dev.to/ddialar/ia-fundamentos-y-buenas-practicas-en-la-creacion-de-skills-3ed3</link>
      <guid>https://dev.to/ddialar/ia-fundamentos-y-buenas-practicas-en-la-creacion-de-skills-3ed3</guid>
      <description>&lt;p&gt;Vamos a hablar de skills para agentes de IA y lo vamos a hacer a raíz de haber asistido hace unos días al taller online impartido por &lt;a class="mentioned-user" href="https://dev.to/mouredev"&gt;@mouredev&lt;/a&gt; sobre skills.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp3ikr260b9sc3xlpb2ka.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp3ikr260b9sc3xlpb2ka.png" alt=" " width="594" height="569"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🎥 &lt;a href="https://www.youtube.com/live/UGh6q6rKVM4?si=j4g-Hz56w0IjNu4q" rel="noopener noreferrer"&gt;Vídeo completo en YouTube&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;El taller estuvo muy interesante, tanto que me quedé con ganas de más, así que me puse a investigar un poco, no tanto en lo que hacen y para qué sirven las skills, sino en las buenas prácticas a la hora de definirlas.&lt;/p&gt;

&lt;p&gt;El resultado es este post donde trato de organizar las principales ideas que he ido reuniendo y ordenando. Espero que te sean tan útiles como lo ha sido para mi.&lt;/p&gt;

&lt;p&gt;Sin más... empecemos.&lt;/p&gt;

&lt;h1&gt;
  
  
  🤓 Introducción
&lt;/h1&gt;

&lt;p&gt;Las skills se emplean para automatizar los procesos repetitivos cuyo procedimiento puede ser descrito con un alto grado de definición.&lt;/p&gt;

&lt;p&gt;La información dentro de una skill ha de estar perfectamente estructurada y afinada para que el modelo no pueda tener la más mínima posibilidad de inventarse resultados.&lt;/p&gt;

&lt;p&gt;El contenido de una skill puede tener:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prompt&lt;/li&gt;
&lt;li&gt;Reglas&lt;/li&gt;
&lt;li&gt;Herramientas&lt;/li&gt;
&lt;li&gt;MCPs&lt;/li&gt;
&lt;li&gt;Otras skills&lt;/li&gt;
&lt;li&gt;etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cuando arrancamos un agente, éste conoce las skills disponibles; ahora bien, las skills pueden ser invocadas manualmente por el desarrollador (&lt;strong&gt;invocación explícita&lt;/strong&gt;), o de manera automática por parte del propio agente (&lt;strong&gt;invocación implícita&lt;/strong&gt;), cuando le hemos informado de la disponibilidad de las mismas.&lt;/p&gt;

&lt;p&gt;Cada skill que usemos, queda cargada en el contexto de la sesión en curso, por lo que es importante controlar su uso en exceso cuando no son necesarias.&lt;/p&gt;

&lt;h1&gt;
  
  
  💻 Creación de Skills
&lt;/h1&gt;

&lt;h2&gt;
  
  
  📍 Ubicación de las skills
&lt;/h2&gt;

&lt;p&gt;Las skills las crearemos dentro del directorio &lt;code&gt;./.agents/skills&lt;/code&gt; (si son únicamente para el proyecto actual), o en &lt;code&gt;~/.agents/skills&lt;/code&gt; (si son globales para todos los agentes que se ejecuten en el equipo).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🔎 NOTA&lt;br&gt;
Éste es el estándar entre la gran mayoría de los agentes pero por si acaso, siempre es interesante revisar cómo un agente en concreto trata las skills.&lt;br&gt;
Por ejemplo, en Claude Code CLI, cuando creamos una skill manualmente en nuestro repo, aunque reiniciemos el agente, no la detectará. ¿Por qué? Pues porque para este agente hay que crearlas en &lt;code&gt;./.claude/skills&lt;/code&gt;. No es un problema con la skill o su definición, sino con dónde la busca Claude Code.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Cada skill deberá crearse dentro de un directorio específico para dicha skill. Por lo tanto, es imperativo que el nombre del directorio de la skill coincida con el nombre que le demos a la misma, ya que será el nombre por el cual dicha skill será invocada por el agente.&lt;/p&gt;

&lt;p&gt;Además de esto, dentro de dicho directorio &lt;strong&gt;siempre&lt;/strong&gt; debe existir el archivo &lt;code&gt;SKILL.md&lt;/code&gt;, que contiene la definición de la skill en cuestión.&lt;/p&gt;

&lt;h2&gt;
  
  
  🎩 Encabezado de una skill
&lt;/h2&gt;

&lt;p&gt;Toda skill ha de poseer un encabezado que la describirá. Este encabezado se identifica por el uso de los caracteres &lt;code&gt;---&lt;/code&gt; de apertura y cierre propios de Yaml.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ IMPORTANTE&lt;br&gt;
A pesar de que la skill se define usando Markdown, el contenido de la cabecera de la skill ha de escribirse en Yaml.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;El contenido de dicho encabezado ha de albergar los siguientes campos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;name&lt;/code&gt;&lt;/strong&gt; (🔥 &lt;strong&gt;obligatorio&lt;/strong&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Debe tener entre 1 y 64 caracteres alfanuméricos, en minúsculas y en formato &lt;code&gt;kebab-case&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;No puede empezar ni terminar con guión medio y en nigún caso puede contener dos o más guiones medios consecutivos (&lt;code&gt;--&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;Finalmente, ha de coincidir con el nombre del directorio donde se está definiendo la skill ya que será éste el que se emplee para invocar a la skill (&lt;code&gt;/skill-name&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;La RegEx de dicho nombre es la siguiente: &lt;code&gt;^[a-z0-9]+(-[a-z0-9]+)*$&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;description&lt;/code&gt;&lt;/strong&gt; (🔥 &lt;strong&gt;obligatorio&lt;/strong&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Debe tener entre 1 y 1024 caracteres.&lt;/p&gt;

&lt;p&gt;Ha de ser lo suficientemente específico como para que el agente sea capaz leer su contenido y determinar si es lo que el usuario está buscando.&lt;/p&gt;

&lt;p&gt;Para ello, la descripción debe contener frases específicas, verbos o casos de uso concreto, por ejemplo: &lt;code&gt;Use when the user asks to review code or optimize database queries&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;license&lt;/code&gt;&lt;/strong&gt; (opcional)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Se emplea para definir el contexto legal de uso de la skill o los permisos de uso de la misma.&lt;/p&gt;

&lt;p&gt;A la hora de definir su valor, se considera buena práctica seguir el estándar de identificadores SPDX (&lt;code&gt;Short-form License Identifiers&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Los valores más usuales suelen ser: &lt;code&gt;MIT&lt;/code&gt;, &lt;code&gt;Apache-2.0&lt;/code&gt;, &lt;code&gt;GLP-3.0&lt;/code&gt; y &lt;code&gt;Proprietary&lt;/code&gt; o &lt;code&gt;Commercial&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;version&lt;/code&gt;&lt;/strong&gt; (opcional)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Contiene la cadena de caracteres que define el seguimiento semántico de las versiones de la skill, por ejemplo: &lt;code&gt;1.0.0&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Suele emplearse para configurar pipelines, actualizaciones de despliegues o el seguimiento en determinados ecosistemas.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;context&lt;/code&gt;&lt;/strong&gt; (opcional) &lt;a href="https://dev.todefault%20%20raw%20`inline`%20endraw%20"&gt;&lt;code&gt;inline&lt;/code&gt; | &lt;code&gt;fork&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nos permite definir cómo se ejecuta la skill en relación al hilo de conversación principal del agente.&lt;/p&gt;

&lt;p&gt;Este aspecto es importante a nivel de configuración de orquestación de memoria y el aislamiento de entornos por parte del agente cuando la skill es invocada.&lt;/p&gt;

&lt;p&gt;Por defecto, si este campo no es definido, la skill se ejecuta en linea con la conversación en curso, sin embargo, existen valores que le podemos dar al campo para modificar este comportamiento:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;inline&lt;/code&gt; (valor por defecto)&lt;/p&gt;

&lt;p&gt;La skill se ejecuta directamente dentro de la ventana conversacional actual.&lt;/p&gt;

&lt;p&gt;Las instrucciones, ejemplos y herramientas definidas dentro de &lt;code&gt;SKILL.md&lt;/code&gt; son añadidas directamente al contexto actual y el modelo las retiene en su totalidad para ser usadas en posteriores conversaciones.&lt;/p&gt;

&lt;p&gt;Se recomienda usar esta configuración cuando al arrancar múltiples tareas que han de trabajar de manera colaborativa, la skill deba conocer todo el historial, tono y referencias específicas que el usuario haya hecho para operar correctamente.&lt;/p&gt;

&lt;p&gt;Con todo esto, si elegimos esta opción, hemos de establecer guardarraíles a nivel de descripción para que la skill sepa exactamente cuándo debe dar un paso atrás para recopilar información y luego volver a donde estaba.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;fork&lt;/code&gt; (modo sub-agente o sandbox aislado)&lt;/p&gt;

&lt;p&gt;En este modo, podríamos entender que se crea un "proceso hijo" metafórico donde el agente pausa temporalmente el hilo de conversación principal, copia la información relevante del estado actual y arranca un sub-agente aislado para procesar exclusivamente la tarea encomendada por la skill.&lt;/p&gt;

&lt;p&gt;A nivel de memoria, el subagente creado no lee toda la memoria ni el historial de la conversación principal sino que previene que el modelo se distraiga de dicha conversación evitando desviaciones por mezcla de otras consultas.&lt;/p&gt;

&lt;p&gt;A nivel de consumo de tokens, como no se coge el historial principal de conversación, sino una pequeña sección muy concreta, podemos reducir el volumen de tokens de entrada y obtener una ejecución más barata y rápida.&lt;/p&gt;

&lt;p&gt;Una vez que la skill ha completado su tarea, ésta recopila, limpia y destila un resumen del resultado y lo inyecta en el hilo de la conversación principal antes de cerrar el "proceso hijo" donde ha estado operando.&lt;/p&gt;

&lt;p&gt;Con todo esto, si elegimos esta opción, hemos de ser conscientes de que las instrucciones han de ser completamente auto-contenidas ya que la skill no tendrá acceso al historial completo de conversación.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Tabla comparativa&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Funcionalidad / Comportamiento&lt;/th&gt;
&lt;th&gt;
&lt;code&gt;context: inline&lt;/code&gt; (Default)&lt;/th&gt;
&lt;th&gt;&lt;code&gt;context: fork&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Área de trabajo&lt;/td&gt;
&lt;td&gt;Ventana de conversación principal&lt;/td&gt;
&lt;td&gt;Subagente aislado&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Visibilidad del historial&lt;/td&gt;
&lt;td&gt;Historial completo preservado&lt;/td&gt;
&lt;td&gt;Oculto (o fuertemente restringido)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Consumo de tokens&lt;/td&gt;
&lt;td&gt;Alto (Crece con la longitud de la conversación)&lt;/td&gt;
&lt;td&gt;Bajo (Optimizado para datos específicos de la skill)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Resultado obtenido&lt;/td&gt;
&lt;td&gt;Continuo a medida que la conversación continua&lt;/td&gt;
&lt;td&gt;Un resumen sencillo y estructurado&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Caso de uso principal&lt;/td&gt;
&lt;td&gt;Asistente interactivo (p.e., Copywriting)&lt;/td&gt;
&lt;td&gt;Trabajos pesados en background (p.e., Code review)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;compatibility&lt;/code&gt;&lt;/strong&gt; (opcional)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Este campo actúa como una validación de entorno, asegurándose de que el equipo donde se está ejecutando la skill o el motor de ejecución, tiene las capacidades de hardware, paquetes de software o requisitos de motor LLM requeridos para ejecutar las instrucciones definidas dentro de la skill. Por ejemplo, podemos definir los requisitos de razonamiento mínimos del LLM, así como las restricciones del sistema operativo, los paquetes para la CLI o los binarios requeridos.&lt;/p&gt;

&lt;p&gt;En caso de que el sistema escanee la skill y detecte un problema de compatibilidad, la skill se desactiva o es ocultada para prevenir errores en tiempo de ejecución.&lt;/p&gt;

&lt;p&gt;Por ejemplo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;compatibility&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;gt;=gpt-4o"&lt;/span&gt;
    &lt;span class="na"&gt;os&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;linux,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;darwin"&lt;/span&gt;
    &lt;span class="na"&gt;dependencies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;python&amp;gt;=3.10"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ffmpeg"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;allowed-tools&lt;/code&gt;&lt;/strong&gt; (opcional) (también conocido por &lt;code&gt;allow-tools&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Este campo está definido por un listado de valores que permiten establecer o acotar estrictamente aquellas herramientas que están permitidas al agente para ser utilizadas cuando se ejecute la skill.&lt;/p&gt;

&lt;p&gt;A la hora de definir el contenido de esta propiedad se ha de tener en mente el &lt;strong&gt;Principio del Mínimo Privilegio&lt;/strong&gt; de manera que, si una skill necesita leer documentación local, no se le debe permitir ejecutar comandos de terminal.&lt;/p&gt;

&lt;p&gt;Cuando una skill que posee esta propiedad definida se ejecuta, el agente automáticamente suspende/desactiva cualquier herramienta no especificada en el listado, de manera que incluso si el modelo alucinara e invocase alguna de las herramientas no permitidas, el orquestador del modelo la bloquearía.&lt;/p&gt;

&lt;p&gt;Una de las principales ventajas de este campo es que previene el consumo excesivo a raíz del uso innecesario de herramientas caras o la ejecución de operaciones peligrosas (archivos, bases de datos, infraestructura, etc.), que no estén claramente autorizadas.&lt;/p&gt;

&lt;p&gt;Por ejemplo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;allowed-tools&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;internet_browse&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;read_local_file&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;parse_json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;disable-model-invocation&lt;/code&gt;&lt;/strong&gt; (opcional) &lt;a href="https://dev.todefault%20%20raw%20`false`%20endraw%20"&gt;&lt;code&gt;true&lt;/code&gt; | &lt;code&gt;false&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Este campo se emplea para configurar el nivel de "conciencia" que el modelo tiene sobre la existencia o no de la skill en cuestión.&lt;/p&gt;

&lt;p&gt;Cuando este campo no se define o se pone a su valor por defecto (&lt;code&gt;false&lt;/code&gt;), cuando el agente arranque la tendrá en su listado de skills disponibles y si el usuario dice &lt;code&gt;Revisar los registros de mi servidor&lt;/code&gt;, el modelo analizará la descripción de la skill y si coincide con el objetivo de la petición, comenzará a ejecutar la skill.&lt;/p&gt;

&lt;p&gt;En el caso de que esta opción se defina como &lt;code&gt;true&lt;/code&gt;, el modelo ignorará por completo esta skill, independientemente del uso que se esté haciendo del agente. La única manera que hay de poder usarla es a través de la invocación explícita de dicha skill, es decir &lt;code&gt;/skill-name&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Esta configuración es realmente interesante para flujos de trabajo destructivos, que apliquen cambios irreversibles o que conlleven un alto riesgo para el sistema, donde una interpretación errónea por parte del modelo puede acarrear la pérdida accidental de datos valiosos o modificaciones no autorizadas del sistema.&lt;/p&gt;

&lt;p&gt;Por ejemplo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wipe-database-cache&lt;/span&gt;
  &lt;span class="na"&gt;disable-model-invocation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;metadata&lt;/code&gt;&lt;/strong&gt; (opcional)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Este campo es un diccionario no estructurado diseñado para tareas específicas de desarrollo, donde podemos añadir información adicional referente a la skill.&lt;/p&gt;

&lt;p&gt;Sirve como puente de comunicación entre diferentes plataformas, editores de código y frameworks, que necesitan una manera de definir información adicional relevante para cada cual, sin romper el estándar abierto de la definición de skills.&lt;/p&gt;

&lt;p&gt;Uno de los principales usos que se le suele dar a este campo es para mostrar información de la skill en formato &lt;em&gt;human-friendly&lt;/em&gt; en aplicaciones o herramientas que gestionan skills.&lt;/p&gt;

&lt;p&gt;Por otro lado, cuando las skills no son públicas, este campo se puede usar para la trazabilidad de su desarrollo a nivel corporativo.&lt;/p&gt;

&lt;p&gt;Por ejemplo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;author&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Platform&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Security&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Team"&lt;/span&gt;
    &lt;span class="na"&gt;category&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DevOps&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;SRE"&lt;/span&gt;
    &lt;span class="na"&gt;icon&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;shield-alert"&lt;/span&gt;
    &lt;span class="na"&gt;cost_center&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;fintech-ops-99"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Algunos ejemplos completos de cabeceras de skill podrían ser los siguientes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Este ejemplo usa 'context: fork' para que se ejecute en un hilo seguro, con bajo consumo de tokens para realizar&lt;/span&gt;
&lt;span class="c1"&gt;# la validación pesada a nivel de backend. Además, como se ejecuta de manera autónoma, puede ser invocada de manera&lt;/span&gt;
&lt;span class="c1"&gt;# automática por parte del modelo he incluir las herramientas CLI necesarias para poder llevar a cabo su tarea.&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kubernetes-manifest-validator&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Use this skill when the user provides Kubernetes YAML files, Helm charts, or K8s deployment manifests and requests syntax validation, security linting, or API deprecation checks.&lt;/span&gt;
&lt;span class="na"&gt;license&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Apache-2.0&lt;/span&gt;
&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;2.4.1&lt;/span&gt;
&lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;fork&lt;/span&gt;
&lt;span class="na"&gt;compatibility&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;gt;=gpt-4o"&lt;/span&gt;
  &lt;span class="na"&gt;os&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;linux"&lt;/span&gt;
  &lt;span class="na"&gt;dependencies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;kubeconform&amp;gt;=0.6.0"&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;trivy&amp;gt;=0.45.0"&lt;/span&gt;
&lt;span class="na"&gt;allowed-tools&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;read_local_file&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;execute_terminal_command&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;write_local_file&lt;/span&gt;
&lt;span class="na"&gt;disable-model-invocation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;team&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SRE-Core"&lt;/span&gt;
  &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;staging-validation"&lt;/span&gt;
  &lt;span class="na"&gt;severity-tier&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;medium"&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="c1"&gt;# Este ejemplo usa 'context: inline' para que se ejecute dentro de la ventana de contexto del agente, dado que&lt;/span&gt;
&lt;span class="c1"&gt;# necesita acceso al historial de conversación completo. La restricción de herramientas permiten al modelo&lt;/span&gt;
&lt;span class="c1"&gt;# ejecutar la skill durante la conversación.&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;market-competitor-analyzer&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Use this skill when the user asks for competitive intelligence, financial market trends, stock ticker comparisons, or landscape analysis regarding corporate competitors.&lt;/span&gt;
&lt;span class="na"&gt;license&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;MIT&lt;/span&gt;
&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1.0.3&lt;/span&gt;
&lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;inline&lt;/span&gt;
&lt;span class="na"&gt;compatibility&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;gt;=gpt-4-mini"&lt;/span&gt;
  &lt;span class="na"&gt;os&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;any"&lt;/span&gt;
  &lt;span class="na"&gt;dependencies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;python-yfinance&amp;gt;=0.2.0"&lt;/span&gt;
&lt;span class="na"&gt;allowed-tools&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;web_search&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;fetch_url_content&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;render_data_chart&lt;/span&gt;
&lt;span class="na"&gt;disable-model-invocation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;department&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Product-Strategy"&lt;/span&gt;
  &lt;span class="na"&gt;billing-code&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mkt-res-2026"&lt;/span&gt;
  &lt;span class="na"&gt;ux-icon&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;trending-up"&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Esta skill conlleva la eliminación irrevocable de datos por lo que contiene la configuración&lt;/span&gt;
&lt;span class="c1"&gt;# 'disable-model-invocation: true', haciendo que sólo pueda ser invocada por una persona. Además,&lt;/span&gt;
&lt;span class="c1"&gt;# con el campo 'compatibility' le estamos limitando la base de datos específica sobre la que puede&lt;/span&gt;
&lt;span class="c1"&gt;# operar.&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;production-database-purger&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Mandatorily hidden from automatic routing. This skill safely drops stale tables, truncates high-volume log schemas, and runs database vacuuming routines on production clusters during maintenance windows.&lt;/span&gt;
&lt;span class="na"&gt;license&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Proprietary&lt;/span&gt;
&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;4.0.0-rc1&lt;/span&gt;
&lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;fork&lt;/span&gt;
&lt;span class="na"&gt;compatibility&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;gt;=o1-preview"&lt;/span&gt;
  &lt;span class="na"&gt;os&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;linux,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;darwin"&lt;/span&gt;
  &lt;span class="na"&gt;dependencies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;postgresql-client-16"&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;aws-cli-v2"&lt;/span&gt;
&lt;span class="na"&gt;allowed-tools&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;execute_sql_query&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;fetch_vault_secret&lt;/span&gt;
&lt;span class="na"&gt;disable-model-invocation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;compliance-required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SOC2-Type-II"&lt;/span&gt;
  &lt;span class="na"&gt;requires-human-approval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;criticality&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;high"&lt;/span&gt;
  &lt;span class="na"&gt;slack-alert-channel&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;#prod-ops-logs"&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Llegados a este punto, es importante remarcar que, aunque estos campos puedan pertenecer a un estándar, no todos los agentes los entienden o usan nomenclaturas diferentes para alcanzar el mismo objetivo. En la siguiente tabla se muestra qué agente acepta qué campo de cabecera:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Campos de cabecera para SKILL.md soportados según agente&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Header Field&lt;/th&gt;
&lt;th&gt;Claude Code CLI&lt;/th&gt;
&lt;th&gt;Claude.ai (Web)&lt;/th&gt;
&lt;th&gt;Claude API&lt;/th&gt;
&lt;th&gt;AutoGen Studio&lt;/th&gt;
&lt;th&gt;CrewAI Core&lt;/th&gt;
&lt;th&gt;LangGraph Engine&lt;/th&gt;
&lt;th&gt;OpenCode CLI&lt;/th&gt;
&lt;th&gt;Aider CLI&lt;/th&gt;
&lt;th&gt;CodeRabbit CLI&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;name&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;description&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;version&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;license&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;context&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅ &lt;em&gt;(mode)&lt;/em&gt;
&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;allowed-tools&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅ &lt;em&gt;(perms)&lt;/em&gt;
&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;disable-model-invocation&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅ &lt;em&gt;(disable)&lt;/em&gt;
&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;compatibility&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅ &lt;em&gt;(deps)&lt;/em&gt;
&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;metadata&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  🛢 Cuerpo de una skill (patrón &lt;strong&gt;&lt;code&gt;T-I-P-O&lt;/code&gt;&lt;/strong&gt;)
&lt;/h2&gt;

&lt;p&gt;Una vez hemos completado la definición de la cabecera de la skill, ahora le toca el turno al cuerpo.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ IMPORTANTE&lt;br&gt;
A diferencia de lo que sucede en la sección de cabecera, el cuerpo de una skill sí se define usando Markdown.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Lo que definamos aquí será un compendio de directivas semánticas, estructuradas de una determinada manera, que el modelo es capaz de asimilar como instrucciones operativas.&lt;/p&gt;

&lt;p&gt;Este bloque es determinante para que el agente se comporte lo más determinista posible o que empiece a tener alucinaciones críticas durante la ejecución.&lt;/p&gt;

&lt;p&gt;A pesar de la importancia de este bloque, al tratarse de open source, no existe una estructura única y estricta que de manera universal nos obligue a definir el cuerpo de una skill de una determinada manera. No obstante, en entornos empresariales se está convergiendo a usar el patrón denominado &lt;strong&gt;&lt;code&gt;T-I-P-O&lt;/code&gt;&lt;/strong&gt; (&lt;code&gt;Targets&lt;/code&gt;, &lt;code&gt;Inputs&lt;/code&gt;, &lt;code&gt;Procedure&lt;/code&gt; y &lt;code&gt;Outputs&lt;/code&gt;), siendo el mínimo indispensable aceptado de facto para garantizar un mínimo de determinismo en el modelo.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;# Target&lt;/code&gt; (u &lt;code&gt;# Objective&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Define qué se está buscando con la ejecución de esta skill y cuál es el objetivo final esperado.&lt;/p&gt;

&lt;p&gt;Cuando un agente se pierde en un bucle de llamadas a herramientas, reevalua su progreso comparándolo con lo definido en este apartado.&lt;/p&gt;

&lt;p&gt;Ejemplo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;  # Target

  The absolute objective of this skill is to locate deprecated API endpoints inside the repository, upgrade them to the current SDK version, and ensure the test suite passes with zero errors.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;# Inputs&lt;/code&gt; (o &lt;code&gt;# Prerequisites&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Define las variables, archivos o formatos de datos exactos que el agente debe recibir &lt;strong&gt;antes&lt;/strong&gt; de empezar a trabajar.&lt;/p&gt;

&lt;p&gt;Este apartado es importante porque evita que el agente empiece a "adivinar" o inventar datos; de modo que, si el contexto actual no contiene estos elementos, el agente sabe que debe detenerse y pedirlos.&lt;/p&gt;

&lt;p&gt;Por ejemplo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;  # Inputs

  This skill requires two primary artifacts from the active workspace context:
&lt;span class="p"&gt;
  1.&lt;/span&gt; &lt;span class="sb"&gt;`legacy_endpoints.json`&lt;/span&gt; - A manifest listing the raw endpoints.
&lt;span class="p"&gt;  2.&lt;/span&gt; &lt;span class="sb"&gt;`current_sdk_spec.yaml`&lt;/span&gt; - The up-to-date OpenAPI schema reference.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;# Procedure&lt;/code&gt; (o &lt;code&gt;# Execution Steps&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Define una lista numerada y secuencial que el agente debe seguir estrictamente y paso por paso.&lt;/p&gt;

&lt;p&gt;Con esto conseguimos segmentar el razonamiento del modelo en subtareas manejables (Chain-of-Thought) al tiempo que forzamos al agente a seguir pasos numerados, reduciendo las posibles alucinaciones en flujos donde se empleen múltiples herramientas.&lt;/p&gt;

&lt;p&gt;Por ejemplo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;  # Procedure
&lt;span class="p"&gt;
  1.&lt;/span&gt; Parse the &lt;span class="sb"&gt;`legacy_endpoints.json`&lt;/span&gt; file using the &lt;span class="sb"&gt;`read_file`&lt;/span&gt; tool.
&lt;span class="p"&gt;  2.&lt;/span&gt; For each endpoint listed, locate its definition in the codebase using &lt;span class="sb"&gt;`grep_search`&lt;/span&gt;.
&lt;span class="p"&gt;  3.&lt;/span&gt; Replace the deprecated syntax with the new methods specified in &lt;span class="sb"&gt;`current_sdk_spec.yaml`&lt;/span&gt;.
&lt;span class="p"&gt;  4.&lt;/span&gt; Run the local testing pipeline using &lt;span class="sb"&gt;`execute_terminal_command(command="npm test")`&lt;/span&gt;.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;# Outputs&lt;/code&gt; (o &lt;code&gt;# Expected Output Format&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Define el contrato de salida al finalizar la ejecución de la skill.&lt;/p&gt;

&lt;p&gt;Aquí indicamos si queremos obtener un JSON, un Markdown, un Markdown Code Block, etc., así como la estructura exacta de la respuesta final.&lt;/p&gt;

&lt;p&gt;De este modo, las salidas del agente serán fácilmente legibles por otros scripts automatizados o por el usuario, sin que contenga texto de relleno.&lt;/p&gt;

&lt;p&gt;Por ejemplo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;  # Outputs

  Return exclusively a valid JSON block containing the compilation summary. Do not include conversational preambles.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ejemplo completo de una skill definida:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;api-migration-tool&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Use when the user requests an automated upgrade of legacy API endpoints.&lt;/span&gt;
&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1.0.0&lt;/span&gt;
&lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;fork&lt;/span&gt;
&lt;span class="na"&gt;allowed-tools&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;read_file&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;write_file&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;grep_search&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;execute_terminal_command&lt;/span&gt;
&lt;span class="na"&gt;disable-model-invocation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;

&lt;span class="gh"&gt;# Target&lt;/span&gt;

Migrate deprecated microservice API routing schemas to the v2 standard.

&lt;span class="gh"&gt;# Inputs&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; Workspace variable: &lt;span class="sb"&gt;`target_directory`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Source config file: &lt;span class="sb"&gt;`api_routing.conf`&lt;/span&gt;

&lt;span class="gh"&gt;# Procedure&lt;/span&gt;
&lt;span class="p"&gt;
1.&lt;/span&gt; Scan the &lt;span class="sb"&gt;`target_directory`&lt;/span&gt; for any &lt;span class="sb"&gt;`.conf`&lt;/span&gt; files.
&lt;span class="p"&gt;2.&lt;/span&gt; Cross-reference keys against the official version 2 documentation wrapper.
&lt;span class="p"&gt;3.&lt;/span&gt; Apply the structural rewrites into a new temporary branch.
&lt;span class="p"&gt;4.&lt;/span&gt; Validate the syntax integrity.

&lt;span class="gh"&gt;# Outputs&lt;/span&gt;

Provide a markdown table summarizing:
&lt;span class="p"&gt;
-&lt;/span&gt; The file paths modified.
&lt;span class="p"&gt;-&lt;/span&gt; The original lines of code.
&lt;span class="p"&gt;-&lt;/span&gt; The rewritten replacement chunks.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;🔎 NOTA&lt;br&gt;
Como nota final relativa al cuerpo de una skill, cuando estamos en una sección donde implementamos un listado no ordenado, en ocasiones podemos encontrar que se usa tanto el guión medio (&lt;code&gt;-&lt;/code&gt;) como el asterisco (&lt;code&gt;*&lt;/code&gt;) para indicar un elemento de dicha lista.&lt;br&gt;
Si bien es verdad que a nivel computacional, al modelo le da exactamente igual, aquí prima la limpieza y el orden a nivel de DevEx, por lo que se promueve el uso de guión medio (&lt;code&gt;-&lt;/code&gt;) para los elementos de una lista no ordenada, frente a cualquier otro carácter.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Estas son las secciones básicas que debería tener una skill. A parte de estas, si las necesidades de nuestra aplicación requieren de la definición de más secciones, tenemos total libertad para hacerlo siempre que ello permita afinar más el uso de la skill.&lt;/p&gt;

&lt;p&gt;Algunas secciones adicionales a los propuestos por el patrón &lt;code&gt;T-I-P-O&lt;/code&gt; son las siguientes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;# Guardrails &amp;amp; Safety Constraints&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Define restricciones críticas mediante el listado de límites absolutos, comportamientos prohibidos y zonas donde el agente jamás debe interactuar.&lt;/p&gt;

&lt;p&gt;Esta es la principal línea de defensa contra destrucciones de datos o brechas de seguridad.&lt;/p&gt;

&lt;p&gt;Por ejemplo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;  # Guardrails &amp;amp; Safety Constraints
&lt;span class="p"&gt;
  -&lt;/span&gt; &lt;span class="gs"&gt;**NEVER**&lt;/span&gt; pass raw string variables directly into bash command lines without character escaping.
&lt;span class="p"&gt;  -&lt;/span&gt; Do not modify or read any files inside the hidden &lt;span class="sb"&gt;`.git/`&lt;/span&gt; or &lt;span class="sb"&gt;`.vault/`&lt;/span&gt; internal directories.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;code&gt;# User Verification Gates&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Establece los puntos de aprobación humana, definiendo explícitamente qué acciones específicas detienen de forma obligatoria el flujo autónomo del agente para requerir un &lt;code&gt;"Ok"&lt;/code&gt; visual o confirmación manual por parte del usuario, en el chat.&lt;/p&gt;

&lt;p&gt;Por ejemplo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;  # User Verification Gates
&lt;span class="p"&gt;
  -&lt;/span&gt; &lt;span class="gs"&gt;**Trigger:**&lt;/span&gt; Prior to executing any database truncation or dropping an index.
&lt;span class="p"&gt;  -&lt;/span&gt; &lt;span class="gs"&gt;**Action:**&lt;/span&gt; Halt the script, render the specific SQL payload to the user, and ask: "Do you confirm the execution of this database migration? (y/n)".
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;code&gt;# Escalation Protocols&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Esta sección evita que el agente se quede atrapado intentando resolver problemas que superan sus capacidades de permisos, instruyéndolo sobre cuándo rendirse y derivar el caso a un usuario humano.&lt;/p&gt;

&lt;p&gt;Por ejemplo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;  # Escalation Protocols
&lt;span class="p"&gt;
  -&lt;/span&gt; If a connection timeout error occurs more than 3 consecutive times on port 5432, halt automation.
&lt;span class="p"&gt;  -&lt;/span&gt; Do not attempt to guess credentials. Output: &lt;span class="sb"&gt;`[CRITICAL] Network isolation detected. Escalating ticket to SRE team.`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;code&gt;# State Tracking &amp;amp; Memory Logging&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Con esta sección forzamos al modelo a estructurar su proceso de pensamiento e internalizar los cambios de estado en variables locales antes de llamar a la siguiente herramienta, solucionando la pérdida de memoria en flujos de trabajo muy largos.&lt;/p&gt;

&lt;p&gt;Por ejemplo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;  # State Tracking &amp;amp; Memory Logging
&lt;span class="p"&gt;
  -&lt;/span&gt; Before modifying a file, open a &lt;span class="sb"&gt;`&amp;lt;state&amp;gt;`&lt;/span&gt; block to log the original file hash and line count.
&lt;span class="p"&gt;  -&lt;/span&gt; Maintain a rolling list of modified assets in your tool call parameters to avoid circular file edits.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;code&gt;# Chain-of-Thought Auditing&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Aquí lo que hacemos es obliga al agente a justificar cada acción utilizando etiquetas XML específicas (como ) antes de invocar comandos terminales, lo que facilita enormemente la depuración y auditoría del comportamiento del agente.&lt;/p&gt;

&lt;p&gt;Por ejemplo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;  # Chain-of-Thought Auditing
&lt;span class="p"&gt;
  -&lt;/span&gt; Every tool call must be preceded by a &lt;span class="sb"&gt;`&amp;lt;reasoning&amp;gt;`&lt;/span&gt; block containing:
&lt;span class="p"&gt;    1.&lt;/span&gt; Why this tool is necessary now.
&lt;span class="p"&gt;    2.&lt;/span&gt; The expected outcome of the invocation.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;code&gt;# Performance &amp;amp; Cost Optimization&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Aquí podemos prevenir que el agente consuma de forma descontrolada el presupuesto de la API (o agote la ventana de contexto) ,regulando la cantidad de texto que puede leer o escribir en una sola iteración.&lt;/p&gt;

&lt;p&gt;Por ejemplo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;  # Performance &amp;amp; Cost Optimization
&lt;span class="p"&gt;
  -&lt;/span&gt; When parsing log files, use range parameters to inspect a maximum of 150 lines per tool call.
&lt;span class="p"&gt;  -&lt;/span&gt; Avoid re-reading large context files if the content was already logged in the active scratchpad.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;code&gt;# Compliance &amp;amp; Regulatory Standards&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Este apartado es importante cuando operamos con determinados datos, ya que nos asegura que los entregables generados por el agente (como código fuente o reportes de datos), cumplan con normativas legales u organizacionales estrictas del sector (SOC2, GDPR, ISO), o de la propia empresa.&lt;/p&gt;

&lt;p&gt;Por ejemplo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;  # Compliance &amp;amp; Regulatory Standards
&lt;span class="p"&gt;
  -&lt;/span&gt; All telemetry methods designed by this skill must completely sanitize PII (Personally Identifiable Information).
&lt;span class="p"&gt;  -&lt;/span&gt; Ensure encryption-in-transit configurations use TLS 1.3 as a baseline.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;code&gt;# Workspace Clean-up &amp;amp; Idempotency&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Con esta propiedad podemos garantiza la higiene del sistema local, asegurando que el agente borre sus archivos temporales de ejecución y que, si la skill se ejecuta dos veces seguidas, el resultado sea idéntico sin duplicar datos.&lt;/p&gt;

&lt;p&gt;Por ejemplo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;  # Workspace Clean-up &amp;amp; Idempotency
&lt;span class="p"&gt;
  -&lt;/span&gt; Upon task completion or premature failure, execute an explicit cleanup step to delete &lt;span class="sb"&gt;`/tmp/cache_*.json`&lt;/span&gt;.
&lt;span class="p"&gt;  -&lt;/span&gt; Design every code refactor to be completely idempotent; running the skill twice must yield zero changes on the second run.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;code&gt;# Corporate Style &amp;amp; Terminology Glossaries&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Aquí podemos unificar los términos de negocio y la voz del agente cuando genera documentación técnica, reportes o respuestas textuales dirigidas a clientes finales o a la directiva de la empresa.&lt;/p&gt;

&lt;p&gt;Por ejemplo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;  # Corporate Style &amp;amp; Terminology Glossaries
&lt;span class="p"&gt;
  -&lt;/span&gt; Use the term "Client Workspace" instead of "Tenant Folder" across all markdown outputs.
&lt;span class="p"&gt;  -&lt;/span&gt; Keep tone formal and highly concise; eliminate words like "obviously", "simply", or conversational expressions.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;code&gt;# Diagnostic &amp;amp; Telemetry Footprints&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Esta propiedad inyecta firmas digitales y logs estandarizados en los commits de Git o cabeceras de archivos creados por el agente para identificar de forma unívoca qué cambios fueron hechos por la IA y qué versión de la skill se utilizó.&lt;/p&gt;

&lt;p&gt;Por ejemplo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;  # Diagnostic &amp;amp; Telemetry Footprints
&lt;span class="p"&gt;
  -&lt;/span&gt; Append this precise signature at the end of every modified file header:
    &lt;span class="sb"&gt;`/* Automated optimization applied via agent-skill: db-optimizer (v2.4.1) */`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🗃 Skills más complejas
&lt;/h2&gt;

&lt;p&gt;A medida que desarrollamos una skill, ésta puede volverse cada vez más compleja lo que hace que nuestro archivo &lt;code&gt;SKILL.md&lt;/code&gt; se vuelva prácticamente inoperativo por la cantidad de información, instrucciones, ejemplos o similares que puede contener. El resultado más probable es:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;😰 cargar todo este contenido en un agente,&lt;/li&gt;
&lt;li&gt;🤯 saturación temprana del contexto,&lt;/li&gt;
&lt;li&gt;🔥 incrementando del consumo innecesario de tokens y,&lt;/li&gt;
&lt;li&gt;💀 degradando la respuesta del modelo.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;La solución a esto está en un proceso llamado &lt;strong&gt;&lt;code&gt;Atomización de la skill&lt;/code&gt;&lt;/strong&gt; mediante el cual, se extrae de la misma lógica pesada, recursos externos y ejemplos referenciales que permiten transforma a la skill en un &lt;em&gt;orquestador declarativo&lt;/em&gt;, manteniendo el contenido del archivo por debajo de las 50 o 100 líneas de texto. Esto hace que la velocidad de inicialización de la skill en el agente sea muy alta e incrementa la escalabilidad del sistema a través de la actualización independiente de referencias.&lt;/p&gt;

&lt;p&gt;Este proceso de atomización se lleva a cabo realizando estas dos acciones: &lt;strong&gt;&lt;code&gt;Estructuración rigurosa&lt;/code&gt;&lt;/strong&gt; y &lt;strong&gt;&lt;code&gt;Enlazado técnico&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;Estructuración rigurosa&lt;/code&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Dentro del directorio donde hemos definido el archivo &lt;code&gt;SKILL.md&lt;/code&gt;, comenzaremos a crear directorios con nombres semánticamente razonables.&lt;/p&gt;

&lt;p&gt;En cada uno de esos directorios, crearemos los archivos correspondientes que contendrán la información que deseamos extraer de la skill original.&lt;/p&gt;

&lt;p&gt;La estructura de directorios depende únicamente del equipo de desarrollo pero sí es verdad que hay cierta tendencia a contar con determinados directorios ya establecidos, que no es necesario implementar si nuestra skill no los requiere, pero de hacerlo, se recomienda mantener el nombrado de los mismos.&lt;/p&gt;

&lt;p&gt;Un ejemplo de estructuración rigurosa podría ser éste:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  my-complex-agent-skill/
    ├── SKILL.md                 &lt;span class="c"&gt;# (Obligatorio) Archivo principal (Orquestador y Frontmatter)&lt;/span&gt;
    ├── scripts/                 &lt;span class="c"&gt;# (Opcional   ) Código ejecutable que delega lógica pesada fuera del LLM&lt;/span&gt;
    │   └── optimize_matrix.py   &lt;span class="c"&gt;#               Script de cómputo numérico/análisis complejo&lt;/span&gt;
    ├── assets/                  &lt;span class="c"&gt;# (Opcional   ) Datos estáticos y esquemas de validación&lt;/span&gt;
    │   └── database_schema.json &lt;span class="c"&gt;#               Estructura de la base de datos de referencia&lt;/span&gt;
    ├── references/              &lt;span class="c"&gt;# (Opcional   ) Guías de estilo, manuales o documentación densa&lt;/span&gt;
    │   └── code_style_guide.md  &lt;span class="c"&gt;#               Reglas de formato que el LLM solo lee si es necesario&lt;/span&gt;
    └── examples/                &lt;span class="c"&gt;# (Opcional   ) Biblioteca de Few-Shot Examples (historias de usuario)&lt;/span&gt;
        ├── standard_case.md
        └── edge_case_timeout.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;Enlazado técnico&lt;/code&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ahora que ya hemos extraído el exceso de información de nuestra skill a secciones independientes de nuestra estructura de directorios, necesitamos enlazar dicho contenido dentro del archivo que ha quedado.&lt;/p&gt;

&lt;p&gt;Para ello emplearemos &lt;strong&gt;rutas relativas explícitas&lt;/strong&gt; al contenido que queramos hacer referencia. Los agentes son capaces de leer estas rutas y mediante el uso de herramientas internas, pueden acceder a los archivos &lt;strong&gt;bajo demanda&lt;/strong&gt;, únicamente cuando la sección del procedimiento lo exige.&lt;/p&gt;

&lt;p&gt;Con esto:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ evitamos cargar todo este contenido en un agente de primeras,&lt;/li&gt;
&lt;li&gt;✅ evitamos la saturación temprana del contexto,&lt;/li&gt;
&lt;li&gt;✅ reducimos el consumo innecesario de tokens y,&lt;/li&gt;
&lt;li&gt;✅ evitamos la degradación temprana de la respuesta del modelo.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Para hacer esto, hay dos patrones que se usan habitualmente: &lt;strong&gt;&lt;code&gt;Enlace relativo directo&lt;/code&gt;&lt;/strong&gt; y &lt;strong&gt;&lt;code&gt;Enlace de referencia al pie&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;Enlace relativo directo&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Se usa para dependencias inmediatas que el agente siempre debe inspeccionar &lt;strong&gt;antes&lt;/strong&gt; de ejecutar un procedimiento.&lt;/p&gt;

&lt;p&gt;Por ejemplo:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Inputs&lt;/span&gt;

This skill requires the project context infrastructure to match the configuration rules specified in the core &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Database Architectural Reference Schema&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;./assets/database_schema.json&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;.
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;Enlace de referencia al pie&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Se usa para dependencias que es interesante tener enlazadas pero cuya carga se lleva a cabo en situaciones muy concretas, es decir, el agente no cargará estas referencias en el contexto salvo extrema necesidad.&lt;/p&gt;

&lt;p&gt;Además de esto, también suelen ser usados en procedimientos muy largos, relegando las referencias al final del archivo y evitando ruido en el texto.&lt;/p&gt;

&lt;p&gt;Por ejemplo:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Procedure&lt;/span&gt;
&lt;span class="p"&gt;
1.&lt;/span&gt; Pull the latest Docker manifest using the environment variables.
&lt;span class="p"&gt;2.&lt;/span&gt; Build the staging container and verify the cluster health check endpoints.
&lt;span class="p"&gt;3.&lt;/span&gt; In case the build triggers a pipeline schema violation, fetch the resolution steps immediately.

&lt;span class="gh"&gt;# Error Handling &amp;amp; Edge Cases&lt;/span&gt;
&lt;span class="p"&gt;
*&lt;/span&gt; If the server returns a 503 error, verify if your service mesh matches the internal corporate architecture layout.
&lt;span class="p"&gt;
---
&lt;/span&gt;
&lt;span class="gh"&gt;# Resource Footnotes / Lazy-Load References: ./assets/health_check_spec.json: ./references/pipeline_troubleshooting_guide.md: ./references/corporate_network_mesh_v2.md&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ahora bien, ¿cómo podemos empezar a externalizar una skill? Pues podemos empezar por plantear los siguientes pasos:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Mover scripts fuera del contexto de prompt (&lt;code&gt;/scripts&lt;/code&gt;)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Dado que explicarle naturalmente a un agente las operaciones que debe llevar a cabo un script es complejo y consume tokens sin necesidad, podemos crear un archivo de scripting en un lenguaje de nuestra elección, que realice dicha operación.&lt;/p&gt;

&lt;p&gt;En la cabecera de la skill, dentro del apartado &lt;code&gt;allowed-tools&lt;/code&gt; daremos permisos de ejecución al comando &lt;code&gt;execute_terminal_command&lt;/code&gt; e invocaremos nuestros script desde el texto de la skill.&lt;/p&gt;

&lt;p&gt;Por ejemplo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="p"&gt;  ---&lt;/span&gt;
  name: my-custom-skill
  description: ...
  allowed-tools:
&lt;span class="p"&gt;    -&lt;/span&gt; execute_terminal_command
&lt;span class="p"&gt;  ---
&lt;/span&gt;
  # Procedure
&lt;span class="p"&gt;
  1.&lt;/span&gt; Do not compute matrix variances manually. Instead, trigger the native optimization script:
    &lt;span class="sb"&gt;`execute_terminal_command(command="python3 ./scripts/optimize_matrix.py --path=.")`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Externalizar la biblioteca de ejemplos (&lt;code&gt;/examples&lt;/code&gt;)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Hay que tener mucho cuidado con el uso que hagamos de los archivos enlazados de esta sección, dado que por lo general, al contener ejemplos extensos y concretos, pueden ocupar mucho, elevando el consumo de tokens, tanto de entrada como de salida, si hacemos un mal uso de los ejemplos.&lt;/p&gt;

&lt;p&gt;Lo ideal aquí es que cada ejemplo sea un archivo independiente, de manera que su enlazado permita cargar uno u otro dependiendo de las necesidades del agente.&lt;/p&gt;

&lt;p&gt;Por ejemplo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;  # Expected Workflows

  Before formatting your final response, read and analyze the corresponding execution logs inside the example library based on the current workload:
&lt;span class="p"&gt;  -&lt;/span&gt; For standard microservice queries, read &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Standard Flow Case&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;./examples/standard_case.md&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;.
&lt;span class="p"&gt;  -&lt;/span&gt; For database connection timeouts, read &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Timeout Recovery Case&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;./examples/edge_case_timeout.md&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Cargar documentación bajo demanda (&lt;code&gt;/references&lt;/code&gt;)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;La documentación de APIs, procedimientos, etc., puede saturar el contexto de un agente de manera muy rápida, además de que su continua actualización requeriría modificar también el contenido de la skill.&lt;/p&gt;

&lt;p&gt;Si extraemos dicha documentación a archivos independientes y aislados, podemos realizar una carga selectiva de los mismos únicamente cuando sea necesario.&lt;/p&gt;

&lt;p&gt;Por ejemplo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;  # Error Handling &amp;amp; Edge Cases

  If a compilation error occurs due to typing differences, do not attempt to guess the syntax. Read the internal reference document &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Type Definition Manual&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;./references/code_style_guide.md&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; before attempting a second patch rewrite.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  🧐 Buenas (✅) y malas (❌) prácticas a seguir a la hora de definir una skill
&lt;/h1&gt;

&lt;h2&gt;
  
  
  A nivel de cabecera
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;Sincronizar el nombre y la carpeta&lt;/strong&gt;: Mantén el campo name escrito en kebab-case (minúsculas con guiones) y asegúrate de que coincida exactamente con el nombre de la carpeta contenedora para evitar fallos de indexación.&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Acotar las descripciones semánticas&lt;/strong&gt;: Redacta el campo description utilizando verbos de acción y palabras clave de activación específicas (ej. "Use when the user requests an API optimization"). Esto optimiza el enrutado y evita activaciones accidentales.&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Aplicar el Principio de Menor Privilegio&lt;/strong&gt;: Declara exclusivamente en allowed-tools las herramientas que la skill necesita estrictamente para cumplir su objetivo, bloqueando el acceso a comandos peligrosos del sistema si no son requeridos.&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Forzar la aprobación humana en tareas críticas&lt;/strong&gt;: Configura &lt;code&gt;disable-model-invocation: true&lt;/code&gt; en skills destructivos o de producción (como despliegues o purgas de bases de datos) para obligar a que la skill sólo se active mediante un comando de barra (&lt;code&gt;/&lt;/code&gt;) escrito por una persona.&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Especificar las dependencias del entorno&lt;/strong&gt;: Utiliza el campo &lt;code&gt;compatibility&lt;/code&gt; para listar las versiones mínimas de los binarios del sistema (ej. python&amp;gt;=3.10, docker), para que el framework detenga la ejecución antes de generar un error de terminal.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;✅ &lt;strong&gt;Aprovechar los metadatos para la gobernanza&lt;/strong&gt;: Utiliza el bloque metadata de forma sistemática en entornos corporativos para registrar el equipo propietario, el centro de costos y los identificadores de cumplimiento (ej. &lt;code&gt;compliance: SOC2&lt;/code&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;❌ &lt;strong&gt;Duplicar nombres de skills&lt;/strong&gt;: Usar el mismo campo name en diferentes archivos SKILL.md dentro del repositorio, lo que provoca colisiones y hace que el orquestador ignore componentes de forma aleatoria.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;❌ &lt;strong&gt;Crear descripciones genéricas o ambiguas&lt;/strong&gt;: Escribir descripciones del tipo description: "An AI assistant to help you write code". Esto causa que el LLM active la skill constantemente para tareas comunes, saturando la ventana de contexto.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;❌ &lt;strong&gt;Otorgar permisos universales por pereza&lt;/strong&gt;: Declarar comodines en las herramientas o incluir herramientas de ejecución de terminal (&lt;code&gt;execute_terminal_command&lt;/code&gt;) en skills que sólo requieren lectura de datos.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;❌ &lt;strong&gt;Ignorar el control de versiones&lt;/strong&gt;: Dejar el campo version estático en &lt;code&gt;1.0.0&lt;/code&gt; indefinidamente, impidiendo que las canalizaciones de CI/CD verifiquen si los agentes en producción ejecutan el comportamiento validado más reciente.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;❌ &lt;strong&gt;Confundir el rol de &lt;code&gt;context: fork&lt;/code&gt;&lt;/strong&gt;: Configurar un skill como &lt;code&gt;context: inline&lt;/code&gt; cuando requiere procesar miles de líneas de registros de servidores, provocando que el chat principal se llene de ruido y se agote el presupuesto de tokens.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;❌ &lt;strong&gt;Omitir el campo license en skills compartidos&lt;/strong&gt;: Dejar el campo de licencia vacío en repositorios internos compartidos, lo que expone a los equipos de desarrollo a problemas de cumplimiento de propiedad intelectual.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A nivel de cuerpo
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;Adoptar la estructura estándar &lt;code&gt;T-I-P-O&lt;/code&gt;&lt;/strong&gt;: Organiza siempre el cuerpo del documento utilizando los bloques &lt;code&gt;# Target&lt;/code&gt;, &lt;code&gt;# Inputs&lt;/code&gt;, &lt;code&gt;# Procedure&lt;/code&gt; y &lt;code&gt;# Outputs&lt;/code&gt; para guiar al modelo a través de un flujo determinista.&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Escribir procedimientos imperativos y numerados&lt;/strong&gt;: Utiliza listas numeradas (1., 2., 3.) en la sección del procedimiento para forzar un razonamiento secuencial paso a paso (&lt;code&gt;Chain-of-Thought&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Estandarizar las viñetas no ordenadas con guiones&lt;/strong&gt;: Utiliza exclusivamente guiones (&lt;code&gt;-&lt;/code&gt;) para listas de restricciones, entradas o herramientas. Deja los asteriscos (&lt;code&gt;*&lt;/code&gt;) únicamente para negritas (&lt;code&gt;**&lt;/code&gt;) o itálicas (&lt;code&gt;*&lt;/code&gt;), facilitando la lectura de los analizadores de sintaxis.&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Externalizar los manuales densos mediante notas al pie&lt;/strong&gt;: Aplica la carga perezosa (&lt;code&gt;lazy loading&lt;/code&gt;) moviendo las rutas de manuales o guías secundarias al pie de la página (&lt;code&gt;[Reference 1]: ./references/guide.md&lt;/code&gt;), manteniendo el flujo principal limpio de texto de relleno.&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Definir contratos de salida estrictos&lt;/strong&gt;: En la sección &lt;code&gt;# Outputs&lt;/code&gt;, especifica el formato exacto de respuesta (ej. un esquema JSON válido o una tabla Markdown), y prohíbe explícitamente los preámbulos conversacionales como "Sure, here is your summary".&lt;/li&gt;
&lt;li&gt;&lt;p&gt;✅ &lt;strong&gt;Declarar restricciones negativas de forma asertiva&lt;/strong&gt;: Dedica una sección independiente a las restricciones de seguridad (&lt;code&gt;# Guardrails &amp;amp; Safety Constraints&lt;/code&gt;) y redacta las prohibiciones en mayúsculas e imperativo (ej. "NEVER run recursive deletes").&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;❌ &lt;strong&gt;Mezclar estilos de viñetas en un mismo bloque&lt;/strong&gt;: Combinar guiones (&lt;code&gt;-&lt;/code&gt;) y asteriscos (&lt;code&gt;*&lt;/code&gt;) de forma aleatoria dentro de una misma lista, lo que puede romper la segmentación del contexto en ciertos motores de análisis.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;❌ &lt;strong&gt;Escribir instrucciones en prosa narrativa de formato libre&lt;/strong&gt;: Redactar el procedimiento como un párrafo largo en lugar de una lista estructurada. Los modelos tienden a omitir instrucciones secundarias cuando están ocultas en bloques densos de texto.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;❌ &lt;strong&gt;Incrustar código fuente extenso dentro de las instrucciones&lt;/strong&gt;: Pegar scripts completos de Python o Bash en el cuerpo del prompt. Esto degrada drásticamente la atención del modelo y dispara los costos de ejecución.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;❌ &lt;strong&gt;Asumir que el agente conoce el entorno actual&lt;/strong&gt;: Escribir procedimientos sin definir previamente la sección &lt;code&gt;# Inputs&lt;/code&gt;, causando que el agente intente adivinar rutas de archivos, nombres de variables o entornos de bases de datos.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;❌ &lt;strong&gt;Utilizar lenguaje ambiguo o condicional&lt;/strong&gt;: Usar frases como "Please try to optimize the query if you think it is a good idea". Los agentes de producción requieren instrucciones directas y deterministas (ej. "Analyze query latency using the EXPLAIN tool").&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;❌ &lt;strong&gt;Saturar la skill con demasiados objetivos secundarios&lt;/strong&gt;: Intentar que un solo archivo &lt;code&gt;SKILL.md&lt;/code&gt; realice análisis de código, despliegues en la nube y optimización de bases de datos simultáneamente. Si el alcance crece, divídelo en skills independientes.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A nivel de robustez, manejo de errores y escalabilidad
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;Diseñar rutas de escape claras para errores de herramientas&lt;/strong&gt;: Dedica una sección a &lt;code&gt;# Error Handling&lt;/code&gt; donde indiques detalladamente qué debe hacer el agente si una herramienta devuelve un error, se agota el tiempo de espera (&lt;code&gt;timeout&lt;/code&gt;) o devuelve datos vacíos.&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Delegar el procesamiento numérico y algorítmico a scripts&lt;/strong&gt;: En lugar de pedirle al modelo que analice una matriz o un JSON gigante mediante prompts, escribe un script nativo en &lt;code&gt;/scripts&lt;/code&gt; y haz que el agente lo ejecute y procese únicamente el resumen de salida.&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Implementar Few-Shot Examples modulares&lt;/strong&gt;: Almacena los ejemplos complejos de interacciones en archivos independientes dentro de un directorio &lt;code&gt;/examples&lt;/code&gt; y enlázalos bajo demanda, evitando saturar el contexto inicial del agente.&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Verificar la integridad de los enlaces relativos mediante CI/CD&lt;/strong&gt;: Implementa un script automatizado en tu flujo de integración que valide que todas las referencias a &lt;code&gt;./assets/&lt;/code&gt;, &lt;code&gt;./scripts/&lt;/code&gt; o &lt;code&gt;./references/&lt;/code&gt; dentro de tus skills existan físicamente y no estén rotas.&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Establecer límites de detención (&lt;code&gt;Halt Conditions&lt;/code&gt;)&lt;/strong&gt;: Instruye explícitamente al agente para que detenga la ejecución de inmediato y solicite la intervención de un supervisor humano si encuentra problemas de permisos críticos (p.e.: &lt;code&gt;403 Unauthorized&lt;/code&gt;), o fallos de red persistentes.&lt;/li&gt;
&lt;li&gt;✅ &lt;code&gt;Mantener el core de la skill por debajo de los 100 tokens de configuración&lt;/code&gt;: Diseña el &lt;code&gt;SKILL.md&lt;/code&gt; principal como un director de orquesta ligero y minimalista que delega en recursos externos, garantizando arranques ultra rápidos y un consumo óptimo de memoria.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;✅ &lt;strong&gt;Definir skills a nivel de proyecto según las necesidades de éste&lt;/strong&gt;: Cuando una skill es usada en proyectos de manera aislada, no es recomendable extraerlas para que sean consumidas de manera global, dado que cualquier agente la cargará independientemente de que la necesite para el repositorio en cuestión, o no. Sólo crearemos skills globales o promocionaremos una skill local a global, cuando tengamos un 100% de certeza de que dicha skill va a ser empleada por todos los proyectos.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;❌ &lt;code&gt;Permitir bucles infinitos de reintentos&lt;/code&gt;: Omitir instrucciones de contingencia ante fallos, lo que causa que el agente intente ejecutar la misma herramienta defectuosa una y otra vez en un ciclo infinito que consume tu presupuesto de API.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;❌ &lt;strong&gt;Ocultar mensajes de error del sistema&lt;/strong&gt;: Instruir al modelo para que ignore los fallos del terminal (p.e.: &lt;code&gt;2&amp;gt; /dev/null&lt;/code&gt;). Si el agente enmascara los errores, diagnosticar comportamientos anómalos en entornos de producción se vuelve imposible.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;❌ &lt;strong&gt;Hardcodear credenciales, rutas absolutas o secretos&lt;/strong&gt;: Escribir contraseñas, tokens de API o rutas absolutas como &lt;code&gt;/Users/username/project&lt;/code&gt; en el cuerpo de la skill. Esto rompe la portabilidad del agente entre diferentes sistemas y genera una vulnerabilidad crítica de seguridad.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;❌ &lt;strong&gt;Confiar ciegamente en la memoria de contexto a largo plazo&lt;/strong&gt;: Diseñar un procedimiento que dependa de que el agente recuerde un dato proporcionado al inicio del chat general, especialmente cuando opera en configuraciones &lt;code&gt;context: inline&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;❌ &lt;strong&gt;Validar cambios utilizando el propio entorno de producción&lt;/strong&gt;: Permitir que una skill de refactorización de código aplique modificaciones directas sobre la rama principal (&lt;code&gt;main&lt;/code&gt;) sin forzar la ejecución previa de la suite de pruebas unitarias locales en una rama aislada.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;❌ &lt;strong&gt;Actualizar scripts externos sin actualizar el manual de la skill&lt;/strong&gt;: Modificar los parámetros de entrada de un script de automatización en &lt;code&gt;./scripts/&lt;/code&gt; pero olvidar actualizar las reglas de llamada a herramientas correspondientes en el cuerpo del archivo &lt;code&gt;SKILL.md&lt;/code&gt;, provocando que el agente invoque comandos con sintaxis obsoleta.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  👋 Conclusiones finales
&lt;/h1&gt;

&lt;p&gt;Está claro que si hay algo que no ha cambiado con la llegada de la IA al mundo del desarrollo, es que las buenas prácticas son más necesarias ahora que nunca y como muestra de ello, es el especial mimo que debemos poner a la hora de definir nuestras skills.&lt;/p&gt;

&lt;p&gt;Espero que este contenido te haya sido útil. Si tienes cualquier pregunta, siéntete totalmente libre de contactar conmigo. Aquí están mis perfiles de &lt;a href="https://x.com/ddialar" rel="noopener noreferrer"&gt;X&lt;/a&gt;, &lt;a href="https://www.linkedin.com/in/ddialar" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; y &lt;a href="https://github.com/ddialar" rel="noopener noreferrer"&gt;Github&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  🙏 Reconocimientos y agradecimientos
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Por supuesto a &lt;a class="mentioned-user" href="https://dev.to/mouredev"&gt;@mouredev&lt;/a&gt; por destinar tiempo a preparar y difundir el taller que ha sido el origen de todo el texto que, si has llegado hasta aquí, has leído.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>skills</category>
      <category>devepment</category>
      <category>programming</category>
    </item>
    <item>
      <title>Definiendo nuestras infraestructuras para desarrollo y testing con Docker</title>
      <dc:creator>Dailos Rafael Díaz Lara</dc:creator>
      <pubDate>Mon, 28 Dec 2020 16:39:44 +0000</pubDate>
      <link>https://dev.to/ddialar/definiendo-nuestras-infraestructuras-de-desarrollo-y-testing-con-docker-12ai</link>
      <guid>https://dev.to/ddialar/definiendo-nuestras-infraestructuras-de-desarrollo-y-testing-con-docker-12ai</guid>
      <description>&lt;p&gt;🇬🇧 &lt;a href="https://dev.to/ddialar/mocking-our-development-and-testing-infrastructures-with-docker-4nf0"&gt;English version&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🎯 Objetivo
&lt;/h2&gt;

&lt;p&gt;Cuando estamos creando una nueva aplicación o funcionalidad, normalmente necesitamos enviar peticiones a recursos independientes como pueden ser una base de datos o servicios con comportamiento controlado pero obviamente, realizar estas tareas contra servidores en la nube tiene un coste.&lt;/p&gt;

&lt;p&gt;En este tipo de situaciones es cuando el aislamiento de sistemas que nos proporcionan los contenedores de Docker, es realmente útil.&lt;/p&gt;

&lt;p&gt;En este artículo vamos a ver cómo podemos usar Docker para levantar una infraestructura mínima que nos permita ejecutar las tareas de desarrollo y/o testing, localmente.&lt;/p&gt;

&lt;p&gt;El principal objetivo de este texto es mostrar cómo utilizar un único archivo &lt;code&gt;docker-compose.yml&lt;/code&gt; para ambos entornos, empleando diferentes archivos &lt;code&gt;.env&lt;/code&gt; para personalizar cada contenedor específico tanto para desarrollo como para testing.&lt;/p&gt;

&lt;p&gt;Además, nos centraremos en cómo arrancar un nuevo contenedor para testing, ejecutar los tests que sean pertinentes y finalmente, apagar dicho contenedor.&lt;/p&gt;

&lt;h2&gt;
  
  
  💻 Configuración del sistema
&lt;/h2&gt;

&lt;p&gt;Si vamos a hablar sobre Docker, es obvio que necesitamos tenerlo instalado en nuestro sistema. Si aún no lo tienes, puedes seguir las indicaciones dadas en la &lt;a href="https://docs.docker.com/get-docker/" rel="noopener noreferrer"&gt;documentación oficial&lt;/a&gt;, para el sistema operativo que corresponda.&lt;/p&gt;

&lt;p&gt;Otro elemento que vamos a necesitar tener instalado en nuestro sistema es &lt;code&gt;docker-compose&lt;/code&gt;. De nuevo, si aún no lo tienes instalado, puedes seguir las indicaciones de la &lt;a href="https://docs.docker.com/compose/install/" rel="noopener noreferrer"&gt;documentación oficial&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Por último, dado que este ejemplo está orientado a aplicaciones basadas en JavaScript/TypeScript, necesitamos tener instalado NodeJS (&lt;a href="https://nodejs.org/en/download/" rel="noopener noreferrer"&gt;documentación oficial&lt;/a&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  🏗 Inicialización del proyecto
&lt;/h2&gt;

&lt;p&gt;🔥 Si ya tienes inicializando tu propio proyecto basado en NodeJS, puedes saltarte esta sección 🔥&lt;/p&gt;

&lt;p&gt;Vamos a inicializar nuestro proyecto NodeJS abriendo una consola de comandos, en el directorio donde queramos trabajar, y escribimos el siguiente comando:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm init &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Esta acción nos creará una único archivo &lt;code&gt;package.json&lt;/code&gt; en la raíz de nuestro proyecto, con el siguiente contenido:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;Ahora podemos instalar &lt;a href="https://jestjs.io/" rel="noopener noreferrer"&gt;Jest&lt;/a&gt; ejecutando la siguiente instrucción en nuestra consola de comandos, para incluir esta librería en nuestro proyecto:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;-D&lt;/span&gt; jest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;El siguiente paso es crear la estructura más básica de directorios para el proyecto.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/
|-- /docker &lt;span class="c"&gt;# &amp;lt;= Nuevo directorio.&lt;/span&gt;
|-- /node_modules
|-- /src &lt;span class="c"&gt;# &amp;lt;= Nuevo directorio.&lt;/span&gt;
|-- package-lock.json
|-- package.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  🐳 Definiendo la configuración de Docker
&lt;/h2&gt;

&lt;p&gt;Vamos a tener dos entornos principales (&lt;code&gt;development&lt;/code&gt; y &lt;code&gt;test&lt;/code&gt;) y la idea es tener un único archivo &lt;code&gt;docker-compose.yml&lt;/code&gt; para gestionar los contenedores de ambos entornos.&lt;/p&gt;
&lt;h3&gt;
  
  
  📄 Definición del archivo &lt;code&gt;docker-compose.yml&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Para conseguir nuestro objetivo, dentro del directorio &lt;code&gt;/docker&lt;/code&gt; vamos a crear un único archivo llamado &lt;code&gt;docker-compose.yml&lt;/code&gt;, el cual contendrá el siguiente código:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;Como podemos apreciar, hay varias líneas marcadas como &lt;code&gt;coupling smell&lt;/code&gt;. Esto significa que, con la configuración actual, podemos ejecutar un único contenedor de Docker destinado principalmente para tareas de desarrollo. Por lo tanto, está altamente acoplado a su entorno de ejecución.&lt;/p&gt;

&lt;p&gt;¿No sería genial si fuésemos capaces de reemplazar esas configuraciones definidas directamente en el código, por referencias las cuales vinieran establecidas por algún tipo de archivo de configuración?&lt;/p&gt;

&lt;h3&gt;
  
  
  ⚙ Archivos &lt;code&gt;.env&lt;/code&gt; para contenedores Docker
&lt;/h3&gt;

&lt;p&gt;!Sí! Podemos usar archivos &lt;code&gt;.env&lt;/code&gt; de la misma manera que ya los usamos para nuestras aplicaciones, pero para configurar contenedores de Docker.&lt;/p&gt;

&lt;p&gt;Lo primero que necesitamos hacer es modificar el archivo &lt;code&gt;docker-compose.yml&lt;/code&gt; que acabamos de crear para usar plantillas basadas en llaves, para definir nombres de constantes que reemplazaremos con los valores indicados en nuestros archivos &lt;code&gt;.env&lt;/code&gt;. De este modo, el contenido del archivo &lt;code&gt;docker-compose.yml&lt;/code&gt; quedará de la siguiente manera:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Como podemos ver, hemos reemplazado los valores directamente escritos en el código por referencias del tipo &lt;code&gt;${CONSTANT_NAME}&lt;/code&gt;. El nombre de las variables escrito entre llaves será el nombre de los valores definidos en nuestros archivos &lt;code&gt;.env&lt;/code&gt;. De esta manera, cuando arranquemos el comando &lt;code&gt;docker-compose&lt;/code&gt; usando una opción específica de la línea de comandos que veremos más adelante, el contenido del archivo &lt;code&gt;.env&lt;/code&gt; será reemplazado en nuestro archivo &lt;code&gt;docker-compose.yml&lt;/code&gt; antes de que se cree el contenedor de Docker.&lt;/p&gt;

&lt;p&gt;Ahora es el momento de definir nuestros entornos así que modificamos el contenido del directorio &lt;code&gt;/docker&lt;/code&gt; para que quede tal que así:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/
|-- /docker
|   |-- /dev &lt;span class="c"&gt;# &amp;lt;= Nuevo directorio y archivo.&lt;/span&gt;
|   |   |-- .docker.dev.env
|   |-- /test &lt;span class="c"&gt;# &amp;lt;= Nuevo directorio y archivo.&lt;/span&gt;
|   |   |-- .docker.test.env
|   |-- docker-compose.yml
|-- /node_modules
|-- /src
|-- package-lock.json
|-- package.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Por cada entorno hemos creado un único subdirectorio: &lt;code&gt;dev&lt;/code&gt; y &lt;code&gt;test&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Dentro de cada subdirectorio de entorno hemos creado un archivo &lt;code&gt;.env&lt;/code&gt; específico: &lt;code&gt;.docker.dev.env&lt;/code&gt; y &lt;code&gt;.docker.test.env&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;🙋❓ &lt;strong&gt;¿Sería posible nombrar los archivos de entorno sólo como  &lt;code&gt;.env&lt;/code&gt;?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Sí, es posible y además, no habría ningún problema en ello pero... un nombre de archivo tan descriptivo es una ayuda para nuestro rol como profesionales del desarrollo. Dado que en un mismo proyecto es muy probable que haya múltiples archivos de configuración, es útil ser capaz de diferenciarlos cuando tenemos varios de ellos abiertos, en el editor de código, al mismo tiempo. Esta es la razón por la que los archivos &lt;code&gt;.env&lt;/code&gt; tienen uno nombres tan descriptivos.&lt;/p&gt;

&lt;p&gt;Ahora pasaremos a definir el contenido de nuestros archivos de entornos, para que queden de la siguiente manera:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;y...&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Hay cuatro propiedades a las que debemos prestar atención a la hora de diferenciar entre los dos archivos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;CONTAINER_NAME&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;EXTERNAL_PORT&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;VOLUME_NAME&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CONFIGURATION_PATH&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;La propiedad &lt;code&gt;CONTAINER_NAME&lt;/code&gt; nos permite definir el nombre del contenedor que veremos después de que éste ha sido creado y además, cuando ejecutamos el comando &lt;code&gt;docker ps -a&lt;/code&gt; para listar todos los contenedores presentes en nuestro sistema.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;EXTERNAL_PORT&lt;/code&gt; es una propiedad realmente sensible ya que nos permite definir el puerto que el contenedor tendrán publicado y a través del cual, nuestra aplicación podrá conectarse con él. Es realmente importante tener cuidado con este parámetro porque algunas veces nos interesará tener levantados ambos entornos al mismo tiempo (&lt;code&gt;development&lt;/code&gt; y &lt;code&gt;test&lt;/code&gt;), pero si hemos definido el mismo puerto de acceso para ambos contenedores, el sistema nos lanzará un error al lanzar el segundo contenedor, ya que el puerto estará ocupado.&lt;/p&gt;

&lt;p&gt;La propiedad &lt;code&gt;VOLUME_NAME&lt;/code&gt; definirá el nombre del almacenamiento de datos en nuestro sistema.&lt;/p&gt;

&lt;p&gt;Finalmente, en caso de que hayamos definido cualquier tipo de conjunto de datos para inicializar nuestra base de datos antes de usarla, la propiedad &lt;code&gt;CONFIGURATION_PATH&lt;/code&gt; nos permitirá definir dónde está ubicado ese conjunto de datos.&lt;/p&gt;

&lt;p&gt;🙋‍♀️❓ &lt;strong&gt;Oye pero, ¿qué pasa con la propiedad &lt;code&gt;COMPOSE_PROJECT_NAME&lt;/code&gt;?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Esa es una magnífica pregunta.&lt;/p&gt;

&lt;p&gt;Nuestro primer objetivo es crear un contenedor específico por cada entorno, basándonos en el mismo archivo &lt;code&gt;docker-compose.yml&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Ahora mismo, si ejecutamos nuestro &lt;code&gt;docker-compose&lt;/code&gt; para &lt;code&gt;development&lt;/code&gt;, por ejemplo, crearemos el contenedor con esa definición de entorno y el archivo &lt;code&gt;docker-compose.yml&lt;/code&gt; quedará enlazado a dicho contenedor.&lt;/p&gt;

&lt;p&gt;De este modo, si intentamos ahora arrancar el mismo archivo pero utilizando la configuración para &lt;code&gt;testing&lt;/code&gt;, el resultado final será que hemos actualizado el contenedor previo de &lt;code&gt;development&lt;/code&gt;, sin la configuración para el entorno de &lt;code&gt;testing&lt;/code&gt;. ¿Por qué? Pues porque el archivo de composición está enlazado al contenedor que arrancamos inicialmente.&lt;/p&gt;

&lt;p&gt;Para conseguir nuestro objetivo satisfactoriamente, empleamos la propiedad &lt;code&gt;COMPOSE_PROJECT_NAME&lt;/code&gt; dentro de cada archivo &lt;code&gt;.env&lt;/code&gt; y le asignamos valores diferentes dependiendo del entorno al que pertenezca.&lt;/p&gt;

&lt;p&gt;De esta manera, cada vez que ejecutemos el archivo de composición, dado que el nombre de proyecto es diferente para cada archivo &lt;code&gt;.env&lt;/code&gt;, las modificaciones que se apliquen sólo afectarán al contenedor que corresponda con dicho nombre de proyecto.&lt;/p&gt;

&lt;p&gt;🙋❓ &lt;strong&gt;Vale, bien, pero hemos usado la propiedad &lt;code&gt;COMPOSE_PROJECT_NAME&lt;/code&gt; sólo dentro de nuestros archivos &lt;code&gt;.env&lt;/code&gt; y no en el archivo &lt;code&gt;docker-compose.yml&lt;/code&gt;. ¿Cómo es posible que afecte al resultado final?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Es posible porque esa propiedad es leída directamente por el comando &lt;code&gt;docker-compose&lt;/code&gt; y no es necesario que esté incluida dentro del archivo &lt;code&gt;docker-compose.yml&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;En este enlace puedes encontrar toda la &lt;a href="https://docs.docker.com/compose/reference/envvars/#compose_project_name" rel="noopener noreferrer"&gt;documentación oficial&lt;/a&gt; about &lt;code&gt;COMPOSE_PROJECT_NAME&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  🤹‍♂️ Inicializando la base de datos
&lt;/h2&gt;

&lt;p&gt;🔥 Advertencia: El proceso que se expone a continuación está dirigido a inicializar el contenido de una base de datos MongoDB. Si quieres usar un motor diferente, necesitarás adaptar este proceso así como la configuración del &lt;code&gt;docker-compose.yml&lt;/code&gt; para ello. 🔥&lt;/p&gt;

&lt;p&gt;El concepto más básico que debemos saber, si es que no lo sabemos ya, es que cuando un contenedor basado en MongoDB se ejecuta por primera vez, todos los archivos con extensión &lt;code&gt;.sh&lt;/code&gt; o &lt;code&gt;.js&lt;/code&gt; ubicados en el directorio &lt;code&gt;/docker-entrypoint-initdb.d&lt;/code&gt; dentro del propio contenedor, son ejecutados.&lt;/p&gt;

&lt;p&gt;Esto nos proporciona una manera para inicializar nuestra base de datos.&lt;/p&gt;

&lt;p&gt;Si quieres profundizar en esta propiedad, puedes consultar la &lt;a href="https://hub.docker.com/_/mongo" rel="noopener noreferrer"&gt;documentación de la imagen oficial de MongoDB en Docker&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  🧪 Configuración del entorno de testing
&lt;/h3&gt;

&lt;p&gt;Para ver cómo podemos hacer esto, vamos a empezar por el entorno de &lt;code&gt;testing&lt;/code&gt; así que antes de nada, tenemos que crear la siguiente estructura de archivos dentro del directorio &lt;code&gt;/docker/test&lt;/code&gt; de nuestro proyecto:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/
|-- /docker
|   |-- /dev
|   |   |-- .docker.dev.env
|   |-- /test
|   |   |-- /configureDatabase &lt;span class="c"&gt;# &amp;lt;= Nuevo directorio y archivo.&lt;/span&gt;
|   |   |   |-- initDatabase.js
|   |   |-- .docker.test.env
|   |-- docker-compose.yml
|-- /node_modules
|-- /src
|-- package-lock.json
|-- package.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;El contenido del archivo &lt;code&gt;initDatabase.js&lt;/code&gt; será el siguiente:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;Este script está dividido en tres elementos diferentes.&lt;/p&gt;

&lt;p&gt;La constante &lt;code&gt;apiDatabases&lt;/code&gt; contiene todas las definiciones de bases de datos que queremos crear para nuestro contenedor.&lt;/p&gt;

&lt;p&gt;Cada definición de base de datos contendrá su nombre (&lt;code&gt;dbName&lt;/code&gt;), un array de usuarios (&lt;code&gt;dbUsers&lt;/code&gt;) los cuales estarán autorizados para operar con la base de datos (incluyendo la definición de sus privilegios de acceso) y el conjunto de datos con los que inicializaremos la base de datos.&lt;/p&gt;

&lt;p&gt;La función &lt;code&gt;createDatabaseUser&lt;/code&gt; está destinada a gestionar la información contenida en cada bloque del &lt;code&gt;apiDatabases&lt;/code&gt;, procesar los datos de usuarios y crearlos dentro de la base de datos indicada.&lt;/p&gt;

&lt;p&gt;Finalmente, el bloque &lt;code&gt;try/catch&lt;/code&gt; contiene la magia porque en este bloque iteramos sobre la constante &lt;code&gt;apiDatabase&lt;/code&gt;, conmutamos entre bases de datos y procesamos la información.&lt;/p&gt;

&lt;p&gt;Una vez que hemos analizado este código, si recordamos el contenido de nuestro archivo &lt;code&gt;docker-compose.yml&lt;/code&gt;, dentro de la sección &lt;code&gt;volumes&lt;/code&gt; definimos la siguiente línea:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;- ${CONFIGURATION_PATH}:/docker-entrypoint-initdb.d:rw&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Además, para el entorno de &lt;code&gt;testing&lt;/code&gt;, dentro del archivo &lt;code&gt;.docker.test.env&lt;/code&gt;, configuramos lo siguiente:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;CONFIGURATION_PATH="./test/configureDatabase"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Con esta acción, el proceso &lt;code&gt;docker-compose&lt;/code&gt; está copiando el contenido de la ruta indicada por &lt;code&gt;CONFIGURATION_PATH&lt;/code&gt; dentro del directorio del contenedor &lt;code&gt;/docker-entrypoint-initdb.d:rw&lt;/code&gt; antes de que éste se arranque por primera vez. Así es como estamos definiendo el script de configuración de nuestra base de datos, para que sea ejecutado al iniciarse el contenedor.&lt;/p&gt;

&lt;p&gt;🙋‍♀️❓ &lt;strong&gt;Para esta configuración no estás usando ningún conjunto de datos iniciales. ¿Por qué?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Porque esta será la base de datos de testing y la intención es que se almacenen y eliminen datos ad-hoc en base a los tests que estén ejecutándose en un momento concreto. Por esta razón no tiene sentido que inicialicemos la base de datos con información que vamos a crear/editar/eliminar dinámicamente.&lt;/p&gt;

&lt;h3&gt;
  
  
  🛠 Configuración del entorno de desarrollo
&lt;/h3&gt;

&lt;p&gt;Esta configuración es muy similar a la de &lt;code&gt;testing&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Lo primero que tenemos que hacer es modificar el subdirectorio  &lt;code&gt;/docker/dev&lt;/code&gt; de nuestro proyecto, para que quede tal que así:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/
|-- /docker
|   |-- /dev
|   |   |-- /configureDatabase &lt;span class="c"&gt;# &amp;lt;= Nuevo directorio y archivos.&lt;/span&gt;
|   |   |   |-- initDatabase.js
|   |   |   |-- postsDataToBePersisted.js
|   |   |   |-- usersDataToBePersisted.js
|   |   |-- .docker.dev.env
|   |-- /test
|   |   |-- /configureDatabase
|   |   |   |-- initDatabase.js
|   |   |-- .docker.test.env
|   |-- docker-compose.yml
|-- /node_modules
|-- /src
|-- package-lock.json
|-- package.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Los archivos &lt;code&gt;postsDataToBePersisted.js&lt;/code&gt; y &lt;code&gt;usersDataToBePersisted.js&lt;/code&gt; sólo contienen información estática definida dentro de constantes independientes. Esta información será almacenad en la base de datos indicada, dentro de la colección especificada.&lt;/p&gt;

&lt;p&gt;La estructura de dichos contenidos será la siguiente:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;




&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Por otro lado, el contenido del archivo &lt;code&gt;initDatabase.js&lt;/code&gt; es bastante similar al del entorno de &lt;code&gt;testing&lt;/code&gt; pero un poco más complejo ya que ahora tenemos que gestionar colecciones y datos. De este modo, el resultado final es este:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;En este script hay varias partes que necesitamos analizar.&lt;/p&gt;

&lt;p&gt;En la cabecera tenemos un bloque compuesto por dos llamadas a la función &lt;code&gt;load()&lt;/code&gt; encaminadas a importar los datos preparados y almacenados en las constantes que declaramos en los otros archivos JavaScript.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;🔥 Hay que prestar atención a que la ruta indicada para hacer referencia a los archivos de datos, es relativa al interior de la estructura de ficheros del contenedor de Docker y no a la de nuestro sistema. 🔥&lt;/p&gt;

&lt;p&gt;ℹ️ Si quieres aprender más acerca de cómo ejecutar MongoDB archivos JavaScript en su consola de comandos, echa un vistazo a su &lt;a href="https://docs.mongodb.com/manual/tutorial/write-scripts-for-the-mongo-shell/#execute-a-javascript-file" rel="noopener noreferrer"&gt;documentación oficial&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Después de "importar" las definiciones de las constantes &lt;code&gt;usersToBePersisted&lt;/code&gt; y &lt;code&gt;postsToBePersisted&lt;/code&gt; mediante el uso de la función &lt;code&gt;load()&lt;/code&gt;, estas están disponibles globalmente dentro del contexto de nuestro script de inicialización.&lt;/p&gt;

&lt;p&gt;El siguiente bloque a analizar es el de la constante &lt;code&gt;apiDatabases&lt;/code&gt; donde, además de los campos &lt;code&gt;dbName&lt;/code&gt; y &lt;code&gt;dbUsers&lt;/code&gt; que ya vimos en la configuración de &lt;code&gt;testing&lt;/code&gt;, en este caso el array &lt;code&gt;dbData&lt;/code&gt; es un poco más complejo.&lt;/p&gt;

&lt;p&gt;Cada objeto declarado dentro del array &lt;code&gt;dbData&lt;/code&gt; define el nombre de la colección así como el conjunto de datos que debe ser almacenado en dicha colección.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Ahora nos encontramos con la definición de la constante &lt;code&gt;collections&lt;/code&gt;. Es la definición de un mapa de funciones el cual contiene las acciones que se deben ejecutar por cada colección definida en el bloque &lt;code&gt;apiDatabases.dbData&lt;/code&gt;.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Como podemos ver, en estas funciones estamos invocando directamente instrucciones nativas de MongoDB.&lt;/p&gt;

&lt;p&gt;La siguiente función que nos encontramos es &lt;code&gt;createDatabaseUsers&lt;/code&gt; la cual no tiene diferencias con la que definimos para el entorno de &lt;code&gt;testing&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Justo antes de terminar el archivo, encontramos la función &lt;code&gt;populateDatabase&lt;/code&gt;.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;En esta función es donde vamos a través de las colecciones de bases de datos, insertando los datos asignados y aquí es donde invocamos al mapa de funciones &lt;code&gt;collections&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Finalmente tenemos el bloque &lt;code&gt;try/catch&lt;/code&gt; donde ejecutamos las mismas acciones que para el entorno &lt;code&gt;testing&lt;/code&gt; pero hemos incluido la llamada a la función &lt;code&gt;populateDatabase&lt;/code&gt;.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;De esta manera es como hemos podido configurar el script de inicialización para nuestra base de datos del entorno de desarrollo.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧩 Comando de Docker Compose
&lt;/h2&gt;

&lt;p&gt;Una vez que hemos definido el archivo de composición así como el conjunto de datos que inicializará nuestra base de datos, tenemos que definir los campos mediante los cuales, operaremos nuestros contenedores.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;🔥 Hay que prestar especial atención al hecho de que las rutas empleadas están referenciadas a la raíz de nuestro proyecto. 🔥&lt;/p&gt;

&lt;h2&gt;
  
  
  🌟 Configurando los últimos detalles para NodeJS
&lt;/h2&gt;

&lt;p&gt;El último paso es definir los scripts necesarios dentro de nuestro archivo &lt;code&gt;package.json&lt;/code&gt;.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Para proporcionar una mejor modularización de los scripts, es muy recomendable que se dividan en diferentes scripts atómicos y luego, crear otros scripts diferentes para agrupar aquellos que sean más específicos.&lt;/p&gt;

&lt;p&gt;Por ejemplo, en este código hemos definido los scripts &lt;code&gt;dev_infra:up&lt;/code&gt;, &lt;code&gt;dev_infra:down&lt;/code&gt;, &lt;code&gt;test:run&lt;/code&gt;, &lt;code&gt;test_infra:up&lt;/code&gt; and &lt;code&gt;test_infra:down&lt;/code&gt; que son atómicos porque definen una acción simple y serán los encargados de arrancar y para los contenedores para cada entorno, así como de ejecutar la suite de test.&lt;/p&gt;

&lt;p&gt;Por el contrario tenemos los scripts &lt;code&gt;build:dev&lt;/code&gt; y &lt;code&gt;test&lt;/code&gt; que son compuestos ya que cada uno involucra varios scripts atómicos.&lt;/p&gt;

&lt;h2&gt;
  
  
  🤔 FAQ
&lt;/h2&gt;

&lt;p&gt;❓ &lt;strong&gt;¿Qué pasa si la suite de testing se para repentinamente porque alguno de los tests ha fallado?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No hay que preocuparse por esto porque es verdad que la infraestructura de testing continuará ejecutándose pero tenemos dos opciones:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Mantener en ejecución el contenedor ya que la próxima vez que ejecutemos la suite de tests, el comando &lt;code&gt;docker-compose&lt;/code&gt; actualizará el contenido del contenedor.&lt;/li&gt;
&lt;li&gt;Ejecutar manualmente el script de apagado del contenedor de testing.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;❓ &lt;strong&gt;¿Qué sucede si en lugar de una base de datos, necesitamos ejecutar algún servicio más complejo como una API?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Sólo necesitamos configurar los contenedores/servicios necesarios dentro del archivo &lt;code&gt;docker-compose.yml&lt;/code&gt;, prestando especial atención a la configuración &lt;code&gt;.env&lt;/code&gt; para cada entorno.&lt;/p&gt;

&lt;p&gt;No importa lo que queramos incluir en nuestros contenedores. Lo importante aquí es que vamos a ser capaces de arrancarlos y detenerlos cuando nuestro proyecto lo necesite.&lt;/p&gt;

&lt;h2&gt;
  
  
  👋 Conclusiones finales
&lt;/h2&gt;

&lt;p&gt;Con esta configuración podemos incluir la gestión de la infraestructura necesaria para nuestros proyectos con NodeJS.&lt;/p&gt;

&lt;p&gt;Este tipo de configuraciones nos proporciona un nivel de desacoplamiento que aumenta nuestra independencia durante la fase de desarrollo, ya que vamos a tratar elementos externos a nuestro código como una caja negra con la cual interactuar.&lt;/p&gt;

&lt;p&gt;Otro punto interesante de esta estrategia es que cada vez que arrancamos el contenedor mediante el comando &lt;code&gt;docker-compose&lt;/code&gt;, éste es totalmente renovado lo que nos permite asegurar que nuestras suites de tests van a ejecutarse sobre sistemas completamente limpios.&lt;/p&gt;

&lt;p&gt;Además, mantendremos limpio nuestro propio sistema ya que no necesitaremos instalar ningún tipo de aplicación auxiliar porque todas ellas, estarán incluidas en diferentes contenedores que compondrán nuestras infraestructura de pruebas.&lt;/p&gt;

&lt;p&gt;Sólo una advertencia a este respecto, trata de mantener el contenido de dichos contenedores lo más actualizado posible para, de ese modo, hacer las pruebas contra un entorno lo más parecido posible al que podemos encontrarnos en producción.&lt;/p&gt;

&lt;p&gt;Espero que este contenido te sea útil. Si tienes cualquier pregunta, siéntete totalmente libre de contactar conmigo. Aquí están mis perfiles de &lt;a href="https://twitter.com/ddialar" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;, &lt;a href="https://linkedin.com/in/ddialar" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; y &lt;a href="https://github.com/ddialar" rel="noopener noreferrer"&gt;Github&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  🙏 Reconocimientos y agradecimientos
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://twitter.com/jntramosbonilla" rel="noopener noreferrer"&gt;Jonatan Ramos&lt;/a&gt; por darme la pista del &lt;code&gt;COMPOSE_PROJECT_NAME&lt;/code&gt; para crear archivos  &lt;code&gt;docker-compose.yml&lt;/code&gt; único que se comparten entre diferentes entornos.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>docker</category>
      <category>javascript</category>
      <category>typescript</category>
      <category>tdd</category>
    </item>
    <item>
      <title>Mocking our development and testing infrastructures with Docker</title>
      <dc:creator>Dailos Rafael Díaz Lara</dc:creator>
      <pubDate>Mon, 28 Dec 2020 16:39:18 +0000</pubDate>
      <link>https://dev.to/ddialar/mocking-our-development-and-testing-infrastructures-with-docker-4nf0</link>
      <guid>https://dev.to/ddialar/mocking-our-development-and-testing-infrastructures-with-docker-4nf0</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/ddialar/definiendo-nuestras-infraestructuras-de-desarrollo-y-testing-con-docker-12ai"&gt;🇪🇸 Versión en español&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🎯 Context
&lt;/h2&gt;

&lt;p&gt;When we are creating a new application or feature, we usually need to send requests to independent resources such as databases or mocked services, but it's obvious that running these kind of actions against deployed servers have a cost.&lt;/p&gt;

&lt;p&gt;On these kind of situations is where the isolation of systems provided by Docker containers becomes really useful.&lt;/p&gt;

&lt;p&gt;In this post we are going to see how to use Docker for getting up the minimum infrastructure which allows us to run the development and/or testing tasks... locally.&lt;/p&gt;

&lt;p&gt;The main target of this text is to show how to user a single &lt;code&gt;docker-compose.yml&lt;/code&gt; file for both environments, using different &lt;code&gt;.env&lt;/code&gt; files to customize the specific container for every one, development and testing.&lt;/p&gt;

&lt;p&gt;In addition, we will focus on how to start up the new container for testing purposes, execute the tests and then, shut down the container.&lt;/p&gt;

&lt;h2&gt;
  
  
  💻 System configuration
&lt;/h2&gt;

&lt;p&gt;If we are going to be talking about Docker, it's obvious that we need to have it already installed in our system. If you don't have it yet, you can follow the &lt;a href="https://docs.docker.com/get-docker/" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt; instruction for your specific operative system.&lt;/p&gt;

&lt;p&gt;Another element that we are going to need is &lt;code&gt;docker-compose&lt;/code&gt;. Once again, if you have not installed it yet, you can follow the &lt;a href="https://docs.docker.com/compose/install/" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Finally, due to this example is aimed to applications development based on JavaScript/TypeScript, we need to have installed NodeJS (&lt;a href="https://nodejs.org/en/download/" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  🏗 Project initialization
&lt;/h2&gt;

&lt;p&gt;🔥 If you have already started your NodeJS project, you can skip this section 🔥&lt;/p&gt;

&lt;p&gt;We are going to initialize our NodeJS project opening a CLI, in the folder where we want to work, and typing the next command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm init &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This action will create a single &lt;code&gt;package.json&lt;/code&gt; file in the root of our project, with the next content:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;Now we can install &lt;a href="https://jestjs.io/" rel="noopener noreferrer"&gt;Jest&lt;/a&gt; running the next command in our CLI, in order to include this library in the project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;-D&lt;/span&gt; jest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The next step is to create the most basic folder structure for the project.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/
|-- /docker &lt;span class="c"&gt;# &amp;lt;= New subfolder.&lt;/span&gt;
|-- /node_modules
|-- /src &lt;span class="c"&gt;# &amp;lt;= New subfolder.&lt;/span&gt;
|-- package-lock.json
|-- package.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  🐳 Setting up the Docker configuration
&lt;/h2&gt;

&lt;p&gt;We are going to have two main environments (&lt;code&gt;development&lt;/code&gt; and &lt;code&gt;test&lt;/code&gt;) and the main idea is to have a single &lt;code&gt;docker-compose.yml&lt;/code&gt; file in order to manage both environment containers.&lt;/p&gt;
&lt;h3&gt;
  
  
  📄 &lt;code&gt;docker-compose.yml&lt;/code&gt; file definition
&lt;/h3&gt;

&lt;p&gt;In order to reach that goal, inside the &lt;code&gt;/docker&lt;/code&gt; folder we are going to create our single &lt;code&gt;docker-compose.yml&lt;/code&gt; file which will contains the next code:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;As we can see, there are several lines marked as &lt;code&gt;coupling smell&lt;/code&gt;. It means that, with the current configuration, we can only run a single Docker container mainly aimed for development tasks. So we are strongly coupled to this environment.&lt;/p&gt;

&lt;p&gt;Wouldn't it be fine whether we were able to replace those hardcoded configurations by references and that those references are defined by any kind of configuration file?&lt;/p&gt;

&lt;h3&gt;
  
  
  ⚙ &lt;code&gt;.env&lt;/code&gt; files for Docker containers
&lt;/h3&gt;

&lt;p&gt;Yes!!! We can use &lt;code&gt;.env&lt;/code&gt; files in the same way we do that for our applications, but for configuring Docker containers.&lt;/p&gt;

&lt;p&gt;First at all, we need to edit the &lt;code&gt;docker-compose.yml&lt;/code&gt; file we created recently in order to use &lt;code&gt;curly-braces&lt;/code&gt; templates to define the constant names which will be replaced with the value defined in our &lt;code&gt;.env&lt;/code&gt; files. This way, the &lt;code&gt;docker-compose.yml&lt;/code&gt; file content will be defined this way:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;As we can see, we have replaced the hardcoded values by &lt;code&gt;${CONSTANT_NAME}&lt;/code&gt; references. The name typed between curly braces will be the name of the values defined into our &lt;code&gt;.env&lt;/code&gt; files. This way, when we run the &lt;code&gt;docker-compose&lt;/code&gt; command, using some special CLI options that we will see later, the &lt;code&gt;.env&lt;/code&gt; file content will be replaced into our &lt;code&gt;docker-compose.yml&lt;/code&gt; file before creating the Docker container.&lt;/p&gt;

&lt;p&gt;Now it's time to define our environments so we are edit the &lt;code&gt;/docker&lt;/code&gt; folder content this way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/
|-- /docker
|   |-- /dev
|   |   |-- .docker.dev.env
|   |-- /test
|   |   |-- .docker.test.env
|   |-- docker-compose.yml
|-- /node_modules
|-- /src
|-- package-lock.json
|-- package.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;For every environment, we have created a single subfolder: &lt;code&gt;dev&lt;/code&gt; and &lt;code&gt;test&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Into every environment subfolder we have created a specific &lt;code&gt;.env&lt;/code&gt; file: &lt;code&gt;.docker.dev.env&lt;/code&gt; and &lt;code&gt;.docker.test.env&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;🙋❓ &lt;strong&gt;Could it be possible just naming the environment files as &lt;code&gt;.env&lt;/code&gt;?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes, it could and besides, there wouldn't be any issue with it but... a so descriptive file name is a kindly help for us as developers. Due to in the same project it's really likely there are multiple configuration files, it's useful to be able to differentiate between then when we have several ones open, at the same time, in the code editor. That is the reason why the &lt;code&gt;.env&lt;/code&gt; files have a so descriptive names.&lt;/p&gt;

&lt;p&gt;Now it's time to define the content of our environment files this way:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;and...&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;There are four properties which you must pay attention in order to differentiate both files:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;CONTAINER_NAME&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;EXTERNAL_PORT&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;VOLUME_NAME&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CONFIGURATION_PATH&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;CONTAINER_NAME&lt;/code&gt; property will define the name that we will see after the container is created and we run the command &lt;code&gt;docker ps -a&lt;/code&gt; in order to list the whole containers in our system.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;EXTERNAL_PORT&lt;/code&gt; is a really sensitive property due to it will define the connection port published by the container through which our application will connect with it. It's really important to be careful with this parameter because some times we will want to run the testing suite at the same time we have up the application in development mode, so if we define the same port for both containers, the system will throw an error because the selected port is already in use.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;VOLUME_NAME&lt;/code&gt; property will define the data storage name in our system.&lt;/p&gt;

&lt;p&gt;Finally, in case we have defined any kind of data to prepopulate the database before using it, the &lt;code&gt;CONFIGURATION_PATH&lt;/code&gt; property will allow us to define where that set of data is located.&lt;/p&gt;

&lt;p&gt;🙋‍♀️❓ &lt;strong&gt;Hey but, what about the &lt;code&gt;COMPOSE_PROJECT_NAME&lt;/code&gt; property?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That's a great question.&lt;/p&gt;

&lt;p&gt;Our main goal is to create a specific container per environment, based on the same &lt;code&gt;docker-compose.yml&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Right now, if we run our &lt;code&gt;docker-compose&lt;/code&gt; for &lt;code&gt;development&lt;/code&gt;, for instance, we will create the container with that environment definition and the &lt;code&gt;docker-compose.yml&lt;/code&gt; file will be bound with that container.&lt;/p&gt;

&lt;p&gt;This way, if we try to run the same file but setting the &lt;code&gt;testing&lt;/code&gt; configuration, the final result will be an update of the previous &lt;code&gt;development&lt;/code&gt; container, without the defined &lt;code&gt;testing&lt;/code&gt; configuration. Why? Because the compose file is bound to the first started container.&lt;/p&gt;

&lt;p&gt;In order to reach our target successfully, we use the &lt;code&gt;COMPOSE_PROJECT_NAME&lt;/code&gt; property into every &lt;code&gt;.env&lt;/code&gt; file and we set a different value depending on the environment.&lt;/p&gt;

&lt;p&gt;This way, every time we run the compose file, due to the project name is different for every &lt;code&gt;.env&lt;/code&gt; file, the modifications will only affect to the containers bound with every project name.&lt;/p&gt;

&lt;p&gt;🙋❓ &lt;strong&gt;That's fine but we are using &lt;code&gt;COMPOSE_PROJECT_NAME&lt;/code&gt; only into our &lt;code&gt;.env&lt;/code&gt; files and not in the &lt;code&gt;docker-compose.yml&lt;/code&gt; one. How is possible that it affect to the final result?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It's possible because that property is read directly by &lt;code&gt;docker-compose&lt;/code&gt; command and it's not needed to be included into the &lt;code&gt;docker-compose.yml&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;In this link you have the whole &lt;a href="https://docs.docker.com/compose/reference/envvars/#compose_project_name" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt; about &lt;code&gt;COMPOSE_PROJECT_NAME&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  🤹‍♂️ Populating the database
&lt;/h2&gt;

&lt;p&gt;🔥 Caveat: The next explained process is aimed to populate a MongoDB database. If you want to use a different engine, you have to adapt this process and the &lt;code&gt;docker-compose.yml&lt;/code&gt; configuration for it. 🔥&lt;/p&gt;

&lt;p&gt;The most basic concept we must know, if we already don't, is that when a MongoDB based on container starts first time, the whole files with extension &lt;code&gt;.sh&lt;/code&gt; or &lt;code&gt;.js&lt;/code&gt; located into the container folder &lt;code&gt;/docker-entrypoint-initdb.d&lt;/code&gt; are executed.&lt;/p&gt;

&lt;p&gt;This situation provides us a way to initialize our database.&lt;/p&gt;

&lt;p&gt;If you want to get deeper about it, you can find the whole information about it in this link of the &lt;a href="https://hub.docker.com/_/mongo" rel="noopener noreferrer"&gt;MongoDB Docker image documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  🧪 Testing environment configuration
&lt;/h3&gt;

&lt;p&gt;In order to see how we can do that, we are going to start by the &lt;code&gt;testing&lt;/code&gt; environment so first at all, we have to create the next file structure into the &lt;code&gt;/docker/test&lt;/code&gt; folder of our project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/
|-- /docker
|   |-- /dev
|   |   |-- .docker.dev.env
|   |-- /test
|   |   |-- /configureDatabase &lt;span class="c"&gt;# &amp;lt;= New subfolder and file.&lt;/span&gt;
|   |   |   |-- initDatabase.js
|   |   |-- .docker.test.env
|   |-- docker-compose.yml
|-- /node_modules
|-- /src
|-- package-lock.json
|-- package.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The content of the &lt;code&gt;initDatabase.js&lt;/code&gt; file will be the next one:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;This script is divided in three different elements.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;apiDatabases&lt;/code&gt; constant contains the whole databases definitions that we want to create for this container.&lt;/p&gt;

&lt;p&gt;Every database definition will contain its name (&lt;code&gt;dbName&lt;/code&gt;), an array of users (&lt;code&gt;dbUsers&lt;/code&gt;) whose will be allowed to operate with the database (including their accessing privilege definitions) and the dataset which we will populate the database.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;createDatabaseUser&lt;/code&gt; function is focused on handle the information contained into every &lt;code&gt;apiDatabases&lt;/code&gt; block, process the users data and create them into the specified database.&lt;/p&gt;

&lt;p&gt;Finally the &lt;code&gt;try/catch&lt;/code&gt; block contains the magic because in this block we iterate over the &lt;code&gt;apiDatabases&lt;/code&gt; constant, switch between databases and process the information.&lt;/p&gt;

&lt;p&gt;Once we have checked this code, if we remember our &lt;code&gt;docker-compose.yml&lt;/code&gt; file content, into the &lt;code&gt;volumes&lt;/code&gt; section we defined the next line:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;- ${CONFIGURATION_PATH}:/docker-entrypoint-initdb.d:rw&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;In addition, for the &lt;code&gt;testing&lt;/code&gt; environment, into the &lt;code&gt;.docker.test.env&lt;/code&gt; file we set this configuration:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;CONFIGURATION_PATH="./test/configureDatabase"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;With this action, the &lt;code&gt;docker-compose&lt;/code&gt; process is copying the content of the path defined by &lt;code&gt;CONFIGURATION_PATH&lt;/code&gt; into the container &lt;code&gt;/docker-entrypoint-initdb.d:rw&lt;/code&gt; before it's run first time. So we are setting our database configuration script to be executed in the container start up.&lt;/p&gt;

&lt;p&gt;🙋‍♀️❓ &lt;strong&gt;For this configuration you are not setting any initial data. Why?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Because it will be the testing database so the intention is to persist and remove data ad-hoc based on the tests that are running in a specific moment. By this reason, it has not sense to populate this database with mocked information once we are going to create/edit/delete it dynamically.&lt;/p&gt;

&lt;h3&gt;
  
  
  🛠 Development environment configuration
&lt;/h3&gt;

&lt;p&gt;This configuration is pretty similar to the &lt;code&gt;testing&lt;/code&gt; one.&lt;/p&gt;

&lt;p&gt;First at all, we have to modify the &lt;code&gt;/docker/dev&lt;/code&gt; subfolder content in our project, in order to get this result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/
|-- /docker
|   |-- /dev
|   |   |-- /configureDatabase &lt;span class="c"&gt;# &amp;lt;= New subfolder and files.&lt;/span&gt;
|   |   |   |-- initDatabase.js
|   |   |   |-- postsDataToBePersisted.js
|   |   |   |-- usersDataToBePersisted.js
|   |   |-- .docker.dev.env
|   |-- /test
|   |   |-- /configureDatabase
|   |   |   |-- initDatabase.js
|   |   |-- .docker.test.env
|   |-- docker-compose.yml
|-- /node_modules
|-- /src
|-- package-lock.json
|-- package.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The &lt;code&gt;postsDataToBePersisted.js&lt;/code&gt; and &lt;code&gt;usersDataToBePersisted.js&lt;/code&gt; files only contain static data defined into independent constants. That information will be stored in the defined database, into the specified collection.&lt;/p&gt;

&lt;p&gt;The structure for the content included into these files is like that:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;




&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;In the other hand, the content of &lt;code&gt;initDatabase.js&lt;/code&gt; file is pretty similar to the &lt;code&gt;testing&lt;/code&gt; environment definition but a little bit complex due to we have to manage collections and data. So the final result is this one:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;At this script there are several parts that we need to analyze.&lt;/p&gt;

&lt;p&gt;The header block composed by two &lt;code&gt;load()&lt;/code&gt; function calls which are used in order to import the mocked data constants declarations that we did in the other JavaScript files.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;🔥 Pay attention to the full data location path is referenced to the inner Docker container file structure, not to our system. 🔥&lt;/p&gt;

&lt;p&gt;ℹ️ If you want to learn more about how MongoDB executes JavaScript files in its console, take a look to the &lt;a href="https://docs.mongodb.com/manual/tutorial/write-scripts-for-the-mongo-shell/#execute-a-javascript-file" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After "importing" the &lt;code&gt;usersToBePersisted&lt;/code&gt; and &lt;code&gt;postsToBePersisted&lt;/code&gt; constants definitions via &lt;code&gt;load()&lt;/code&gt; function, they are globally available into the context of our initialization script.&lt;/p&gt;

&lt;p&gt;The next block to be analyzed is the &lt;code&gt;apiDatabases&lt;/code&gt; constant definition where besides the &lt;code&gt;dbName&lt;/code&gt; and &lt;code&gt;dbUsers&lt;/code&gt; that we covered in the &lt;code&gt;testing&lt;/code&gt; configuration, in this case the &lt;code&gt;dbData&lt;/code&gt; array is a little bit more complex.&lt;/p&gt;

&lt;p&gt;Every object declared into the &lt;code&gt;dbData&lt;/code&gt; array defines the collection name as well as the dataset that must be persisted in that collection.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Now we find the &lt;code&gt;collections&lt;/code&gt; constant definition. It's a set of mapped functions (or object lookup) which contains the actions to execute for every collection defined into the &lt;code&gt;apiDatabases.dbData&lt;/code&gt; block.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;As we can see, in these functions we are directly invoking native MongoDB instructions.&lt;/p&gt;

&lt;p&gt;The next function is &lt;code&gt;createDatabaseUsers&lt;/code&gt; which has not differences with the defined for the &lt;code&gt;testing&lt;/code&gt; environment.&lt;/p&gt;

&lt;p&gt;Just before ending the script file we can find the &lt;code&gt;populateDatabase&lt;/code&gt; function.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;In this function we go through the database collections inserting the assigned data and here is where we invoke the &lt;code&gt;collections&lt;/code&gt; mapped functions object.&lt;/p&gt;

&lt;p&gt;Finally we have the &lt;code&gt;try/catch&lt;/code&gt; block where we run the same actions that we did for the &lt;code&gt;testing&lt;/code&gt; environment but we have included the &lt;code&gt;populateDatabase&lt;/code&gt; function call.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;This way is how we can configure the initialization script for our &lt;code&gt;development&lt;/code&gt; environment database.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧩 Docker Compose commands
&lt;/h2&gt;

&lt;p&gt;Once we have defined the composing file as well as the dataset that will initialize our databases, we have to define the commands which will run our containers.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;🔥 Pay attention to the used paths are referenced to our project root. 🔥&lt;/p&gt;

&lt;h2&gt;
  
  
  🌟 Setting the final NodeJS commands
&lt;/h2&gt;

&lt;p&gt;The final step is to define the needed scripts into our &lt;code&gt;package.json&lt;/code&gt; file.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;In order to provide a better modularization of scripts, it's strongly recommended to divide the different scripts in atomic ones and then, create new ones which group the more specific ones.&lt;/p&gt;

&lt;p&gt;For instance, in this code we have defined the &lt;code&gt;dev_infra:up&lt;/code&gt;, &lt;code&gt;dev_infra:down&lt;/code&gt;, &lt;code&gt;test:run&lt;/code&gt;, &lt;code&gt;test_infra:up&lt;/code&gt; and &lt;code&gt;test_infra:down&lt;/code&gt; scripts which are atomic because define a single action to do and will be in charge to start and turning off the containers for every environment as well as to run the testing suite.&lt;/p&gt;

&lt;p&gt;In opposite we have the &lt;code&gt;build:dev&lt;/code&gt; and &lt;code&gt;test&lt;/code&gt; scripts which are composed due to they include several atomic actions.&lt;/p&gt;

&lt;h2&gt;
  
  
  🤔 FAQ
&lt;/h2&gt;

&lt;p&gt;❓ &lt;strong&gt;What happen if the testing suite suddenly stops because any test fails?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Don't worry about that because it's true that the testing infrastructure will keep running but we have two options:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;To keep it running so the next time we run the testing suite, the &lt;code&gt;docker-compose&lt;/code&gt; command will update the current container.&lt;/li&gt;
&lt;li&gt;To run manually the shutting down script for the testing container.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;❓ &lt;strong&gt;What happen whether instead of a database we need to run a more complex service like an API?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We just need to configure the needed containers/services into the &lt;code&gt;docker-compose.yml&lt;/code&gt; file, paying special attention to the &lt;code&gt;.env&lt;/code&gt; configurations for every environment.&lt;/p&gt;

&lt;p&gt;It doesn't matter what we wrap and/or include in our container/s. The important point here is that we are going to be able to start and turning off them when our project needs it.&lt;/p&gt;

&lt;h2&gt;
  
  
  👋 Final words
&lt;/h2&gt;

&lt;p&gt;With this configuration, we can include infrastructure management to our NodeJS based on project.&lt;/p&gt;

&lt;p&gt;This kind of configuration provides us a decoupling level that will increase our independency during the development period, because we are going to treat the external elements to our code as a black box which we interact.&lt;/p&gt;

&lt;p&gt;Another interesting point for this strategy is that every time we start up the container via &lt;code&gt;docker-compose&lt;/code&gt;, it's totally renewed so we can be sure that our testing suites are going to be run in a completely clean system.&lt;/p&gt;

&lt;p&gt;In addition, we will keep clean our system due to we don't need to install any auxiliar application on it because all of them will be included into the different containers that compose our mocked infrastructure.&lt;/p&gt;

&lt;p&gt;Just a caveat, try to keep the content of the containers up-to-date in order to work with the closest production environment conditions as it's possible.&lt;/p&gt;

&lt;p&gt;I hope this tip is useful for you. If you have any question, feel free to contact me. Here there are my &lt;a href="https://twitter.com/ddialar" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;, &lt;a href="https://linkedin.com/in/ddialar" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; and &lt;a href="https://github.com/ddialar" rel="noopener noreferrer"&gt;Github&lt;/a&gt; profiles.&lt;/p&gt;

&lt;h2&gt;
  
  
  🙏 Credits and thanks
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://twitter.com/jntramosbonilla" rel="noopener noreferrer"&gt;Jonatan Ramos&lt;/a&gt; for providing the clue of &lt;code&gt;COMPOSE_PROJECT_NAME&lt;/code&gt; to create a single &lt;code&gt;docker-compose.yml&lt;/code&gt; file shared between different environments.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>docker</category>
      <category>javascript</category>
      <category>typescript</category>
      <category>tdd</category>
    </item>
    <item>
      <title>De if/switch a mapped functions (a.k.a. object lookup)</title>
      <dc:creator>Dailos Rafael Díaz Lara</dc:creator>
      <pubDate>Mon, 14 Dec 2020 22:12:23 +0000</pubDate>
      <link>https://dev.to/ddialar/de-if-switch-a-mapped-functions-a-k-a-object-lookup-1pb8</link>
      <guid>https://dev.to/ddialar/de-if-switch-a-mapped-functions-a-k-a-object-lookup-1pb8</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/ddialar/from-if-switch-to-mapped-functions-a-k-a-object-lookup-i3d"&gt;🇬🇧 English version&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🎯 Objetivo
&lt;/h2&gt;

&lt;p&gt;Este texto está orientado a proporcionar una alternativa para aquellas situaciones donde nuestro código debe estructurarse para ejecutar una u otra función, dependiendo de un conjunto definido de posibles condiciones.&lt;/p&gt;

&lt;p&gt;En ningún momento es mi intención criticar el uso de &lt;code&gt;if/else&lt;/code&gt; o de &lt;code&gt;switch/case&lt;/code&gt;. Mi único objetivo es proporcionar una propuesta diferente con que mejorar el mantenimiento y la escalabilidad de nuestro código.&lt;/p&gt;

&lt;p&gt;Una vez dicho esto... empecemos!!!&lt;/p&gt;

&lt;h2&gt;
  
  
  📚 Instrucción if/else
&lt;/h2&gt;

&lt;p&gt;Desde que empezamos a aprender a programar, el primer control de flujo de información que aprendemos es el &lt;code&gt;if/else&lt;/code&gt; (&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/if...else" rel="noopener noreferrer"&gt;MDN if/else documentation&lt;/a&gt;). De este modo, cuando ya lo hemos aprendido, es realmente fácil utilizarlo.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Incluso cuando la cantidad de posibles opciones se incrementa, podemos encadenar multiples &lt;code&gt;if/else&lt;/code&gt;.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Además, cuando tenemos varias opciones que deben tratarse de la misma manera, es decir que comparten la misma lógica de negocio, podemos usar operadores booleanos (el &lt;code&gt;OR&lt;/code&gt; en este caso), para agrupar todas esas opciones bajo el mismo bloque de código.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Todo esto está genial pero cuando los posibles casos superan las dos o tres opciones, el código empieza a parece un poco sucio.&lt;/p&gt;

&lt;h4&gt;
  
  
  Pros (✅) y contras (👎)
&lt;/h4&gt;

&lt;p&gt;✅ Es la manera más fácil de controlar el flujo de información.&lt;br&gt;
✅ Es relativamente fácil de aprender.&lt;br&gt;
✅ discriminar entre dos posibles opciones es realmente cómodo.&lt;/p&gt;

&lt;p&gt;👎 Cuando gestionamos más de tres opciones, el código empieza a parece un poco sucio..&lt;br&gt;
👎 Encadenar múltiples opciones disminuye la legibilidad y el mantenimiento de nuestro código.&lt;br&gt;
👎 Agrupar opciones empleando operadores booleanos puede hacer más complicadas las reglas de comparación en cada situación.&lt;br&gt;
👎 Para una cantidad relativamente grande de casos posibles, es más lento ya que cada condición deber ser comprobada hasta alcanzar aquella que coincida con el valor de referencia proporcionado.&lt;/p&gt;
&lt;h2&gt;
  
  
  🤓 Instrucción switch/case
&lt;/h2&gt;

&lt;p&gt;Cuando queremos mejorar la legibilidad y mantenimiento de nuestro código debido a que tenemos múltiples opciones que gestionar, es cuando aprendemos la alternativa al &lt;code&gt;if/else&lt;/code&gt;, es decir, el &lt;code&gt;switch/case&lt;/code&gt; (&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/switch" rel="noopener noreferrer"&gt;MDN switch/case documentation&lt;/a&gt;).&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;De la misma manera que hacíamos con el &lt;code&gt;if/else&lt;/code&gt;, con el &lt;code&gt;switch/case&lt;/code&gt; también podremos agrupar opciones pero en este caso, no necesitamos utilizar ningún operador booleano. Sólo necesitamos mantener unidos los diferentes casos a agrupar.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Como ya sabrás, esto es posible gracias a que la ejecución del &lt;code&gt;switch/case&lt;/code&gt; es un proceso secuencial, donde cada posible caso definido en el conjunto de opciones, es comparado con la referencia proporcionada.&lt;/p&gt;

&lt;p&gt;Si ambos valores coincide, el bloque de código incluido en ese caso se ejecuta y, si no hay una instrucción &lt;code&gt;break&lt;/code&gt; o &lt;code&gt;return&lt;/code&gt; al final de dicho bloque de código, el siguiente caso será comprobado hasta encontrar la próxima coincidencia o hasta llegar al bloque &lt;code&gt;default&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Basándonos en esto, para agrupar múltiples opciones las cuales van a ser gestionadas por el mismo bloque de código, sólo necesitamos definir el caso para el valor deseado, sin ningún tipo de lógica de negocio. De este modo seremos capaces de encadenar múltiples opciones para el mismo resultado.&lt;/p&gt;

&lt;h4&gt;
  
  
  Pros (✅) y contras (👎)
&lt;/h4&gt;

&lt;p&gt;✅ Proporciona una mejor estructura del código que al usar instrucciones &lt;code&gt;if/else&lt;/code&gt;.&lt;br&gt;
✅ Es posible crear agrupamiento de casos de una manera más clara que con instrucciones &lt;code&gt;if/else&lt;/code&gt;.&lt;br&gt;
✅ Es realmente sencillo discriminar entre más de dos opciones.&lt;/p&gt;

&lt;p&gt;👎 Tenemos que estar pendiente de completar todos los bloques de código con una instrucción &lt;code&gt;break&lt;/code&gt; o &lt;code&gt;return&lt;/code&gt;. Si nos olvidamos de hacerlo, nos podemos meter en un buen lío.&lt;br&gt;
👎 Para cantidades relativamente grandes de casos, es lento dado que cada condición debe ser comprobada hasta llegar a aquella que coincide con la referencia que le hemos proporcionado.&lt;/p&gt;
&lt;h2&gt;
  
  
  🔥 Mapped functions
&lt;/h2&gt;

&lt;p&gt;Esta es una estrategia poco conocida (también llamada &lt;code&gt;object lookup&lt;/code&gt;) y está destinada a mejorar determinados aspectos del uso de instrucciones &lt;code&gt;if/else&lt;/code&gt; y &lt;code&gt;switch/case&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;La idea es aprovechar el comportamiento de los objetos de JavaScript para usar sus claves como mapa de referencias y acceder directamente a lógica de negocio específica.&lt;/p&gt;

&lt;p&gt;Antes de nada, necesitamos tener definidos los posibles casos que van a ser gestionados.&lt;/p&gt;

&lt;p&gt;Cada caso individual será asociado a una clave del objeto literal.&lt;/p&gt;

&lt;p&gt;Una vez hemos creado nuestro objeto, usaremos el estilo de acceso array para ejecutar el código de cada caso individual.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h4&gt;
  
  
  Pros (✅) y contras (👎)
&lt;/h4&gt;

&lt;p&gt;✅ Proporciona una estructuración del código mejor que la que obtenemos al usar instrucciones &lt;code&gt;if/else&lt;/code&gt; y &lt;code&gt;switch/case&lt;/code&gt;.&lt;br&gt;
✅ No hay agrupamiento de posibles casos dado que cada uno de ellos tiene definida su propia lógica de negocio.&lt;br&gt;
✅ Es extremadamente fácil diferenciar entre múltiples opciones de ejecución.&lt;br&gt;
✅ Puede ser reutilizado en varias partes de nuestra aplicación (via exportación de módulo).&lt;br&gt;
✅ Es más rápido que &lt;code&gt;if/else&lt;/code&gt; y &lt;code&gt;switch/case&lt;/code&gt; dado que accedemos a la condición específica que queremos ejecutar, sin necesitar verificar cada uno de los casos secuencialmente, hasta encontrar el correcto.&lt;/p&gt;

&lt;p&gt;👎 Esta estrategia rara vez aparece en las formaciones más habituales.&lt;br&gt;
👎 Si el objeto no se define en el lugar indicado de la aplicación, puede consumir un poco más de memoria de la necesaria.&lt;/p&gt;
&lt;h2&gt;
  
  
  🤔 FAQ
&lt;/h2&gt;
&lt;h4&gt;
  
  
  ❓ ¿Qué sucede si proporcionamos una opción que no están entre las claves del objeto?
&lt;/h4&gt;

&lt;p&gt;La respuesta corta es que se disparará una excepción ya que no es posible ejecutar una función de &lt;code&gt;undefined&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;No obstante, podemos prevenir esto definiendo un caso &lt;code&gt;default&lt;/code&gt;, de la misma manera que hacemos en la instrucción &lt;code&gt;switch/case&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Para ser capaces de acceder a este nuevo caso, comprobaremos si la opción proporcionada existe dentro del objeto y si no existe, entonces ejecutaremos la opción &lt;code&gt;default&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Para estos casos, &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_Operator" rel="noopener noreferrer"&gt;operador condicional (ternario)&lt;/a&gt; será nuestro aliado.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h4&gt;
  
  
  ❓ ¿Qué puedo o debo devolver en el caso &lt;code&gt;default&lt;/code&gt;?
&lt;/h4&gt;

&lt;p&gt;Esto va a depender del caso de uso que estemos definiendo pero básicamente, vamos a tener tres opciones principales:&lt;/p&gt;

&lt;p&gt;1 - Devolver el mismo valor que hemos proporcionado:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;2 - Devolver &lt;code&gt;null&lt;/code&gt; o &lt;code&gt;undefined&lt;/code&gt;:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;En este caso, podemos incluso aprovechar el &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining" rel="noopener noreferrer"&gt;optional chaining&lt;/a&gt; y dejar más limpio nuestro código:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Debemos prestar atención porque in este caso, si no hay coincidencia en las opciones disponibles, vamos a devolver &lt;code&gt;undefined&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;3 - Definir una lógica de negocio específica:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Aquí debemos tener cuidado si nuestro código, como se muestra en el ejemplo, va a disparar un error. Tenemos que gestionar dicho error para evitar un error total que bloquee nuestra aplicación.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Obviamente el código que implementa el error puede ser reemplazado por cualquier otra lógica de negocio que se adecue mejor al comportamiento de nuestra aplicación.&lt;/p&gt;

&lt;h4&gt;
  
  
  ❓ ¿Necesito definir una función anónima para cada caso?
&lt;/h4&gt;

&lt;p&gt;No, en absoluto.&lt;/p&gt;

&lt;p&gt;Si tenemos perfectamente definida la función que debe ser ejecutada para cada caso y además, dicha función recibe únicamente un argumento que coincide con el que estamos proporcionando cuando invocamos al mapa, podemos usar esta sintaxis:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Incluso si queremos devolver &lt;code&gt;undefined&lt;/code&gt; cuando la opción proporcionada no está incluida dentro del mapa, podemos usar esta otra sintaxis extremadamente simplificada (&lt;strong&gt;Advertencia ‼️&lt;/strong&gt;: todas las funciones usadas para crear las claves del mapa, han de estar definidas previamente):&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h4&gt;
  
  
  ❓ ¿Es posible que el nombre de una propiedad entre en conflicto con el de un objeto?
&lt;/h4&gt;

&lt;p&gt;Rotundamente sí.&lt;/p&gt;

&lt;p&gt;Es totalmente posible, pero para evitar esto tenemos que prestar atención a los nombres que estamos usando, de la misma manera que nunca utilizaríamos una palabra reservada del lenguaje como nombre de variable, función u objeto.&lt;/p&gt;

&lt;h4&gt;
  
  
  ❓ ¿Esto podría formar una nueva convención de nombres?
&lt;/h4&gt;

&lt;p&gt;Sí, claro.&lt;/p&gt;

&lt;p&gt;Pero para este tipo de situaciones tenemos el apoyo y las guías proporcionadas por el Clean Code.&lt;/p&gt;

&lt;p&gt;Cada código que creemos requerirá una convención de nombres. En algunos casos cuando seamos la única persona que ha iniciado el proyecto, podremos definir dicha convención (pet-projects principalmente). En otras situaciones, será el equipo de desarrollo el responsable de cualquier acuerdo alcanzado a tal efecto.&lt;/p&gt;

&lt;h4&gt;
  
  
  ❓ ¿Requerirá un uso de memoria adicional mientras que el &lt;code&gt;if/else&lt;/code&gt; y el &lt;code&gt;switch/case&lt;/code&gt; no lo hacen?
&lt;/h4&gt;

&lt;p&gt;Sí, lo hará.&lt;/p&gt;

&lt;p&gt;Sin embargo, basándonos en los tipos de dispositivos que ejecutan nuestras aplicaciones JavaScript hoy en día así como en sus características, el incremento de memoria es prácticamente insignificante en comparación con el resto de la aplicación.&lt;/p&gt;

&lt;h4&gt;
  
  
  ❓ ¿Sería esta opción más lenta que el &lt;code&gt;if/else&lt;/code&gt; o el &lt;code&gt;switch/case&lt;/code&gt; dependiendo del motor de JavaScript que se use?
&lt;/h4&gt;

&lt;p&gt;Esto va a depender de cómo definamos el objeto en sí.&lt;/p&gt;

&lt;p&gt;Por ejemplo, si definimos el objeto de mapeado de funciones dentro de una función, la cual va a ser invocada cada vez que queramos hacer uso del mapa, obviamente esta solución va a ser más lenta que las otras opciones, porque el objeto debe ser creado cada vez.&lt;/p&gt;

&lt;p&gt;En este código podemos ver la situación donde la función &lt;code&gt;mappedFunction&lt;/code&gt; tiene definido el objeto dentro de ella:&lt;/p&gt;

&lt;p&gt;Codepen 👉 &lt;a href="https://codepen.io/ddialar/pen/JjRWNBJ" rel="noopener noreferrer"&gt;Speed race Switch 🐇 vs Object Lookup 🐢 v1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Aquí no importa qué motor de JavaScript estemos usando para ejecutar el código (&lt;a href="https://en.wikipedia.org/wiki/WebKit#JavaScriptCore" rel="noopener noreferrer"&gt;AppleWebKit&lt;/a&gt; para Safari, &lt;a href="https://en.wikipedia.org/wiki/SpiderMonkey" rel="noopener noreferrer"&gt;SpiderMonkey&lt;/a&gt; para Firefox o &lt;a href="https://en.wikipedia.org/wiki/V8_(JavaScript_engine)" rel="noopener noreferrer"&gt;V8&lt;/a&gt; para Google Chrome y/o NodeJS), porque el mapeado de funciones será siempre más lento (incluso si operamos los primeros casos), debido a que el objeto se está creando ad-hoc en cada ejecución de la función.&lt;/p&gt;

&lt;p&gt;Sin embargo, si definimos el mapeado de funciones de manera global (al módulo o a la aplicación), el objeto se cargará cuando el módulo o la aplicación lo usen. De este modo, el acceso a las funciones mapeadas será siempre más rápido que las otras dos opciones.&lt;/p&gt;

&lt;p&gt;En este código hemos definido el mapa fuera de la función  &lt;code&gt;mappedFunction&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;Codepen 👉 &lt;a href="https://codepen.io/ddialar/pen/ZEpeKqW" rel="noopener noreferrer"&gt;Speed race Switch 🐢 vs Object Lookup 🐇 v2&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  ❓ ¿Qué pasa con el recolector de basura?
&lt;/h4&gt;

&lt;p&gt;Hoy en día el recolector de basura es algo a lo que quienes desarrollamos con JavaScript no le prestamos mucha atención, debido a que está ampliamente cubierto por las especificaciones del lenguaje así que, una vez el mapa de funciones ya no está en uso en el proceso de ejecución actual, el objeto será gestionado por el recolector de basura automáticamente.&lt;/p&gt;

&lt;p&gt;Para más información respecto a este tema, recomiendo echar un vistazo a esta documentación de la MDN relativa a la &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Memory_Management" rel="noopener noreferrer"&gt;gestión de la memoria&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Recursos adicionales:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://javascript.info/garbage-collection" rel="noopener noreferrer"&gt;Garbage collection por javascript.info (2020)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  👋 Conclusiones finales
&lt;/h2&gt;

&lt;p&gt;Como ya he dicho al principio de este post, no es mi intención criticar de ningún modo el uso de &lt;code&gt;if/else&lt;/code&gt; o &lt;code&gt;switch/case&lt;/code&gt;, sino que únicamente pretendo proporcionar otra manera de realizar dichas operaciones.&lt;/p&gt;

&lt;p&gt;Resumiendo, cuando tengamos que discriminar entre dos simples opciones, es obvio que la alternativa más sencilla es usar &lt;code&gt;if/else&lt;/code&gt;. Además recomiendo encarecidamente que intentes usar el operador ternario allí donde sea posible.&lt;/p&gt;

&lt;p&gt;Para aquellos casos donde tengamos que diferenciar entre tres o más opciones, sinceramente recomiendo el uso de funciones mapeadas para proporcionar una mejor legibilidad, mantenimiento y reutilización de nuestro código.&lt;/p&gt;

&lt;p&gt;Espero que este contenido te sea útil. Si tienes cualquier pregunta, siéntete totalmente libre de contactar conmigo. Aquí están mis perfiles de &lt;a href="https://twitter.com/ddialar" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;, &lt;a href="https://linkedin.com/io/ddialar" rel="noopener noreferrer"&gt;Linkedin&lt;/a&gt; y &lt;a href="https://github.com/ddialar" rel="noopener noreferrer"&gt;Github&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  🙏 Reconocimientos y agradecimientos
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;a href="https://twitter.com/LissetteIbnz" rel="noopener noreferrer"&gt;Lissette Luis&lt;/a&gt;, &lt;a href="https://twitter.com/AdrianFerrera91" rel="noopener noreferrer"&gt;Adrián Ferrera&lt;/a&gt; e &lt;a href="https://twitter.com/ivanbtrujillo" rel="noopener noreferrer"&gt;Iván Bacallado&lt;/a&gt; por formar parte de un equipo fantástico donde se comparte el conocimiento y especialmente, por sus propuestas sobre el tema abordado en este texto.&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://twitter.com/SimonHoiberg" rel="noopener noreferrer"&gt;Simon Høiberg&lt;/a&gt; por iniciar este interesantísimo &lt;a href="https://twitter.com/SimonHoiberg/status/1337688868527726593?s=20" rel="noopener noreferrer"&gt;hilo de Twitter&lt;/a&gt; que ha originado la creación de este post.&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://twitter.com/knnyldz90" rel="noopener noreferrer"&gt;Kenan Yildiz&lt;/a&gt; y &lt;a href="https://twitter.com/tluzat" rel="noopener noreferrer"&gt;Thomas Luzat&lt;/a&gt; por compartir una opción más simplificada de la implementación del mapa de funciones.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>node</category>
      <category>webdev</category>
    </item>
    <item>
      <title>From if/switch to mapped functions (a.k.a. object lookup)</title>
      <dc:creator>Dailos Rafael Díaz Lara</dc:creator>
      <pubDate>Mon, 14 Dec 2020 13:53:43 +0000</pubDate>
      <link>https://dev.to/ddialar/from-if-switch-to-mapped-functions-a-k-a-object-lookup-i3d</link>
      <guid>https://dev.to/ddialar/from-if-switch-to-mapped-functions-a-k-a-object-lookup-i3d</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/ddialar/de-if-switch-a-mapped-functions-a-k-a-object-lookup-1pb8"&gt;🇪🇸 Versión en español&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🎯 Context
&lt;/h2&gt;

&lt;p&gt;This text is aimed to provide an alternative to these situations where our code must be structured in order to run one or another function, depending on a bunch of possible conditions.&lt;/p&gt;

&lt;p&gt;I don't want to criticize the use of &lt;code&gt;if/else&lt;/code&gt; or &lt;code&gt;switch/case&lt;/code&gt; sentences far from it. My only goal is to provide a different approach which can improve the maintenance and scalability of our code.&lt;/p&gt;

&lt;p&gt;So... engage!!!&lt;/p&gt;

&lt;h2&gt;
  
  
  📚 if/else sentences
&lt;/h2&gt;

&lt;p&gt;Since all of us start learning to code, the first flow control sentence that we learn is the &lt;code&gt;if/else&lt;/code&gt; one (&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/if...else" rel="noopener noreferrer"&gt;MDN if/else documentation&lt;/a&gt;). So once we have got it, it's really easy to use.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Even when the amount of possible options increases, we can chain multiple &lt;code&gt;if/else&lt;/code&gt;.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;In addition, when we have several options which must be treated in the same way (it means with the same business logic), we can use boolean operators (the &lt;code&gt;OR&lt;/code&gt; one in this case), in order to group all these options under the same block.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;That's fine but when the possible cases are upper than two or three options, the code starts to look like a little bit messy.&lt;/p&gt;

&lt;h4&gt;
  
  
  Pros (✅) and cons (👎)
&lt;/h4&gt;

&lt;p&gt;✅ It's the easier way to control data flow.&lt;br&gt;
✅ It's relatively easy to learn.&lt;br&gt;
✅ It's really comfortable to discriminate between two options.&lt;/p&gt;

&lt;p&gt;👎 For handling more than three options, the code starts to look like a little bit messy.&lt;br&gt;
👎 To chain multiple options reduce the readability and maintenance of our code.&lt;br&gt;
👎 To group options using boolean operators could turn on complex assertion rules for every situation.&lt;br&gt;
👎 For a relatively huge amount of cases, it's slow due to every condition must be checked until getting the only one that matches.&lt;/p&gt;
&lt;h2&gt;
  
  
  🤓 switch/case sentence
&lt;/h2&gt;

&lt;p&gt;When we want to improve the readability and maintenance of our code due to we have multiple options to be handled, is when we learn the &lt;code&gt;if/else&lt;/code&gt; alternative, it means, the &lt;code&gt;switch/case&lt;/code&gt; sentence (&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/switch" rel="noopener noreferrer"&gt;MDN switch/case documentation&lt;/a&gt;).&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;In the same way we did with the &lt;code&gt;if/else&lt;/code&gt; sentence, with &lt;code&gt;switch/case&lt;/code&gt; we can also group options but now, we don't need to use any boolean operator. We just need to keep joined the different cases.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;As you already know, that's possible due to the &lt;code&gt;switch/case&lt;/code&gt; execution is a sequential process, where every possible case defined in the block of options, is compared with the provided value.&lt;/p&gt;

&lt;p&gt;If both values match, the code block included into that case is executed and, if there is not a &lt;code&gt;break&lt;/code&gt; or &lt;code&gt;return&lt;/code&gt; instruction at the end of the code block, the next case will be checked until the next matching or the &lt;code&gt;default&lt;/code&gt; option is reached.&lt;/p&gt;

&lt;p&gt;Based on that, in order to group several options which are going to be handled by the same code block, we just need to define the &lt;code&gt;case&lt;/code&gt; for the wished value, with no business logic. This way we're able to chain multiple options for the same result.&lt;/p&gt;

&lt;h4&gt;
  
  
  Pros (✅) and cons (👎)
&lt;/h4&gt;

&lt;p&gt;✅ It provides a better code structuring than &lt;code&gt;if/else&lt;/code&gt; sentences.&lt;br&gt;
✅ It's possible to create clearest cases grouping than &lt;code&gt;if/else&lt;/code&gt; sentences.&lt;br&gt;
✅ It's really easy to discriminate between more than two options.&lt;/p&gt;

&lt;p&gt;👎 You have to be aware about completing all your code blocks with a &lt;code&gt;break&lt;/code&gt; or a &lt;code&gt;return&lt;/code&gt; instruction. If you forget it, you'll have a real nightmare.&lt;br&gt;
👎 For a relatively huge amount of cases, it's slow due to every condition must be checked until getting the only one that matches.&lt;/p&gt;
&lt;h2&gt;
  
  
  🔥 Mapped functions
&lt;/h2&gt;

&lt;p&gt;This is a little known strategy (a.k.a. &lt;code&gt;object lookup&lt;/code&gt;) aimed to improve several aspects of &lt;code&gt;if/else&lt;/code&gt; and &lt;code&gt;switch/case&lt;/code&gt; sentences.&lt;/p&gt;

&lt;p&gt;The idea is to take advantage of a JavaScript object behavior in order to use its keys as map references to access to specific business logic.&lt;/p&gt;

&lt;p&gt;First at all, we need to have defined the possible cases which must be handled.&lt;/p&gt;

&lt;p&gt;Every single case will be bound to a key in the literal object.&lt;/p&gt;

&lt;p&gt;Once we have created our object, we'll use array-access style to run the code for every single case.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h4&gt;
  
  
  Pros (✅) and cons (👎)
&lt;/h4&gt;

&lt;p&gt;✅ It provides a better code structuring than &lt;code&gt;if/else&lt;/code&gt; and &lt;code&gt;switch/case&lt;/code&gt; sentences.&lt;br&gt;
✅ There is no cases grouping due to every single case has its own business logic.&lt;br&gt;
✅ It's extremely easy to differentiate between multiple options.&lt;br&gt;
✅ Can be reused in several parts of our application (via module export).&lt;br&gt;
✅ It's faster than &lt;code&gt;if/else&lt;/code&gt; and &lt;code&gt;switch/case&lt;/code&gt; due to we access to the specific condition without need to check every defined case sequentially until the correct one is located.&lt;/p&gt;

&lt;p&gt;👎 This strategy rarely appears in common trainings.&lt;br&gt;
👎 If the object is not defined in the right place, it can consume a little bit more memory that it really needs.&lt;/p&gt;
&lt;h2&gt;
  
  
  🤔 FAQ
&lt;/h2&gt;
&lt;h4&gt;
  
  
  ❓ What happen if the provided option is not defined as object key?
&lt;/h4&gt;

&lt;p&gt;The short answer is that an exception will be throw due to it's not possible to run a function from &lt;code&gt;undefined&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;However, we can prevent it defining a &lt;code&gt;default&lt;/code&gt; case, in the same way we do in &lt;code&gt;switch/case&lt;/code&gt; sentences.&lt;/p&gt;

&lt;p&gt;In order to be able to access this new case, we will check if the provided one already exists into the object and if it doesn't, we run the &lt;code&gt;default&lt;/code&gt; option.&lt;/p&gt;

&lt;p&gt;For these cases, the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_Operator" rel="noopener noreferrer"&gt;conditional (ternary) operator&lt;/a&gt; will be our allied.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h4&gt;
  
  
  ❓ What can/must I return in the &lt;code&gt;default&lt;/code&gt; case?
&lt;/h4&gt;

&lt;p&gt;It'll depend on the use case we are defining but basically, we have three main options:&lt;/p&gt;

&lt;p&gt;1 - To return the same value that you have provided:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;2 - To return &lt;code&gt;null&lt;/code&gt; or &lt;code&gt;undefined&lt;/code&gt;:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;In this case, we can even take advantage of the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining" rel="noopener noreferrer"&gt;optional chaining&lt;/a&gt; and clean up the code this way:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;We have to pay attention because in this last case, if there are no matching options, we are going to be returning &lt;code&gt;undefined&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;3 - To define a specific business logic:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;We must be careful if our code, like in this example, is going to throw an error. We need to handle it in order to avoid a full blocking error.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Obviously the code that implements the error can be replaced for any other business logic which suites better with our application behavior.&lt;/p&gt;

&lt;h4&gt;
  
  
  ❓ Do I need to define an anonymous function for every case?
&lt;/h4&gt;

&lt;p&gt;No, you don't.&lt;/p&gt;

&lt;p&gt;If we have perfectly defined the function that must be run for every case and in addition, that function receives only a single argument which matches with the provided one when you invoke the map, we can use this syntax:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Even if we want to return &lt;code&gt;undefined&lt;/code&gt; when the provided option is not included into the map, we can use this extremely simplified syntax (&lt;strong&gt;Caveat ‼️&lt;/strong&gt;: the whole functions used in order to create the mapped object keys must be defined previously):&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h4&gt;
  
  
  ❓ It could be possible that property name clashes with an object one?
&lt;/h4&gt;

&lt;p&gt;Absolutely yes.&lt;/p&gt;

&lt;p&gt;It's possible at all, but in order to avoid that we have to pay attention what names are we using, in the same way we never use a language reserved word as variable, function or object name.&lt;/p&gt;

&lt;h4&gt;
  
  
  ❓ It could force a naming convention?
&lt;/h4&gt;

&lt;p&gt;Yes, it could.&lt;/p&gt;

&lt;p&gt;But for these situation we have the support and guidances of Clean Code.&lt;/p&gt;

&lt;p&gt;Every code we create requires naming convention. Some cases when we are the only person who has started the project, we can define that convention (pet-projects mainly). In other situations, the development team will be the responsible of any agreement about that.&lt;/p&gt;

&lt;h4&gt;
  
  
  ❓ Is it going to require additional memory meanwhile &lt;code&gt;if/else&lt;/code&gt; and &lt;code&gt;switch/case&lt;/code&gt; does not do it?
&lt;/h4&gt;

&lt;p&gt;Yes, it will.&lt;/p&gt;

&lt;p&gt;However, based on the kind of devices that run our JavaScript applications nowadays and its characteristics, the increment of used memory is insignificant compared with the rest of the application.&lt;/p&gt;

&lt;h4&gt;
  
  
  ❓ Should it be slower that &lt;code&gt;if/else&lt;/code&gt; or &lt;code&gt;switch/case&lt;/code&gt; depending on the JavaScript engine?
&lt;/h4&gt;

&lt;p&gt;It depends on where we define the object.&lt;/p&gt;

&lt;p&gt;For instance, if we define the mapped functions object into a function which is going to be called every time we want to use the map, obviously this solution is going to be slower that the other options because the object must be created every time.&lt;/p&gt;

&lt;p&gt;In this code we can see that situation where the &lt;code&gt;mappedFunction&lt;/code&gt; has defined the object lookup inside:&lt;/p&gt;

&lt;p&gt;Codepen 👉 &lt;a href="https://codepen.io/ddialar/pen/JjRWNBJ" rel="noopener noreferrer"&gt;Speed race Switch 🐇 vs Object Lookup 🐢 v1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It doesn't matter which JavaScript engine you use to run this code (&lt;a href="https://en.wikipedia.org/wiki/WebKit#JavaScriptCore" rel="noopener noreferrer"&gt;AppleWebKit&lt;/a&gt; for Safari, &lt;a href="https://en.wikipedia.org/wiki/SpiderMonkey" rel="noopener noreferrer"&gt;SpiderMonkey&lt;/a&gt; for Firefox or &lt;a href="https://en.wikipedia.org/wiki/V8_(JavaScript_engine)" rel="noopener noreferrer"&gt;V8&lt;/a&gt; for Google Chrome and/or NodeJS), because the mapped function will be always slower (even with the first cases) due to the object is created ad-hoc for every function run.&lt;/p&gt;

&lt;p&gt;Nevertheless, if we define the mapped functions globally (to the module or to the application), the object will be loaded just once when the module or the application is used so, this way, the mapped function access is always faster than the other two options.&lt;/p&gt;

&lt;p&gt;In this another code we have defined the object lookup outside the &lt;code&gt;mappedFunction&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;Codepen 👉 &lt;a href="https://codepen.io/ddialar/pen/ZEpeKqW" rel="noopener noreferrer"&gt;Speed race Switch 🐢 vs Object Lookup 🐇 v2&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  ❓ What about the garbage collector?
&lt;/h4&gt;

&lt;p&gt;Nowadays the garbage collector is something that the JavaScript developers don't pay too much attention due to it's widely covered by the language specifications so, once the mapped functions object is not used any more by the current runtime, it will be managed by the garbage collector automatically.&lt;/p&gt;

&lt;p&gt;For further information about that, I recommend you to take a look to this documentation of the MDN about &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Memory_Management" rel="noopener noreferrer"&gt;memory management&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Additional resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://javascript.info/garbage-collection" rel="noopener noreferrer"&gt;Garbage collection by javascript.info (2020)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  👋 Final words
&lt;/h2&gt;

&lt;p&gt;As I said at the beginning of this post, it's not my intention to criticize the use of &lt;code&gt;if/else&lt;/code&gt; or &lt;code&gt;switch/case&lt;/code&gt;, but I only want to provide another way to do these operations.&lt;/p&gt;

&lt;p&gt;Summarizing, when we've to discriminate between two single options, it's obvious that the easier alternative is to use &lt;code&gt;if/else&lt;/code&gt; sentence. Moreover I strongly recommend you to try to use the ternary operator when it's possible.&lt;/p&gt;

&lt;p&gt;For cases where you have to differentiate between three or more options, I sincerely recommend you to use mapped functions in order to provide a better readability, maintenance and code reuse.&lt;/p&gt;

&lt;p&gt;I hope this tip is useful for you. If you have any question, feel free to contact me. Here there are my &lt;a href="https://twitter.com/ddialar" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;, &lt;a href="https://linkedin.com/io/ddialar" rel="noopener noreferrer"&gt;Linkedin&lt;/a&gt; and &lt;a href="https://github.com/ddialar" rel="noopener noreferrer"&gt;Github&lt;/a&gt; profiles.&lt;/p&gt;

&lt;h2&gt;
  
  
  🙏 Credits and thanks
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://twitter.com/LissetteIbnz" rel="noopener noreferrer"&gt;Lissette Luis&lt;/a&gt;, &lt;a href="https://twitter.com/AdrianFerrera91" rel="noopener noreferrer"&gt;Adrián Ferrera&lt;/a&gt; and &lt;a href="https://twitter.com/ivanbtrujillo" rel="noopener noreferrer"&gt;Iván Bacallado&lt;/a&gt; for being part of an awesome knowledge sharing team and specially, for their proposals about the subject covered on this post.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://twitter.com/SimonHoiberg" rel="noopener noreferrer"&gt;Simon Høiberg&lt;/a&gt; for starting this interesting &lt;a href="https://twitter.com/SimonHoiberg/status/1337688868527726593?s=20" rel="noopener noreferrer"&gt;Twitter thread&lt;/a&gt; that originated the creation of this post.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://twitter.com/knnyldz90" rel="noopener noreferrer"&gt;Kenan Yildiz&lt;/a&gt; and &lt;a href="https://twitter.com/tluzat" rel="noopener noreferrer"&gt;Thomas Luzat&lt;/a&gt; for sharing a shorter way to implement the object lookup.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>node</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Jest with global Dotenv configuration</title>
      <dc:creator>Dailos Rafael Díaz Lara</dc:creator>
      <pubDate>Sat, 01 Aug 2020 18:44:34 +0000</pubDate>
      <link>https://dev.to/ddialar/jest-with-global-dotenv-configuration-cgo</link>
      <guid>https://dev.to/ddialar/jest-with-global-dotenv-configuration-cgo</guid>
      <description>&lt;h2&gt;
  
  
  🎯 Target
&lt;/h2&gt;

&lt;p&gt;I want to have several &lt;code&gt;.env&lt;/code&gt; files in order to define different conditions depending on the environment where my code is running. Based on that, in my testing process I want to load a global configuration once for the whole testing suites.&lt;/p&gt;

&lt;h2&gt;
  
  
  😅 Overview
&lt;/h2&gt;

&lt;p&gt;There are some options to load environment variables in my tests, using Dotenv.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;node -r dotenv/config node_modules/.bin/jest&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;That is one of the most usual approaches but I don't like it because this way, Dotenv literally needs a &lt;code&gt;.env&lt;/code&gt; file defined at the root of the project so I cannot set a custom configuration file.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;require(dotenv).config({ path: 'path/to/env_file' });&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This option requires to type this command in every testing file where I'm going to use environment variables. It could be a solution but I don't like it at all because I have to pay attention to the relative path for the environment file location in every testing file.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://jestjs.io/docs/en/configuration#setupfiles-array" rel="noopener noreferrer"&gt;&lt;code&gt;setupFiles&lt;/code&gt;&lt;/a&gt; Jest configuration property.&lt;/p&gt;

&lt;p&gt;That is fine but as the official documentation suggests, the environment is processed once per testing file. This conditions will dealy a little my tests execution and in addition, I don't want it. I want a global configuration at the very begining of the testing process.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  😎 My best matching
&lt;/h2&gt;

&lt;p&gt;After diving a little in the Jest documentation, I found this configuration property: &lt;a href="https://jestjs.io/docs/en/configuration#globalsetup-string" rel="noopener noreferrer"&gt;&lt;code&gt;globalSetup&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In order to use this property, I created the &lt;a href="https://github.com/ddialar/testing.jest.global.dotenv/dotenv/dotenv-test.js" rel="noopener noreferrer"&gt;&lt;code&gt;dotenv-test.js&lt;/code&gt;&lt;/a&gt; file into &lt;code&gt;dotenv&lt;/code&gt; folder (located at the root of the project).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// dotenv-test.js file content.&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dotenv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dotenv&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;dotenv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../env/.env.test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Following the property documentation guidelines, this file must export an asynchronous function that will be executed before the whole testing files.&lt;/p&gt;

&lt;p&gt;When the testing framework runs at the first time, this file is executed, loading the selected environment variables and setting them globally, for the whole testing files and just once. That's all.&lt;/p&gt;

&lt;p&gt;Finally, I have just to introduce this property into the Jest configuration file (&lt;a href="https://github.com/ddialar/testing.jest.global.dotenv/jest.config.json" rel="noopener noreferrer"&gt;&lt;code&gt;jest.config.json&lt;/code&gt;&lt;/a&gt;) and provide the location of the Dotenv file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"globalSetup"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;rootDir&amp;gt;/dotenv/dotenv-test.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  👋 Final words
&lt;/h2&gt;

&lt;p&gt;I hope this tip is useful for you. If you have any question, feel free to contact me. Here there are my &lt;a href="https://twitter.com/ddialar" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;, &lt;a href="https://linkedin.com/io/ddialar" rel="noopener noreferrer"&gt;Linkedin&lt;/a&gt; and &lt;a href="https://github.com/ddialar" rel="noopener noreferrer"&gt;Github&lt;/a&gt; profiles.&lt;/p&gt;

&lt;h2&gt;
  
  
  💾 Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  Example code repository &lt;a href="https://github.com/ddialar/testing.jest.global.dotenv" rel="noopener noreferrer"&gt;https://github.com/ddialar/testing.jest.global.dotenv&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  Jest configuration docs &lt;a href="https://jestjs.io/docs/en/configuration.html" rel="noopener noreferrer"&gt;https://jestjs.io/docs/en/configuration.html&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>tdd</category>
      <category>typescript</category>
      <category>webpack</category>
    </item>
  </channel>
</rss>
