CommunityProgramación y desarrollogithub.com

Diseño de Librerías

Diseñar una librería — ya sea un paquete publicado o un módulo interno desde el momento en que tiene un segundo consumidor. La implementación permanece editable para siempre, mientras que la API pública, el conjunto de dependencias, las características de rendimiento y cualquier comportamiento observable se congelan en el primer uso externo (Ley de Hyrum). Cinco deberes para el llamante: no tomar el control, no agregar costos ocultos (abstracción de costo cero como estándar), no mantener estado oculto, no ocultar fallos, no bloquear la salida. Útil al diseñar o revisar una API pública, al extraer código compartido a un módulo, cuando se pide 'hacer esto reutilizable', cuando un cambio compatible con la firma altera un comportamiento observable, y cuando se está dando forma a una aplicación alrededor de los tipos y el ciclo de vida de un framework.

¿Qué es Diseño de Librerías?

Este skill proporciona una guía completa para diseñar librerías (paquetes publicados o módulos internos) con un enfoque en la estabilidad y la transparencia. Se basa en la Ley de Hyrum, que establece que cualquier comportamiento observable se vuelve una carga de trabajo para los llamantes, por lo que la API debe congelarse en el primer uso externo. Define cinco deberes clave: no tomar el control (separar mecanismo de política), no agregar costos ocultos (abstracción de costo cero), no mantener estado oculto (evitar singletons y estado global), no ocultar fallos (modelar errores en los tipos) y no bloquear la salida (ofrecer dos niveles de API). También relaciona estos principios con Clean Architecture, destacando que una buena arquitectura minimiza la libertad que cada dependencia le quita al llamante. Es ideal para diseñadores y revisores de APIs que buscan crear librerías robustas y fáciles de mantener.

Compatible con~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/ShortArrow/skills/tree/HEAD/skills/library-design

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

El Contrato de la Librería

Una aplicación es juzgada por sus usuarios; una librería es juzgada por programadores, y en un momento específico — cuando quieren algo que el autor no imaginó. Una buena librería no es aquella que es conveniente cuando se usa como se pretendía. Es aquella que no tiene que ser descartada cuando se usa de otra manera.

Cada regla a continuación se deriva de una asimetría. Dentro de la librería puedes cambiar cualquier cosa, para siempre. Fuera de ella, todo lo que un llamante puede observar se congela la primera vez que alguien depende de ello — y con suficientes llamantes, alguien depende de todo.

Qué se congela, y cuándo

La Ley de Hyrum es la condición operativa, no una curiosidad: dados suficientes usuarios, cada comportamiento observable se convierte en una carga — redacción de excepciones, orden de enumeración, salida de logs, temporización, incluso el comportamiento que documentaste como indefinido. "Nunca estuvo en el contrato" no deshace el daño del llamante que envió su código basado en ello.

Así que la línea de congelación está en la observabilidad, no en la palabra clave public, y se siguen tres consecuencias:

  • La superficie comienza pequeña. Un símbolo, una vez público, es una promesa que puedes ampliar pero nunca retirar silenciosamente. "Público por ahora" es una deuda contraída sin beneficio; expón bajo demanda.
  • Los formatos sobreviven al código. Un formato de archivo, un protocolo de red, un esquema persistido sobrevive a cada reescritura de la librería que lo envuelve. Serializar la estructura interna tal cual suelda la capa congelada a la editable.
  • Un módulo interno cruza esta línea en su segundo consumidor, no en el empaquetado. En el momento en que existen dos llamantes, su API tiene la misma economía de congelación que un paquete publicado — solo que el radio de explosión es menor.

El Versionado Semántico es la forma pública de la misma línea, y comienza negándose a funcionar sin una: "El software que utiliza Versionado Semántico DEBE declarar una API pública" (SemVer 2.0.0, cláusula 1). Una vez declarada, los incrementos se deciden en lugar de sentirse — un cambio incompatible con versiones anteriores a esa API DEBE aumentar la versión MAYOR (cláusula 8), una nueva funcionalidad compatible con versiones anteriores DEBE aumentar la versión MENOR (cláusula 7), una corrección compatible con versiones anteriores DEBE aumentar la versión PATCH (cláusula 6). La Ley de Hyrum es lo que hace difícil la declaración: el especifica congela lo que declaraste, y los llamantes congelan lo que observaron.

El espec también tiene un nombre para el módulo que aún no ha cruzado la línea. "La versión mayor cero (0.y.z) es para desarrollo inicial. CUALQUIER COSA puede cambiar en cualquier momento" (cláusula 4). Esa es la etiqueta honesta para un módulo con un consumidor, y el día que gana un segundo es el día en que se debe 1.0.0, no el día en que se empaqueta.

No tomes el control

Separa el mecanismo de la política. Realizar una solicitud HTTP es mecanismo; reintentarla tres veces con retroceso exponencial es política, y la política integrada en el núcleo reduce la librería a los llamantes que la comparten. Ofrece valores predeterminados convenientes para registro, hilos, reintentos, tiempos de espera, caché, serialización — y haz que cada uno de ellos sea reemplazable.

El rol de la aplicación es singular, y no es tuyo. Salir del proceso, escribir en stdout, atrapar SIGINT, instalar manejadores de excepciones de nivel superior, redimensionar el pool de hilos: exactamente un componente por proceso puede hacer esto, y una librería que lo hace lucha contra el componente que tiene derecho a hacerlo. Leer las entradas del proceso pertenece al mismo rol: las variables de entorno, los archivos de configuración y los argumentos de línea de comandos se leen en la raíz de la aplicación, que entrega cada valor a la librería como un parámetro o un objeto de opciones. El llamante entonces elige de dónde proviene un valor, y una prueba lo pasa en lugar de configurar la máquina.

La magia — auto-detección, auto-conversión, auto-reconexión, auto-caché — es admisible solo mientras el llamante pueda reconstruir por qué hizo lo que hizo. La conveniencia que el usuario no puede depurar es control tomado, no trabajo ahorrado.

No agregues costos ocultos

Cuándo suceden las cosas pertenece al llamante. Una importación que inicia un hilo, un constructor que abre una conexión, un inicializador estático que lee variables de entorno — cada uno mueve trabajo real a un momento que el llamante nunca eligió y no puede devolver.

Una dependencia es parte de tu API. Su tiempo de ejecución, su cadencia de actualización, su estabilidad ABI, su licencia, sus efectos secundarios de inicialización se transfieren a cada llamante; estás eligiendo en su nombre.

Las características de rendimiento son contrato. Convertir una búsqueda O(1) en O(n) — o duplicar asignaciones y copias — es un cambio incompatible que lleva una firma compatible, y los llamantes lo descubren en producción.

Donde la entrada llega más rápido de lo que sale — flujos, eventos, registros, colas — un búfer interno ilimitado es una fuga de memoria con retraso. La contrapresión es parte del diseño, no un detalle operativo.

El estándar para la abstracción en sí misma es costo cero, en ambas cláusulas de Stroustrup. Lo que el llamante no usa, no lo paga: la capa de conveniencia no debe gravar a la capa primitiva con sus asignaciones, su despacho o su configuración. Y lo que usan, no podrían escribirlo mejor a mano de manera razonable: una abstracción superada por código hecho a mano se elude, y una librería eludida es una librería descartada que lleva un número de versión.

No mantengas estado oculto

Singletons, cachés estáticas, bucles de eventos globales, configuración a nivel de proceso: cada uno funciona mientras tu librería es la única en el proceso, y choca en el momento en que una segunda librería, un host de complementos, o un conjunto de pruebas paralelo lo comparten. El estado vive en una instancia que el llamante crea, posee, y puede crear dos veces.

No ocultes fallos

La forma de la API declara qué puede suceder y quién es responsable. Errores, cancelación, tiempo de espera, éxito parcial, ausencia — modelados en los tipos, no encaminados a través de "esto nunca ocurre normalmente" y una excepción. La misma forma lleva las respuestas de propiedad que un comentario no puede hacer cumplir: quié n cierra el flujo, si el búfer puede reutilizarse, en qué hilo llega el callback.

Conserva la causa. Colapsar un fallo en false o null elimina exactamente la información que la capa superior necesitaba para decidir; registrarlo tú mismo y continuar pierde la misma información y se apropia de la política de registro del llamante.

La calidad se separa en el camino anormal. En el camino feliz las librerías son intercambiables; tiempo de espera, reconexión, desconexión en medio del flujo, lecturas parciales, datos corruptos y reintentos son donde residen las diferencias, así que diseña para su composición primero.

La cancelación se adapta mal — un token de cancelación atraviesa cada firma que toca. Las operaciones de E/S y de larga duración lo aceptan desde la primera versión.

No bloquees la salida

Construye dos pisos. Una API de alto nivel que sirve el caso común en una línea, descansando sobre primitivas públicas — ReadAsync() sobre ReadAsync(Memory<byte>) — para que el llamante cuyo requisito queda fuera del piso superior baje en lugar de irse. Una librería con solo el piso superior es descartada ante el primer requisito que no anticipó.

La observabilidad se inyecta, no se posee: registradores, métricas, trazas se conectan a la pila del llamante. Un formato de diagnóstico que solo tu librería lee es telemetría tomada como rehén.

Un impuesto, dos monedas

Esta habilidad y Clean Architecture son la misma negativa en dos monedas. La abstracción de costo cero rechaza el impuesto sobre el rendimiento en tiempo de ejecución; la Regla de Dependencia rechaza el impuesto sobre la capacidad de cambio. En ambas, una abstracción se juzga por lo que hace que la parte dependiente renuncie.

Deber aquíSu nombre arquitectónico
Mecanismo separado de políticaCaso de uso separado del framework y del controlador
Valores predeterminados reemplazablesInversión de dependencias — el lado estable elige la implementación
Primitivas debajo de la capa de convenienciaDetalles externos intercambiables
Sin estado globalDependencias explícitas, para que se pueda dibujar un límite
Una dependencia es parte de tu APILa Regla de Dependencia

La forma saludable del llamante coloca tu librería en la parte inferior: un detalle reemplazable debajo de su política y dominio. La forma invertida — la aplicación viviendo dentro de un framework, arrastrando su ciclo de vida, sus tipos, su DI, su modelo de error, su modelo asíncrono — es la librería actuando como la aplicación, escrita a escala arquitectónica.

La forma general: una buena arquitectura minimiza no el número de dependencias sino la libertad que cada dependencia quita.

La sentencia

Una abstracción en una librería existe para ampliar las opciones del llamante. En el momento en que quita rendimiento, control o información a cambio, ha dejado de abstraer y ha comenzado a decidir.

Cuándo no aplicar

Un módulo con un consumidor, un repositorio, un equipo no ha cruzado la línea de congelación — está en 0.y.z en términos de SemVer, lleva o no un número — y construir cada punto de enganche de política reemplazable para ello es especulación con una factura de mantenimiento. Conserva las disciplinas que no cuestan nada desde el primer día — sin estado global, fallos que conservan su causa, sin trabajo antes de que el llamante lo pida — y agrega los puntos de enganche cuando llegue el segundo consumidor, que es el momento en que dejan de ser especulativos.

Fuentes

  • Versionado Semántico 2.0.0, semver.org: cláusula 1 (la API pública DEBE declararse), cláusulas 6–8 (incrementos PATCH, MENOR, MAYOR), cláusula 4 (0.y.z), y la entrada de FAQ sobre deprecación (documentarlo, luego publicar una versión menor con la deprecación en su lugar). Redacción verificada contra la página.
  • La Ley de Hyrum es una observación, no un estándar, y se cita como tal.

Individual skills in this repo

This repo contains 2 individual skills — each has its own dedicated page.

ShortArrow/interface-design

Deciding how a screen looks and behaves, triggered by the moments that hand the decision to the user unworked: about to ask which layout, which colour, which placement, how to draw a diagram, how wide a pane, how to split a view; about to call a visual choice a matter of taste; about to treat "it looks fine" as having checked it; about to ship a control sized by eye; about to read an automated accessibility pass, or a Lighthouse score of 100, as a clean bill of health. Most of these have an answer in a published standard, a measurable threshold, or one screenshot — WCAG 2.2 fixes contrast, target size, reflow and focus; a design system fixes spacing and components; axe-core finds about 57% of WCAG issues, and Lighthouse's accessibility score is that same engine weighted, so both leave the rest to a person. What survives is a genuine question, asked with the evidence attached. Use before asking any question about appearance, layout or interaction, and before calling

ShortArrow/test-design

Deriving test cases, triggered by the moments that produce weak ones: about to write a test list that is the happy path restated, about to derive tests by reading the implementation they check, about to test the bound the specification names and not the other one, about to enumerate the roles the specification lists and skip the caller it forgot, about to vary one condition at a time from the happy path, about to draw a state or decision table without tying each row to a test, about to write an expected result with "or" in it, about to skip the error paths as unlikely, about to leave a path unimplemented and untested, or about to grade a suite as adequate by reading it. A test derived from the implementation passes by construction; coverage counts classes, not tests. Use when writing a test plan or list, when a use case is the specification in hand, when adding tests to existing code, when reviewing a suite for gaps, and when a suite is all green and proves little.

Skills relacionados