CommunityProgramación y desarrollogithub.com

Aliyansayz/agent-skills

Mejora la calidad del código TypeScript reemplazando nombres poco claros, tipos inseguros, lógica duplicada, responsabilidades mixtas, valores mágicos y manejo de errores frágil con código legible, mantenible y fuertemente tipado. Úsalo al revisar, refactorizar, depurar o extender aplicaciones TypeScript existentes, especialmente sistemas CRM de ventas.

¿Qué es agent-skills?

Esta habilidad te permite actuar como un agente de calidad de código TypeScript, mejorando el código existente para que sea más fácil de entender, mantener, probar y extender. Se enfoca en reemplazar nombres de variables vagos y funciones con nombres poco descriptivos por nombres específicos del dominio, utilizando conceptos de negocio claros como 'Lead', 'Contact', 'Opportunity' o 'SalesRepresentative', especialmente en sistemas CRM de ventas. La habilidad promueve el uso de tipos explícitos, interfaces y uniones discriminadas en lugar de `any`, y fomenta la separación de responsabilidades para evitar funciones que mezclan validación, lógica de negocio, acceso a datos y notificaciones. Además, proporciona un flujo de trabajo de refactorización paso a paso que incluye entender el contexto, registrar el comportamiento actual, mejorar nombres, hacer cumplir tipado seguro, separar responsabilidades, simplificar la lógica, manejar errores de forma robusta, añadir pruebas y proceder con cambios incrementales, preservando siempre el comportamiento previsto. Es compatible con cualquier editor o plataforma que soporte habilidades de agente AI como Claude Code, Cursor o Codex, y se basa en su documentación SKILL.md que incluye ejemplos detallados de código mejorable y refactorizado.

Compatible con~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Aliyansayz/agent-skills/tree/HEAD/typescript-code-quality

Installed? Explore more Programación y desarrollo skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

Calidad de Código Contextual — Habilidad de Agente TypeScript

1. Propósito

Actúa como un agente de calidad de código TypeScript que hace que el código existente sea más fácil de entender, mantener, probar y extender.

No refactorices el código solo para que parezca sofisticado. Mejóralo para que los desarrolladores puedan entender:

  • Qué hace cada función.
  • Por qué existe una regla de negocio.
  • Qué representa cada variable.
  • Qué datos espera y devuelve una función.
  • Qué sucede cuando faltan datos o son inválidos.
  • Qué partes de la aplicación son responsables de la lógica de negocio, el acceso a datos y la interacción del usuario.
  • Cómo afecta un cambio al comportamiento existente de la aplicación.

Usa lenguaje específico del dominio en lugar de nombres de variables y funciones vagos.

Al trabajar con código CRM de ventas, usa conceptos de negocio claros como Lead, Contact, Opportunity, SalesRepresentative, LeadStatus, PipelineStage y FollowUpDate.

El objetivo es un software comprensible, fuertemente tipado, confiable, testeable y mantenible.

2. Cuándo Activar

Usa esta habilidad cuando:

  • El código TypeScript contiene variables como x, data, obj, temp, thing o result sin suficiente contexto.
  • Las funciones tienen nombres vagos como process(), handle(), manage() o doStuff().
  • Una función realiza validación, cálculos de negocio, actualizaciones de base de datos, notificaciones y registro todo a la vez.
  • La aplicación depende de any, aserciones de tipo inseguras, cadenas inexplicadas o números mágicos.
  • Las reglas de negocio están duplicadas en varios lugares.
  • Los errores se tragan silenciosamente o se convierten en respuestas de éxito engañosas.
  • Las funciones contienen condiciones profundamente anidadas o ramificación excesiva.
  • La terminología CRM es inconsistente entre componentes, endpoints de API y modelos de base de datos.
  • El código es difícil de probar sin conectarse a una base de datos real o servicio externo.
  • Un desarrollador quiere mejorar el código existente sin cambiar su comportamiento previsto.

3. Principios Fundamentales

3.1 Prefiere nombres específicos del dominio

Los nombres deben explicar su significado en la aplicación.

Prefiere:

const leadId = "lead_123";
const salesRepresentativeId = "user_456";
const nextFollowUpDate = new Date();
const qualifiedLeadCount = 12;

Evita:

const id = "lead_123";
const user = "user_456";
const date = new Date();
const count = 12;

Los nombres cortos son aceptables cuando su significado es obvio dentro de un ámbito pequeño, como i en un bucle corto. No expandas cada nombre mecánicamente; expande los nombres cuando el contexto adicional mejore la comprensión.

3.2 Usa TypeScript para representar conceptos de negocio

Prefiere tipos explícitos, interfaces, uniones discriminadas y modelos de dominio nombrados sobre objetos no estructurados.

Evita usar any como una escapatoria. Usa unknown para datos genuinamente no confiables y valídalos antes de usarlos.

3.3 Separa responsabilidades

Cada función o módulo debe tener una responsabilidad principal clara.

Un servicio CRM no debería tener que construir respuestas HTTP, manipular componentes de UI, enviar correos electrónicos y ejecutar consultas de base de datos en una sola función.

3.4 Preserva el comportamiento previsto

Antes de refactorizar, identifica las reglas de negocio existentes y los resultados esperados.

No cambies silenciosamente:

  • Los criterios de calificación de leads.
  • Las reglas de transición del pipeline.
  • Las asignaciones de representantes de ventas.
  • Las reglas de programación de seguimiento.
  • El comportamiento de detección de duplicados.
  • Los permisos y controles de acceso.
  • Las formas de las respuestas de API.
  • La semántica de la base de datos.

Si el comportamiento existente parece incorrecto o ambiguo, identifica la preocupación explícitamente en lugar de disfrazar un cambio de comportamiento como una refactorización.

3.5 Prefiere código simple sobre abstracciones innecesarias

No introduzcas patrones de diseño, clases, genéricos, fábricas o dependencias adicionales a menos que resuelvan un problema real.

Una función directa con un nombre descriptivo suele ser mejor que una abstracción innecesariamente elaborada.


4. Ejemplo de CRM de Ventas Mal Escrito

El siguiente código es intencionalmente problemático. Ilustra problemas comunes de calidad de código en una aplicación de gestión de leads.

4.1 Antes: Código mal escrito

// bad-crm.ts

export async function go(d: any, db: any, mail: any) {
    try {
        let x = d.name;
        let e = d.email;
        let p = d.phone || "";
        let s = d.status || "new";
        let v = Number(d.value || 0);
        let u = d.user || null;

        if (!x || !e) {
            return { ok: false, msg: "bad data" };
        }

        let old = await db.leads.findOne({ email: e });

        if (old && old.status != "lost") {
            old.v = old.v + v;
            old.s = v > 5000 ? "hot" : old.s;
            old.p = p || old.p;
            await db.leads.update(old.id, old);

            if (old.s == "hot") {
                await mail.send(
                    old.email,
                    "Hot lead",
                    "Lead is hot!"
                );
            }

            return { ok: true, data: old };
        }

        let a = {
            name: x,
            email: e,
            phone: p,
            status: s,
            value: v,
            user: u,
            created: new Date(),
            updated: new Date(),
        };

        let r = await db.leads.insert(a);

        if (v > 5000) {
            await mail.send(
                e,
                "Hot lead",
                "Lead is hot!"
            );
        }

        return { ok: true, data: r };
    } catch (e) {
        console.log(e);
        return { ok: false, msg: "error" };
    }
}

4.2 Problemas que el agente debe identificar

ProblemaEjemploPor qué es importanteMejora
Nombre de función poco clarogo()No comunica la operación de negocioUsa createOrUpdateLead() si describe con precisión el comportamiento
Variables poco clarasd, x, e, p, s, v, u, a, rLos desarrolladores deben inferir el propósito de cada variableUsa nombres específicos del dominio
Tipos insegurosany para entrada, base de datos y servicio de correoElimina la capacidad de TypeScript para detectar muchos erroresDefine interfaces y tipos de entrada explícitos
Regla de negocio ocultav > 5000El umbral de calificación no está explicadoDefine una constante nombrada o una regla de negocio documentada
Terminología inconsistentestatus, hot, lostEl estado del lead y la calificación pueden representar conceptos diferentesDefine un modelo de estado de lead explícito
Responsabilidades mixtasUna función valida, consulta, actualiza, crea, envía correos y maneja erroresLos comportamientos individuales son difíciles de probarSepara las responsabilidades de aplicación, dominio e infraestructura
Posible inconsistencia de datosUna actualización de base de datos puede tener éxito antes de que falle un correoEl llamador puede recibir un error aunque el lead se haya guardadoDefine explícitamente el comportamiento de fallo de notificación
Validación débilSolo verifica x y eDirecciones de correo inválidas, valores incorrectos y campos malformados pueden ingresar al sistemaValida la entrada y los invariantes de negocio
Reglas de estado implícitasLos leads existentes se actualizan a menos que su estado sea "lost"El significado de esta regla está ocultoNombra y documenta la política de actualización
Contrato de persistencia poco clarodb.leads.findOne() y .update() usan formas no documentadasLas suposiciones de la base de datos son difíciles de verificarDefine una interfaz de repositorio
Respuesta de error genérica"error"Pierde contexto útil y complica la resolución de problemasManeja explícitamente los fallos esperados y registra los fallos inesperados
Respuestas inconsistentes{ ok, msg } y { ok, data }Los llamadores deben inferir qué campos existen en cada casoUsa un tipo de resultado discriminado o un modelo de error de aplicación consistente
Datos mutables y poco estructuradosold.v, old.s, old.pLos nombres de campo no coinciden con el modelo persistido aparenteDefine un tipo Lead consistente
Política de duplicados poco claraLos registros existentes se fusionan por correo electrónicoEl significado exacto de la fusión está ocultoDocumenta qué campos se pueden actualizar y cómo se agrega el valor
Dependencias difíciles de probarLa base de datos y el servicio de correo se pasan como anyLos contratos y casos de fallo no son verificados por el compiladorDefine interfaces estrechas e inyecta dependencias

Importante: Este es un ejemplo ilustrativo, no una implementación completa de CRM de producción. Una refactorización debe primero confirmar el comportamiento real del CRM existente, el modelo de datos, la semántica de persistencia y las reglas de negocio.


5. Flujo de Trabajo de Refactorización

Sigue este flujo de trabajo al mejorar código TypeScript existente.

Paso 1: Entiende el contexto

Inspecciona el código relevante antes de modificarlo.

Identifica:

  • El propósito de la función.
  • Sus llamadores y dependencias.
  • Sus formatos de entrada y salida.
  • El esquema de la base de datos o el contrato del repositorio.
  • Las entidades del dominio y la terminología de negocio.
  • Las reglas de validación y autorización existentes.
  • Las pruebas actuales y el comportamiento esperado.
  • El manejo de errores y los efectos secundarios.

Lee los módulos circundantes cuando sea necesario. No infieras el contrato completo de una función solo por su nombre.

Paso 2: Registra el comportamiento actual

Describe la lógica existente en lenguaje sencillo.

Para el ejemplo anterior, el comportamiento observado es:

  1. Leer los detalles del lead desde la entrada.
  2. Rechazar una solicitud si falta el nombre o el correo electrónico.
  3. Buscar un lead existente por correo electrónico.
  4. Si existe un lead coincidente y su estado no es "lost", agregar el valor enviado al valor existente.
  5. Marcar ese lead existente como "hot" cuando el valor resultante supera el umbral.
  6. Reemplazar su número de teléfono cuando se proporciona un número de teléfono nuevo no vacío.
  7. Actualizar el registro y enviar una notificación si el estado resultante es "hot".
  8. De lo contrario, crear un nuevo registro con marcas de tiempo.
  9. Notificar al lead cuando su valor enviado supera el umbral.
  10. Devolver un objeto de éxito o fracaso.

Esta descripción distingue intencionalmente el comportamiento de implementación observado del comportamiento de negocio previsto. Por ejemplo, el código prueba el valor resultante del registro existente para una ruta de notificación, pero el valor enviado para otra. No trates estas reglas como equivalentes silenciosamente.

Paso 3: Mejora los nombres

Reemplaza los nombres genéricos con nombres que expresen su función.

Nombre pobreNombre mejor
gocreateOrUpdateLead
dleadInput
xleadName
eemailAddress, o error en un bloque catch
pphoneNumber
sleadStatus
vleadValue
usalesRepresentativeId
oldexistingLead
anewLead
rcreatedLead
dbleadRepository o databaseClient, dependiendo de su función real
mailemailNotificationService

El nombre mejorado debe representar con precisión el valor. No renombres un valor a salesRepresentative si es solo un identificador; usa salesRepresentativeId.

Paso 4: Haz cumplir el tipado seguro

Define tipos para cada concepto de dominio.

type LeadStatus = "new" | "contacted" | "qualified" | "proposal" | "negotiation" | "won" | "lost";

interface LeadInput {
    name: string;
    email: string;
    phone?: string;
    status?: LeadStatus;
    value: number;
    salesRepresentativeId?: string;
}

interface Lead extends LeadInput {
    id: string;
    createdAt: Date;
    updatedAt: Date;
}

Valida los datos de entrada contra estos tipos en el límite del sistema.

Paso 5: Separa las responsabilidades

Divide la función monolítica en partes más pequeñas con un solo propósito.

  • Capa de aplicación: Coordina las operaciones. Recibe la entrada, llama a las capas de dominio e infraestructura, y da forma a la respuesta.

  • Capa de dominio: Contiene las reglas de negocio como la calificación de leads y la lógica de actualización.

  • Capa de infraestructura: Gestiona la persistencia, el envío de correos electrónicos y otras interacciones externas.

Paso 6: Simplifica la lógica

Reemplaza condiciones anidadas con cláusulas de guarda y retornos tempranos.

// Prefiere esto:
if (!leadInput.name || !leadInput.email) {
    return { ok: false, msg: "bad data" };
}

// Más lógica sin anidamiento adicional...

Paso 7: Maneja los errores de forma robusta

Define resultados explícitos para cada camino de fallo esperado.

type LeadResult =
    | { ok: true; lead: Lead }
    | { ok: false; error: "validation_error"; details: string }
    | { ok: false; error: "not_found" }
    | { ok: false; error: "notification_failed"; leadId: string };

Paso 8: Añade pruebas

Escribe pruebas que verifiquen el comportamiento observado antes de hacer cualquier cambio. Luego refactoriza y asegúrate de que las pruebas sigan pasando.

Paso 9: Cambios incrementales

Refactoriza en pequeños pasos. Después de cada cambio, ejecuta las pruebas. No mezcles cambios de comportamiento con refactorizaciones de estilo.


6. Refactorización de Referencia

Aquí hay una versión refactorizada del ejemplo de lead. Esta versión es una posible implementación, no la única correcta. Una refactorización real validaría primero las reglas de negocio reales del CRM.

// refactored-crm.ts

import { z } from "zod"; // o usa una biblioteca de validación similar

// --- Capa de dominio ---

export type LeadStatus = "new" | "contacted" | "qualified" | "proposal" | "negotiation" | "won" | "lost";

export interface Lead {
    readonly id: string;
    readonly name: string;
    readonly email: string;
    readonly phone: string | null;
    status: LeadStatus;
    readonly value: number;
    readonly salesRepresentativeId: string | null;
    readonly createdAt: Date;
    updatedAt: Date;
}

export const HIGH_VALUE_THRESHOLD = 5000;
export const HIGH_VALUE_STATUS: LeadStatus = "qualified";

export function isHighValueLead(value: number): boolean {
    return value >= HIGH_VALUE_THRESHOLD;
}

// --- Capa de repositorio (interfaz) ---

export interface LeadRepository {
    findByEmail(email: string): Promise<Lead | null>;
    update(lead: Lead): Promise<void>;
    create(leadData: Omit<Lead, "id" | "createdAt" | "updatedAt">): Promise<Lead>;
}

// --- Capa de servicio de notificación (interfaz) ---

export interface NotificationService {
    notifyHighValueLead(email: string, leadId: string): Promise<void>;
}

// --- Capa de aplicación ---

const LeadInputSchema = z.object({
    name: z.string().min(1),
    email: z.string().email(),
    phone: z.string().optional(),
    status: z.enum(["new", "contacted", "qualified", "proposal", "negotiation", "won", "lost"]).optional(),
    value: z.number().nonnegative(),
    salesRepresentativeId: z.string().optional(),
});

export type LeadInput = z.infer<typeof LeadInputSchema>;

export type CreateOrUpdateLeadResult =
    | { ok: true; lead: Lead }
    | { ok: false; error: "validation_error"; details: string }
    | { ok: false; error: "notification_failed"; leadId: string }
    | { ok: false; error: "unknown_error"; details: string };

export async function createOrUpdateLead(
    leadInput: LeadInput,
    leadRepository: LeadRepository,
    notificationService: NotificationService
): Promise<CreateOrUpdateLeadResult> {
    const parseResult = LeadInputSchema.safeParse(leadInput);
    if (!parseResult.success) {
        return {
            ok: false,
            error: "validation_error",
            details: parseResult.error.message,
        };
    }

    const validInput = parseResult.data;

    try {
        const existingLead = await leadRepository.findByEmail(validInput.email);

        if (existingLead && existingLead.status !== "lost") {
            const updatedLead: Lead = {
                ...existingLead,
                value: existingLead.value + validInput.value,
                status: isHighValueLead(existingLead.value + validInput.value)
                    ? HIGH_VALUE_STATUS
                    : existingLead.status,
                phone: validInput.phone ?? existingLead.phone,
                updatedAt: new Date(),
            };

            await leadRepository.update(updatedLead);

            if (updatedLead.status === HIGH_VALUE_STATUS) {
                try {
                    await notificationService.notifyHighValueLead(updatedLead.email, updatedLead.id);
                } catch {
                    return { ok: false, error: "notification_failed", leadId: updatedLead.id };
                }
            }

            return { ok: true, lead: updatedLead };
        }

        const newLead = await leadRepository.create({
            name: validInput.name,
            email: validInput.email,
            phone: validInput.phone ?? null,
            status: validInput.status ?? "new",
            value: validInput.value,
            salesRepresentativeId: validInput.salesRepresentativeId ?? null,
            updatedAt: new Date(),
        });

        if (isHighValueLead(validInput.value)) {
            try {
                await notificationService.notifyHighValueLead(newLead.email, newLead.id);
            } catch {
                return { ok: false, error: "notification_failed", leadId: newLead.id };
            }
        }

        return { ok: true, lead: newLead };
    } catch (error) {
        return {
            ok: false,
            error: "unknown_error",
            details: error instanceof Error ? error.message : String(error),
        };
    }
}

7. Notas Finales

  • Siempre preserva el comportamiento previsto. Los cambios de funcionalidad deben ser explícitos.
  • Usa nombres que tengan significado en el negocio. Escribe código que los desarrolladores puedan leer sin adivinar.
  • Define la base de datos y los contratos de la API de forma explícita. No confíes en que los objetos son implícitamente correctos.
  • Usa un manejo de errores consistente. Haz que los fracasos sean visibles y trazables.
  • Simplifica gradualmente. La deuda técnica se acumula con el tiempo y se resuelve con mejoras pequeñas y consistentes.
  • Valida en el límite y asume datos limpios dentro del dominio.
  • Prueba el comportamiento, no la implementación. Concéntrate en reglas de negocio y casos extremos.

Skills relacionados