¿Cómo usar git-notes en git? Guía completa para desarrolladores

Git incluye una funcionalidad poco conocida llamada git-notes que permite adjuntar metadatos a commits existentes u otros objetos sin modificar su historial.

En proyectos reales suele existir información adicional que no forma parte del commit original, por ejemplo:

  • Referencias a bugs
  • Resultados de integración continua (CI)
  • Revisiones de código
  • Métricas de calidad
  • Auditorías o anotaciones internas

Modificar el mensaje del commit para añadir esta información implicaría reescribir el historial, algo que puede romper flujos de trabajo o causar problemas en repositorios compartidos.

Aquí es donde entran en juego git-notes. En este artículo aprenderás ¿cómo usar git-notes en git? para añadir anotaciones a tus commits sin modificar el historial, mejorando la trazabilidad y colaboración en tus proyectos.

¿Qué son las git-notes?

Las git-notes son anotaciones que pueden añadirse a cualquier objeto de git, aunque el caso más habitual es anotar commits existentes.

A diferencia de modificar el mensaje de un commit, las git-notes se almacenan de forma separada del objeto original. Esto tiene varias implicaciones importantes:

  • El hash del commit no cambia
  • El historial del repositorio permanece intacto
  • Las notas pueden compartirse o no entre repositorios

Por defecto, Git almacena estas anotaciones en el namespace:

refs/notes/commits

Cada nota se asocia al hash del objeto anotado, lo que permite relacionar fácilmente commits con su metadata sin modificar el contenido original del repositorio.

La historia detrás de git-notes

Las git-notes nacieron para resolver un problema muy concreto: añadir información adicional a commits sin alterar su identidad criptográfica.

En Git, cada commit se identifica mediante un hash SHA, generado a partir de su contenido y metadatos. Si modificas el mensaje del commit o cualquiera de sus metadatos, el hash cambia y se crea un commit completamente distinto.

Esto complica tareas habituales como:

  • Añadir resultados de CI a commits ya publicados
  • Asociar revisiones de código posteriores
  • Adjuntar información generada automáticamente por herramientas

La idea original fue propuesta en 2007 por Johan Herland en la mailing list de Git. Su diseño estaba inspirado en herramientas como StGit, que permitían gestionar metadatos asociados a parches. Con el tiempo, la propuesta evolucionó hasta integrarse en el propio Git. Los principales autores de la implementación en el núcleo de Git fueron Johan Herland y Johannes Schindelin.

¿Cómo funcionan internamente las git-notes?

Para entender cómo funcionan las git-notes, conviene revisar primero cómo Git almacena la información internamente.

Git guarda todos los datos como objetos:

  • blobs → contenido de archivos
  • trees → estructura de directorios
  • commits → snapshots del proyecto

Cada objeto está identificado por un hash SHA y se almacena en la base de datos interna del repositorio.

Las git-notes reutilizan exactamente esta misma infraestructura.

Estructura interna

Cuando ejecutas:

git notes add -m "Internal note"

Git realiza internamente los siguientes pasos:

  1. crea un blob con el contenido de la git-note
  2. actualiza la estructura de directorios (tree) de la git-note
  3. crea un commit en la referencia al git-note

Cada modificación genera un nuevo commit en:

refs/notes/commits

Por lo tanto, incluso las git-notes tienen su propio historial, que puede consultarse con:

git log -p refs/notes/commits

Esto permite saber cuándo se añadieron o modificaron las notas dentro del repositorio.

Organización de la estructura de diretorios (tree) de git-notes

Dentro de la estructura de directorios, los nombres de archivo corresponden al hash del objeto anotado.

Esto crea una relación directa entre un commit y su git-note, ya que cada entrada en la estructura de directorios utiliza el hash del objeto como identificador.

En otras palabras, Git mantiene un mapa entre commits y git-notes, donde el hash del commit apunta al blob que contiene el texto de la anotación.

Un ejemplo simplificado de esta estructura sería:

refs/notes/commits
└── tree
├── a1b2c3d4... -> blob (git-note)
├── f9e8d7c6... -> blob (git-note)

En esta estructura de directorios:

  • a1b2c3d4... representa el hash de un commit
  • el blob asociado contiene el texto de la git-note

Gracias a esta estructura, Git puede recuperar rápidamente los git-notes asociados a cualquier objeto sin modificar el commit original.

Usando git-notes

Veamos un escenario real:

Imagina que trabajas en un proyecto donde cada commit debe pasar por:

  • un pipeline de CI
  • una revisión de código
  • una verificación de seguridad

Modificar el mensaje del commit para incluir esta información no es recomendable, porque:

  • Cambia el hash del commit, generando un nuevo commit distinto
  • Rompe el historial compartido, afectando a otros colaboradores
  • Puede impactar ramas ya publicadas y complicar merges

En lugar de modificar el commit, podemos usar git-notes para almacenar metadatos adicionales sin alterar el historial principal.
Esto permite documentar resultados de CI, revisiones y auditorías directamente asociados a cada commit, manteniendo intacto su contenido original.

Ejemplo

Partamos de un repositorio simple para ver cómo usar git-notes en la práctica.

Primero creamos un repositorio y añadimos un archivo:

cd /tmp
git init notes-demo
cd notes-demo
echo "console.log('hello')" > app.js
git add app.js
git commit -m "Add initial application"

Ahora verificamos el historial del repositorio:

git log --oneline

Salida:

44224f9 (HEAD -> master) Add initial application

Este será el commit sobre el que añadiremos nuestras primeras git-notes.

Agregar una git-note a un commit

Supongamos que el sistema de CI ejecuta pruebas sobre este commit y queremos guardar el resultado asociado a él.

Podemos hacerlo con:

git notes add -m "CI: tests passed"

Este comando añade una git-note al commit actual (HEAD).

Para ver la git-note asociada al commit podemos usar:

git notes show

Salida:

CI: tests passed

También podemos visualizar la git-note directamente en el historial de commits ejecutando:

git log --show-notes

Git mostrará algo similar a esto:

commit 44224f9654ca62bbbffae888bf727f25104e3f3c (HEAD -> master)
Author: ...
Date: ...

Add initial application

Notes:
CI: tests passed

De esta forma podemos añadir información útil a los commits sin modificar su contenido original ni alterar su hash.

Añadir git-notes desde archivos

En muchos casos, la información que queremos asociar a un commit no se escribe manualmente, sino que proviene de herramientas automáticas, por ejemplo reportes de tests o resultados de CI.

Primero creamos un archivo con el reporte:

echo "CI Results
Unit tests: OK
Lint: OK
Coverage: 87%" > ci-report.txt

Ahora intentamos añadir esta información como una git-note:

git notes add -F ci-report.txt

Git mostrará un error similar a este:

error: Cannot add notes. Found existing notes for object 44224f9654ca62bbbffae888bf727f25104e3f3c. Use '-f' to overwrite existing notes

Esto ocurre porque el commit ya tiene una git-note asociada.

Por defecto, git notes add no sobrescribe notas existentes para evitar perder información accidentalmente. Si realmente quieres reemplazar la nota actual, puedes usar la opción -f (force).

Editar un git-note

Si un commit ya tiene una nota asociada, podemos editarla directamente con:

git notes edit

Git abrirá el editor configurado en el repositorio con el contenido actual de la nota:

CI: tests passed

Podemos modificarla para añadir más información. Por ejemplo:

CI Results
Tests: OK
Coverage: 87%
Pipeline: success

Guardamos el archivo y cerramos el editor.
Git actualizará automáticamente la git-note asociada al commit.

Si ahora consultamos el historial con:

git log --show-notes

Veremos algo como:

commit 44224f9654ca62bbbffae888bf727f25104e3f3c (HEAD -> master)
Author: ...
Date: ...

    Add initial application

Notes:
CI Results
Tests: OK
Coverage: 87%
Pipeline: success

De esta forma podemos enriquecer las anotaciones asociadas a un commit sin modificar su historial ni su hash.

Agregar mas información a un git-note existente

Si queremos añadir más información a una git-note sin reemplazar su contenido, podemos usar el comando append.

git notes append -m "Review: approved by Alice"

Este comando agrega el nuevo texto al final de la nota existente, en lugar de sobrescribirla.

Si ahora consultamos el historial con:

git log --show-notes

Obtendremos algo similar a:

commit 44224f9654ca62bbbffae888bf727f25104e3f3c (HEAD -> master)
Author: ...
Date: ...

    Add initial application

Notes:
    CI Results
    Tests: OK
    Coverage: 87%
    Pipeline: success
    
    Review: approved by Alice

Este comando es especialmente útil cuando diferentes herramientas o procesos agregan información al mismo commit, por ejemplo:

  • pipelines de CI
  • sistemas de code review
  • auditorías de seguridad
  • herramientas de análisis estático

Cada sistema puede añadir su propia metadata sin interferir con las notas existentes.

Agregar git-notes a commits específicos

También es posible añadir git-notes a commits antiguos, no solo al commit actual.

Primero obtenemos el hash del commit:

git log --oneline

Salida típica:

44224f9 (HEAD -> master) Add initial application

Después añadimos la nota indicando el hash del commit. Por ejemplo:

git notes add -m "Security audit: passed" 44224f9

De esta forma podemos enriquecer commits históricos con información adicional, incluso mucho tiempo después de haber sido creados.

Este enfoque resulta especialmente útil para:

  • Documentación técnica asociada a cambios antiguos
  • Auditorías de seguridad posteriores
  • Resultados de herramientas automáticas
  • Referencias a incidencias detectadas más tarde

Ver todas las git-notes del repositorio

Para ver qué commits tienen git-notes asociadas, podemos usar:

git notes list

Este comando muestra una lista de las notas almacenadas en el repositorio junto con el hash del objeto al que están asociadas.

Si queremos ver las notas directamente dentro del historial de commits, podemos ejecutar:

git log --show-notes

De esta forma, Git mostrará las git-notes junto al mensaje de cada commit, lo que facilita entender el contexto de los cambios.

También es posible configurar Git para que muestre siempre las notes automáticamente al ejecutar git log:

git config --global notes.displayRef refs/notes/commits

Git mostrará siempre las git-notes del namespace refs/notes/commits al consultar el historial.

Por ejemplo, al ejecutar:

git log

Veremos algo como:

commit 44224f9654ca62bbbffae888bf727f25104e3f3c (HEAD -> master)
Author: ...
Date: ...

Add initial application

Notes:
CI Results
Tests: OK
Coverage: 87%
Pipeline: success

Review: approved by Alice

Ventajas de esta configuración:

  • No hace falta añadir --show-notes cada vez que consultamos el historial.
  • Permite ver automáticamente el contexto y metadatos de los commits.
  • Mantiene intacto el historial principal del repositorio, ya que las notas están en un namespace separado.

Eliminar un git-note

Si una git-note deja de ser relevante o ya no necesitamos la información que contiene, podemos eliminarla con:

git notes remove

Esto eliminará la nota asociada al commit actual (HEAD).

Si luego intentamos mostrarla:

git notes show

Git mostrará un mensaje como:

error: no note found for object 44224f9654ca62bbbffae888bf727f25104e3f3c

Es importante notar que solo se elimina la nota, mientras que el commit original permanece intacto.
El hash del commit no cambia, y el historial del repositorio sigue siendo el mismo.

Los namespaces de git-notes

Uno de los aspectos más potentes de git-notes es que soportan namespaces independientes.

Esto permite organizar distintos tipos de metadatos sin mezclarlos, por ejemplo separar notas de CI, revisiones de código o auditorías de seguridad.

Añadir una git-note en otro namespace

git notes --ref=bugs add -m "Bug #123 fixed"

Mostrar la nota en ese namespace

git notes --ref=bugs show

Listar todos los namespaces de notas

git for-each-ref refs/notes

Sincronizar git-notes entre repositorios

Al igual que las ramas, las git-notes pueden sincronizarse entre repositorios, lo que permite compartir metadatos sin alterar los commits originales.

Enviar notas a un repositorio remoto

git push origin refs/notes/*

Traer notas desde un repositorio remoto

git fetch origin refs/notes/*:refs/notes/*

Fusionar notas manualmente. Si existen notas diferentes en repositorios distintos, podemos fusionarlas con:

git notes merge <notes-ref>

Estrategias de merge disponibles

EstrategiaDescripción
manualResolución manual, crea un workspace temporal
oursPrioriza notas locales
theirsPrioriza notas remotas
unionConcatena notas de ambos repos
cat_sort_uniqConcatena y elimina duplicados

Por defecto, git usa la estrategia manual, que permite resolver conflictos de git-notes sin sobrescribir información importante.

Esto hace que las git-notes sean fácilmente colaborativas, ideales para equipos que comparten metadatos de CI, revisiones o auditorías.

Buenas prácticas

Si decides implementar git-notes en un equipo, conviene seguir algunas reglas para mantener el sistema ordenado y seguro.

1. Define namespaces claros

Organiza los metadatos en namespaces independientes para evitar confusión y facilitar la automatización. Por ejemplo:

refs/notes/ci       # estado de builds
refs/notes/review # comentarios de revisión
refs/notes/security # auditorías de seguridad

2. Evita datos sensibles

Ten en cuenta que las git-notes pueden compartirse con otros repositorios.
No incluyas información confidencial o contraseñas en las notas.

3. Automatiza su uso

Las git-notes se integran muy bien con flujos automatizados, por ejemplo:

  • hooks de Git para añadir notas al hacer commits
  • pipelines de CI para registrar resultados de tests o builds
  • herramientas internas que generan métricas, revisiones o auditorías

Automatizar el proceso reduce errores humanos y asegura que todas las notas se mantengan actualizadas y consistentes.

Conclusión

Las git-notes son una funcionalidad potente, aunque poco utilizada, que permite añadir metadatos a commits sin modificar el historial.

Gracias a su diseño:

  • el hash del commit permanece intacto
  • los metadatos se almacenan por separado
  • se pueden organizar mediante namespaces
  • pueden sincronizarse entre repositorios

Esto las convierte en una herramienta muy útil para:

  • pipelines de CI
  • sistemas de revisión de código
  • auditorías de seguridad
  • anotaciones internas y documentación técnica

Si necesitas añadir contexto al historial de Git sin reescribir commits, git-notes ofrece una solución elegante, flexible y segura, ideal para equipos y proyectos que requieren trazabilidad y enriquecimiento del historial de manera colaborativa.

Documentación técnica en la era de la IA: guía esencial

La documentación técnica en la era de la IA, documentar ya no es un simple trámite: se ha convertido en un activo estratégico que potencia la productividad y facilita el onboarding de nuevos colaboradores. La documentación técnica en la era de la IA asegura además que los sistemas inteligentes funcionen con precisión y que el conocimiento crítico del proyecto quede accesible para todo el equipo. Ignorarla limita la escalabilidad del equipo y del producto, mientras que aprovecharla correctamente permite proyectos sostenibles y eficientes.

Este enfoque no es inocente: relegar la documentación es un error estratégico. Hoy, la documentación técnica en la era de la IA deja de ser un subproducto del desarrollo y se transforma en un componente esencial para la sostenibilidad, la escalabilidad y la madurez de cualquier sistema de software.

En este artículo, analizaremos cómo la documentación técnica ha evolucionado y cómo la inteligencia artificial ha elevado su papel: de artefacto auxiliar a pieza central de la capa de inteligencia que sostiene los flujos de desarrollo modernos.

Infografía de la evolución de la documentación técnica: de manuales en papel, a guías digitales, hasta documentación asistida por IA con interfaces inteligentes y chatbots.

¿Para qué existe realmente la documentación técnica?

Partamos del origen.

La documentación técnica surge de los manuales de operación con un objetivo muy concreto: reducir la fricción operativa. Su propósito era evitar que el equipo tuviera que responder, de forma repetitiva, las mismas preguntas.

Preguntas como:

  • ¿Cuál es el propósito del proyecto?
  • ¿Qué problema resuelve?
  • ¿Cómo se instala, configura y utiliza?
  • ¿Qué ha cambiado entre versiones?
  • ¿Cómo se resuelven los problemas más comunes?
  • ¿Cómo pueden contribuir nuevos colaboradores?

Al persistir estas respuestas en documentos claros, estructurados y accesibles, los equipos habilitan un modelo de soporte autoservicio.

“Lea la documentación primero” no era una evasiva. Era un mecanismo simple de delegación y escalabilidad: menos interrupciones implican más foco, y más foco se traduce en mayor capacidad de ejecución.

La documentación como activo estratégico

Con el paso del tiempo, la documentación técnica dejó de limitarse a manuales operativos y comenzó a cubrir áreas más amplias del producto: arquitectura, decisiones técnicas, procesos, APIs, modelos de datos y estándares internos.

Esa evolución aportó beneficios concretos:

  1. Onboarding más rápido: Los nuevos colaboradores se integran antes cuando existen guías claras, ejemplos funcionales y referencias completas.
  2. Retención del conocimiento: El conocimiento deja de estar en unos pocos y se convierte en patrimonio colectivo.
  3. Reducción de duplicidad: Guías bien definidas evitan retrabajo, decisiones inconsistentes y soluciones paralelas al mismo problema.
  4. Gobernanza y trazabilidad: Las decisiones arquitectónicas documentadas permiten auditar el pasado del proyecto y entender —o justificar— el presente.
  5. Progreso observable: Una documentación sólida facilita medir adopción, madurez y evolución del sistema.

En organizaciones con múltiples equipos, la documentación dejó de ser opcional. Se convirtió en un requisito estructural para poder escalar sin perder coherencia.

Creación de buenas prácticas de documentación

A medida que la documentación técnica ganaba relevancia, se volvió necesario establecer marcos de trabajo y estándares que proporcionaran una base sólida para el crecimiento y la coherencia de los proyectos.

Marcos conceptuales

Diátaxis propone estructurar la documentación en cuatro categorías claras:

  • Tutoriales – Aprendizaje guiado paso a paso.
  • Guías prácticas (How-to) – Resolución de tareas concretas.
  • Explicaciones – Conceptos y contexto detrás de decisiones.
  • Referencias técnicas – Información completa y precisa para consulta.

Esta separación evita confundir aprendizaje guiado con documentación formal de referencia.

Guías de estilo

Guías como la Google – Guía de Estilo para Documentación técnica promueven consistencia, claridad y enfoque en el lector técnico, asegurando que la documentación sea fácil de leer y mantener.

Estándares de formato

Estándares como Sembr mejoran la legibilidad, mantenibilidad y estructura del contenido técnico, facilitando la actualización y la integración con procesos de desarrollo continuos.

Estas buenas prácticas transforman la documentación de un simple repositorio de información a un activo estratégico y escalable dentro del ciclo de vida del software.

Eficiencia en la validación de documentación técnica

La incorporación de herramientas en un aseguramiento continuo, ha marcado un hito en la calidad de la documentación técnica. Hoy, los documentos no solo se escriben; se verifican constantemente para garantizar precisión, consistencia y funcionalidad.

Algunas áreas clave de validación incluyen:

  • Correctores ortográficos – Detectan palabras mal escritas.
  • Validadores gramaticales – Aseguran que las reglas del lenguaje se apliquen correctamente.
  • Detectores de enlaces rotos – Identifican referencias desactualizadas o modificadas.
  • Pruebas automatizadas sobre ejemplos de código – Verifican que los fragmentos de código funcionen correctamente.
  • Herramientas de linting – Validan la estructura y consistencia de los documentos.

Estas herramientas no solo facilitan el acceso a información confiable, sino que también mantienen la documentación vigente, permitiendo adaptarse rápidamente a cambios constantes en proyectos modernos y entornos tecnológicos dinámicos.

Documentación en la era de la IA

Con la llegada de la inteligencia artificial como herramienta disruptiva, la documentación técnica ha cambiado de paradigma en dos frentes principales:

  1. Cómo se crea y valida la documentación.
  2. Cómo se consume y aprovecha esta documentación.
Ilustración que muestra los beneficios de la IA en la documentación técnica: por un lado, la IA mejora la calidad de la documentación con corrección automática, validación de código y sugerencias de claridad; por otro, la documentación estructurada y validada alimenta a la IA como fuente de información confiable, representando un ciclo de mejora continua entre humanos, documentación y sistemas inteligentes.

IA para mejorar la calidad de la documentación

Las herramientas tradicionales detectan errores tipográficos o sintácticos, pero carecen de comprensión contextual. Por ejemplo, un corrector ortográfico no distingue si escribes “Pairs” en lugar de “Paris”.

Los modelos de IA contextual sí pueden identificar este tipo de incoherencias semánticas, ambigüedades y errores lógicos.

Las herramientas asistidas por IA permiten:

  • Mejorar claridad y precisión técnica.
  • Detectar ambigüedades y lagunas lógicas.
  • Validar ejemplos de código y casos extremos (edge cases).
  • Sugerir mejoras estructurales y de consistencia.

Estamos pasando de una validación superficial a una validación semántica profunda, más alineada con las necesidades reales del desarrollo moderno.

Documentación como fuente esencial de la IA

La relación es recíproca. La IA no solo mejora la generación de documentación; también depende de ella.

Los sistemas de IA requieren:

  • Documentación actualizada y estructurada.
  • Contexto técnico validado por humanos.
  • Ejemplos funcionales y completos.

Si la documentación es ambigua, incompleta o está obsoleta, la calidad de las respuestas generadas por IA se degrada, perdiendo relevancia y utilidad.

Esto eleva el estándar: ya no basta con que la documentación sea “aceptable”. Debe ser precisa, estructurada, validada y mantenida continuamente.

Hoy, la documentación sirve a dos audiencias simultáneamente:

  1. Personas que buscan orientación clara y accionable.
  2. Sistemas de IA que necesitan contexto confiable para generar respuestas correctas.

En la era de la IA, la documentación deja de ser un simple soporte: se convierte en una capa de inteligencia que impulsa los flujos de desarrollo modernos.

Conclusión

En la era actual, la documentación técnica deja de ser un actor secundario y se convierte en un componente estratégico para cualquier negocio. Su protagonismo amplifica beneficios concretos:

  • Reduce la carga de soporte: No de manera estática, sino dinámica: chatbots y sistemas de autoservicio permiten búsquedas rápidas y respuestas inmediatas a múltiples usuarios.
  • Mejora la experiencia del desarrollador: Agentes de escritura y documentación adaptativa permiten crear contenido técnico para distintos tipos de audiencias.
  • Aumenta la productividad humana: Menos interrupciones, más foco, mejores resultados.
  • Potencia la efectividad de la IA: La documentación precisa y estructurada alimenta sistemas de IA, garantizando respuestas relevantes y actualizadas.
  • Mejora la resiliencia organizacional: El conocimiento del negocio deja de estar concentrado en pocos individuos y se convierte en un recurso accesible para toda la organización.

Si quieres proyectos sostenibles y escalables, documenta como si fuera código crítico. Porque, en realidad, lo es.

IA aplicada al linting: del reporte de problemas a las acciones ejecutables

En el artículo anterior describí el contexto actual: equipos de desarrollo capaces de generar más artefactos de software que nunca y equipos de operaciones obligados a procesarlos a una velocidad que el modelo tradicional simplemente no soporta.

En este artículo aterrizamos ese problema con un ejemplo concreto y mostramos cómo los equipos de desarrollo y operaciones pueden apoyarse en herramientas de Inteligencia Artificial para construir soluciones que no solo reduzcan la carga operativa, sino que lo hagan de forma sostenible y estructural.

El Día 0, Día 1 y Día 2 del SDLC

Dentro del Software Development Lifecycle (SDLC) solemos dividir la evolución de un sistema en tres momentos claramente diferenciados:

  • Día 0: definimos qué vamos a construir. Requerimientos, arquitectura, tecnologías y diseño de componentes.
  • Día 1: lo hacemos realidad. Desarrollo, infraestructura, configuración y despliegue.
  • Día 2: lo mantenemos vivo. Monitoreo, optimización, gestión de incidentes y mejora continua basada en métricas.

El Día 2 es el que realmente sostiene el negocio. Sin embargo, con frecuencia la mayor parte de la atención se concentra únicamente en mantener la solución funcionando, descuidando áreas igualmente críticas como:

  • La documentación actualizada.
  • La deprecación ordenada de métodos y componentes.
  • La implementación y revisión continua de buenas prácticas.

Cuando estas dimensiones se descuidan, la deuda técnica crece de forma silenciosa. Con el tiempo, los sistemas se degradan y la integridad de la aplicación se erosiona.

Peor aún, en muchas organizaciones estas responsabilidades recaen de manera informal en pocas personas, sin un rol claramente definido ni métricas que permitan gestionarlas adecuadamente.

El resultado es predecible: una aplicación sofocada por su propia complejidad y progresivamente encaminada hacia la obsolescencia.

Automatización: necesaria, pero insuficiente

Durante años hemos intentado mitigar este problema mediante la creación de herramientas de automatización que aceleren procesos y reduzcan la intervención humana.

Sin embargo, aún existen tareas que, por su naturaleza, requieren interpretación, contexto y criterio técnico. La automatización tradicional funciona bien cuando las reglas son determinísticas. Pero cuando el problema exige análisis contextual —comprender intención, impacto o dominio— la intervención humana reaparece.

Es precisamente en ese punto donde la Inteligencia Artificial puede cerrar la brecha.

La IA no cambia el objetivo original —eliminar trabajo manual que no agrega valor directo al negocio—, sino que amplía el alcance de la automatización hacia tareas que antes considerábamos demasiado complejas o ambiguas para delegar.

Linters potenciados con IA

Tomemos como ejemplo las herramientas de linter. Estas evalúan el código para:

  • Asegurar la adherencia a buenas prácticas.
  • Mejorar la legibilidad.
  • Reducir defectos.
  • Incrementar la mantenibilidad.
  • En algunos casos, optimizar el rendimiento.

El desafío es que los linters evolucionan constantemente, incorporando nuevos patrones, reglas y recomendaciones. Cada actualización implica esfuerzo de mantenimiento, y no siempre es trivial. Algunas correcciones requieren comprensión del dominio y del contexto específico del sistema.

Aquí es donde un asistente de codificación basado en IA se convierte en un verdadero acelerador. Puede:

  • Interpretar las advertencias del linter.
  • Aplicar refactorizaciones contextualizadas.
  • Verificar que las pruebas unitarias sigan pasando.
  • Proponer cambios de bajo riesgo.

El resultado no es más trabajo, sino menos fricción operativa. El esfuerzo manual y repetitivo se traslada hacia capacidades asistidas, permitiendo que los equipos se concentren en decisiones estratégicas en lugar de tareas mecánicas.

Un ejemplo práctico: GitHub Actions + análisis con IA

Podemos materializar esta idea creando un GitHub Action que se ejecute de forma periódica utilizando la última versión de los linters en un proyecto escrito en Go.

El flujo sería el siguiente:

  1. Ejecutar golangci-lint con la versión más reciente.
  2. Si el análisis falla, capturar la salida en formato JSON.
  3. Enviar ese resultado a un modelo de IA.
  4. Generar automáticamente un issue en GitHub con un análisis estructurado y accionable.

La clave no está solo en detectar errores, sino en traducirlos en trabajo accionable con el menor esfuerzo cognitivo posible. Es transformar señal técnica en tareas claras, priorizables y ejecutables.

name: Go Linter Reporter
on:
  push:
    paths:
      - "**.go"
  schedule:
    - cron: "0 0 * * 5"
  workflow_dispatch:
permissions: read-all
jobs:
  check-golangci:
    name: Check Go Lang (syntax)
    runs-on: ubuntu-latest
    permissions:
      contents: read
      models: read
      issues: write
    steps:
      # jscpd:ignore-start
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 6.0.2
        with:
          fetch-depth: 0
          persist-credentials: false
      - uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # 6.2.0
        with:
          go-version: "^1.25"
          cache: false
          # jscpd:ignore-end
      - name: Run golangci-lint tool
        id: golangci-lint
        uses: golangci/golangci-lint-action@1e7e51e771db61008b38414a730f564565cf7c20 # 9.2.0
        with:
          version: latest
          args: --allow-parallel-runners --output.json.path linter-errors.json
      - uses: actions/ai-inference@a380166897b5408b8fb7dddd148142794cb5624a # 2.0.6
        id: ai-linter-analysis
        if: failure()
        with:
          model: mistral-ai/Ministral-3B
          prompt-file: ./linter-errors.json
          system-prompt: |
            You are a senior Go engineer interpreting golangci-lint output and converting it into a concise, GitHub-ready issue.

            Task: Analyze the provided golangci-lint output and generate a COMPLETE issue description that contains enough technical detail for another developer to fix the problem without additional context.

            HARD REQUIREMENTS:
            - Output MUST be valid Markdown.
            - Use Markdown headings (##), bullet points, and code blocks where appropriate.
            - Do NOT include CI status messages or references to external logs.
            - Do NOT invent code or context beyond the lint output.
            - Cover every reported issue.
            - Group repeated findings from the same linter when applicable.
            - Explicitly mention affected functions, variables, or constructs when named in the lint output.

            Required Markdown Structure:
            ## Issues
            For each issue or grouped set, include:
            - **Linter**
            - **File & line(s)**
            - **Affected code element(s)** (function, variable, or construct name if provided)
            - **Problem** (what the linter is flagging)
            - **Why it matters** (risk or best-practice violation)

            Style:
            - Technical, precise, and concise.
            - Assume a Go developer audience.
            - Avoid filler or meta commentary.
            - Do not suggest disabling linters unless clearly justified.

            Goal: Produce a full comprehensive but sufficiently detailed Markdown issue description that enables another developer to implement the fix confidently.
          # jscpd:ignore-start
      - name: Get the entire analysis output
        id: get-analysis-output
        if: failure()
        env:
          RESPONSE_FILE: ${{ steps.ai-linter-analysis.outputs.response-file }}
        run: |
          DELIM="EOF_$(uuidgen)"
          {
            printf "response<<%s\n" "$DELIM"
            cat "$RESPONSE_FILE"
            printf "\n%s\n" "$DELIM"
          } >> "$GITHUB_OUTPUT"
        # jscpd:ignore-end
      - uses: jayqi/failed-build-issue-action@1a893bbf43ef1c2a8705e2b115cd4f0fe3c5649b # 1.2.0
        if: failure()
        with:
          github-token: ${{ secrets.GITHUB_TOKEN }}
          title-template: "golangci-lint GitHub action has failed"
          label-name: golangci-lint-issue
          body-template: |
            The golangci-lint step has failed.

            Please check the [logs](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}) for more details.

            ${{ steps.get-analysis-output.outputs.response }}

Más allá del linter: impacto en el Día 2

Este patrón no se limita a los linters. Puede extenderse a:

  • Análisis de vulnerabilidades.
  • Revisión de configuraciones de infraestructura.
  • Interpretación de métricas y alertas.
  • Clasificación y priorización de incidentes.

El verdadero valor aparece cuando reducimos la carga operativa del Día 2 sin sacrificar calidad. Cuando la mejora continua deja de depender exclusivamente de la voluntad individual y pasa a ser parte estructural del sistema.

La IA no reemplaza la responsabilidad técnica. La refuerza.

Y cuando se integra correctamente en el SDLC, no solo reduce trabajo: preserva la salud del sistema a lo largo del tiempo.

DevOps: el siguiente cuello de botella

La pared de la confusión

Durante los últimos años hemos visto una transformación profunda en la forma en que distribuimos y operamos software. Antes, existía una separación clara —y problemática— entre los equipos de desarrollo y operaciones, conocida como la pared de la confusión. Esta barrera cultural y operativa impedía que los desarrolladores entendieran las prioridades, métricas y restricciones de los equipos de operaciones.

Cada grupo trabajaba de manera aislada, enfocado en cumplir sus propios objetivos, sin una visión integral del sistema ni del impacto organizacional. Como resultado, los desarrolladores entregaban nuevos artefactos de software a los operadores con la expectativa de un despliegue inmediato, sin considerar aspectos críticos como estabilidad o escalabilidad.

Por su parte, los equipos de operaciones recibían una creciente avalancha de solicitudes de cambio. Para proteger la estabilidad, aplicaban procesos rígidos de control, como flujos de aprobación y sistemas de tickets. Aunque buscaban mitigar riesgos, en la práctica frenaban a la organización y reducían su capacidad de adaptación.

Sin DevOps

La llegada de DevOps

El paradigma DevOps rompió la brecha histórica entre desarrollo y operaciones. Los desarrolladores aprendieron a comprender los procesos y desafíos operativos, mientras que los operadores obtuvieron mayor visibilidad sobre la necesidad de entregar cambios rápidamente. Esta colaboración generó prácticas y herramientas que permitieron desplegar funcionalidades de manera frecuente y confiable, alineadas al valor de negocio.

De esta convergencia nació un ecosistema tecnológico: pipelines de CI/CD, observabilidad mejorada y modelos de infraestructura bajo demanda mediante Cloud Computing. Estos avances ayudaron a las organizaciones a responder con agilidad, incrementar la innovación y mejorar la rentabilidad.

El impacto de la Inteligencia Artificial

La aparición de asistentes de generación de código basados en IA ha acelerado exponencialmente la producción de software. Hoy, los equipos pueden generar más cambios en menos tiempo y con menor esfuerzo. Esta aceleración pone bajo tensión los sistemas actuales, aumentando el riesgo de saturación o fallos.

DevOps particialmente impulsado por Inteligencia Artificial

IA como solución operativa

Las organizaciones deben dotar a sus plataformas de capacidades nuevas que absorban y procesen de forma segura esta avalancha de cambios. Paradójicamente, la forma más efectiva de enfrentar los desafíos de IA es mediante el uso de más IA.

Componentes inteligentes que asistan en revisión de cambios, diagnóstico de errores, gestión de incidentes y automatización de respuestas permiten a los equipos recuperar la agilidad perdida. Estas herramientas no reemplazan a los equipos, sino que amplifican su impacto y reducen el desgaste operativo.

Mantener el ritmo de innovación

Con estas soluciones, las organizaciones pueden sostener la evolución exigida por el entorno actual, sin comprometer estabilidad ni confiabilidad.

DevOps impulsado por Inteligencia Artificial

En los próximos artículos, compartiré experiencias y aprendizajes sobre la incorporación de IA en mis proyectos, para aportar claridad sobre cómo adoptar estas tecnologías de forma pragmática y eficiente.

Creación de un repositorio remoto de Vagrant Boxes privados

En muchos de mis proyectos personales es fundamental poder recrear ambientes de desarrollo de forma reproducible, garantizando que las pruebas y despliegues se realicen sobre entornos consistentes.

Durante años he utilizado Vagrant como pieza central en mis flujos de trabajo. Su flexibilidad de configuración y capacidad de automatización en el aprovisionamiento de entornos me han permitido definir múltiples escenarios de prueba sin esfuerzo.

Problema: Soporte limitado de proveedores

Por alguna razón, Ubuntu unicamente ofrece Vagrant boxes oficiales para VirtualBox, excluyendo Libvirt de su lista de proveedores soportados.

Esta limitante me llevó a buscar alternativas como las ofrecidas por Lavabit, que durante mucho tiempo mantuvo un catálogo amplio de boxes compatibles tanto con VirtualBox como con Libvirt.

Sin embargo, en los últimos años la liberación de nuevas versiones en Lavabit se ha estancado, resultando en:

  • Boxes obsoletos o desactualizadas.
  • Descargas pesadas que incrementan los tiempos de aprovisionamiento.
  • Mayor consumo de red durante las actualizaciones.

Solución: Crear un repositorio local de Vagrant Boxes

Para resolver estos inconvenientes, recientemente he configurado un repositorio local de Vagrant Boxes que almacena imágenes generadas manualmente para Ubuntu, con soporte tanto para Libvirt como para VirtualBox.

Esta solución permite:

  • Controlar completamente las versiones utilizadas.
  • Optimizar el tiempo de aprovisionamiento.
  • Integrar fácilmente los boxes dentro de pipelines de CI/CD.

A continuación, detallo los pasos necesarios para crear este repositorio, los archivos de configuración requeridos y cómo pueden consumirse en entornos de integración continua.

Instalación de Requerimientos (Ubuntu 22.04+)

Antes de comenzar a generar y publicar los Vagrant boxes, es necesario preparar el entorno base con las herramientas necesarias.

1. Actualizar el sistema

sudo apt update && sudo apt upgrade -y

2. Instalar dependencias básicas

sudo apt-get install -y -qq curl wget gnupg lsb-release dkms apt-transport-https ca-certificates software-properties-common

3. Instalar VirtualBox

sudo apt-get install -y -qq -o=Dpkg::Use-Pty=0 gnupg
sudo mkdir -p /usr/share/keyrings
sudo gpg --dearmor --yes < oracle_vbox.asc | sudo tee /usr/share/keyrings/oracle-virtualbox-2016.gpg > /dev/null
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/oracle-virtualbox-2016.gpg] \
http://download.virtualbox.org/virtualbox/debian ${VERSION_CODENAME:-jammy} contrib" \
| sudo tee /etc/apt/sources.list.d/virtualbox.list 2>/dev/null
sudo apt-get update
sudo apt-get install -y -qq -o=Dpkg::Use-Pty=0 VirtualBox-7.2 dkms

4. Instalar HashiCorp Packer y dependencias de virtualización

curl -fsSL https://apt.releases.hashicorp.com/gpg | sudo apt-key add -
sudo apt-add-repository "deb [arch=amd64] https://apt.releases.hashicorp.com $(lsb_release -cs) main" -y
sudo apt update
sudo apt-get --assume-yes install packer qemu qemu-kvm apache2

5. Instalar plugins de Packer para múltiples proveedores

packer plugins install github.com/hashicorp/qemu
packer plugins install github.com/hashicorp/hyperv
packer plugins install github.com/hashicorp/docker
packer plugins install github.com/hashicorp/vmware
packer plugins install github.com/hashicorp/vagrant
packer plugins install github.com/hashicorp/parallels
packer plugins install github.com/hashicorp/virtualbox

Construcción de Boxes Personalizadas

Con todas las dependencias instaladas, el siguiente paso es descargar el proyecto robox el cual contiene configuraciones predefinidas para múltiples proveedores y distribuciones.

En mi caso, utilizo una copia actualizada alojada en mi propio repositorio.

El proyecto incluye el script build.sh, responsable de generar y desplegar los Vagrant boxes en el servidor web local.

Este script ejecuta internamente ./robox.sh, iniciando el proceso de reconstrucción de imágenes. Durante la ejecución:

  • El progreso se registra en la carpeta log/.
  • Las imágenes generadas se almacenan temporalmente en output/.

Finalmente, build.sh mueve las imágenes hacia /var/www/html/ y crea el archivo metadata.json necesario para su importación:

sudo tee "/var/www/html/$user/$vagrant_box/metadata.json" <<EOT
{
  "name": "$user/$vagrant_box",
  "description": "${descriptions[$vagrant_box]}",
  "versions": [
    {
      "version": "$version",
      "providers": [
        {
          "name": "libvirt",
          "url": "http://nuc10-node06/$user/$vagrant_box/$user-$vagrant_box-libvirt-x64-.box",
          "checksum_type": "sha256",
          "checksum": "$(cat "output/$user-$vagrant_box-libvirt-x64-.box.sha256" | awk '{ print $1}')",
          "architecture": "amd64",
          "default_architecture": true
        },
        {
          "name": "virtualbox",
          "url": "http://nuc10-node06/$user/$vagrant_box/$user-$vagrant_box-virtualbox-x64-.box",
          "checksum_type": "sha256",
          "checksum": "$(cat "output/$user-$vagrant_box-virtualbox-x64-.box.sha256" | awk '{ print $1}')",
          "architecture": "amd64",
          "default_architecture": true
        }
      ]
    }
  ]
}
EOT

Consumo de los Vagrant Boxes desde el repositorio local

Una vez que los Vagrant boxes han sido generados y desplegados en el servidor local, pueden consumirse fácilmente a través de los comandos estándar de importación de imágenes de Vagrant.

Por ejemplo, para agregar un box basado en VirtualBox:

vagrant box add --provider virtualbox http://nuc10-node06/generic/ubuntu2204/metadata.json

Este comando descarga los metadatos y la imagen correspondiente desde el repositorio local, registrándola como un recurso disponible para nuevos entornos.

De esta forma, cualquier equipo dentro de la red puede aprovisionar entornos de desarrollo o prueba sin depender de fuentes externas ni de una conexión a Internet.

Beneficios de mantener un repositorio local de Vagrant Boxes

Implementar un repositorio privado de Vagrant Boxes aporta ventajas claras tanto en rendimiento como en control operativo:

  • Reducción de dependencia externa dado que elimina la necesidad de descargar imágenes desde Lavabit o Vagrant Cloud.
  • Evita interrupciones por cambios o eliminación de imágenes remotas.
  • Acelera el tiempo de provisión, al obtener los boxes desde una fuente interna.
  • Versiona tus entornos de desarrollo y producción de forma controlada.
  • Reduce el tráfico externo y la carga de red hacia internet.
  • Facilita la creación de ambientes air-gapped (sin conexión externa), útiles en entornos de alta seguridad.

Conclusión

Montar un repositorio privado de Vagrant Boxes no solo mejora la eficiencia en los entornos de desarrollo, sino que también incrementa la estabilidad, control y reproducibilidad de los despliegues.

Esta práctica se integra fácilmente en pipelines de CI/CD, asegurando que cada entorno sea idéntico y predecible, sin depender de recursos externos que puedan cambiar o desaparecer.

Además, recrear periódicamente los Vagrant boxes permite reducir el tráfico de red y acelerar las tareas de validación y pruebas automatizadas ejecutadas por los sistemas de integración continua.

MCP: De SSE a Streamable HTTP — Limitaciones y beneficios

El Model Context Protocol (MCP) se ha convertido en el estándar para conectar LLMs con herramientas externas. Su primer transporte remoto, HTTP + SSE (Server-Sent Events), funcionó al inicio, pero pronto mostró límites: requería dos endpoints, era difícil de escalar, sufría bloqueos con firewalls o proxies y no tenía mecanismos de recuperación si la conexión fallaba.

Para resolverlo, la actualización v2025-03-26 introdujo Streamable HTTP, un transporte con un único endpoint que combina la simplicidad de HTTP con la flexibilidad del streaming. Esto reduce la complejidad, mejora la compatibilidad con infraestructuras de red y ofrece respuestas más rápidas y estables.

En este artículo veremos:

  • Por qué SSE resultaba problemático.
  • Cómo funciona Streamable HTTP.
  • Sus beneficios prácticos y técnicos.
  • Recomendaciones para desarrolladores MCP.

El Problema de SSE en MCP

La arquitectura inicial con HTTP + SSE requería dos endpoints:

  • /sse: conexión persistente para recibir mensajes.
  • /messages: endpoint separado para enviar mensajes.

Aunque era operativo, este diseño presentaba problemas:

  • Dos conexiones por cliente (POST y SSE).
  • Escalabilidad limitada en escenarios de alta concurrencia.
  • Incompatibilidades con firewalls y proxies que bloqueaban conexiones largas.
  • Falta de mecanismos para recuperar mensajes tras caídas.
  • Más lógica y código en cliente y servidor para coordinar canales.

En la práctica: servidores complejos, clientes frágiles y una comunicación poco confiable.

Cómo funciona Streamable HTTP

La nueva especificación reemplaza el esquema de dos endpoints con uno solo (ej. /mcp).
Dependiendo del caso, el servidor responde con:

  • Una respuesta HTTP normal, o
  • Una conexión en streaming para enviar eventos.

Esto simplifica el modelo: un único canal para todo.

Beneficios de Streamable HTTP

El cambio aporta ventajas claras:

  • Simplicidad: menos endpoints, menos código.
  • Escalabilidad: reutiliza conexiones TCP en lugar de mantener miles abiertas.
  • Compatibilidad: funciona bien detrás de firewalls, proxies y balanceadores.
  • Flexibilidad: sirve tanto para respuestas rápidas como para streams largos.
  • Robustez: mejor manejo de errores y posibilidad futura de reanudar sesiones.

SSE vs. Streamable HTTP en la práctica

AspectoSSE (HTTP + SSE)Streamable HTTP
Conexiones TCPCrecen rápidamente con la concurrencia.Reutiliza conexiones existentes.
Tasa de éxitoSe degrada al alcanzar el límite de conexiones.Mantiene alta disponibilidad con miles de clientes.
Tiempos de respuestaLatencia inestable incluso en baja carga.Respuestas más rápidas y consistentes.
Complejidad del clienteRequiere reconexiones y dos endpoints.Un único POST y procesamiento de respuesta.

Recomendaciones para desarrolladores MCP

  • Nuevos servidores: implementa directamente Streamable HTTP.
  • Servidores con SSE: añade soporte dual para una transición gradual.
  • Clientes MCP: prioriza Streamable HTTP, pero mantén SSE como alternativa para compatibilidad.

Conclusión

El paso de SSE a Streamable HTTP no es un detalle menor: es un cambio estructural que simplifica la arquitectura, mejora la escalabilidad y hace más confiable el ecosistema MCP.

SSE seguirá disponible por compatibilidad, pero el futuro es claro: Streamable HTTP será el estándar de facto.

Si construyes un servidor MCP nuevo, comienza con Streamable HTTP.
Si mantienes uno con SSE, añade soporte dual cuanto antes.

¿Cómo asegurar builds determinísticos, rápidos y seguros en Go?

La gestión de dependencias en Go ha evolucionado mucho en los últimos años. Antes de Go modules, las dependencias se descargaban directamente desde los sistemas de control de versiones (GitHub, Bitbucket, Mercurial, SVN). Esto traía problemas recurrentes:

  • Los autores podían borrar o modificar versiones sin previo aviso.
  • Los builds no siempre eran reproducibles.

Con la llegada de GOPROXY, estos problemas se redujeron de forma significativa.

Pero, ¿Qué es un GOPROXY?

Un GOPROXY es un proxy de módulos de Go que define de dónde se descargan las dependencias. Sus objetivos principales son:

  • Determinismo: garantizar que siempre obtengas la misma versión del código.
  • Seguridad y disponibilidad: mantener accesibles las dependencias incluso si el repositorio original desaparece.

En realidad, un GOPROXY no es magia; es simplemente un servidor HTTP con endpoints bien definidos que asegura determinismo, seguridad y un fallback controlado en la resolución de dependencias.

Estrategias para usar GOPROXY

1. Usar un GOPROXY público

Los GOPROXY públicos (ej. proxy.golang.org) son gratuitos y accesibles globalmente.

Ventajas:

  • Descargas más rápidas que desde el sistema de control de versiones.
  • Integración con pkg.go.dev para explorar módulos.

Limitaciones:

  • Solo funciona con módulos públicos.
  • No gestiona dependencias privadas.

2. Combinar público + privado con GOPRIVATE

Si trabajas en proyectos de código abierto y a la vez con dependencias internas, puedes configurar la variable de entorno GOPRIVATE para excluir ciertos paths del proxy público.

Esto permite:

  • Resolver dependencias privadas directamente desde su origen.
  • Seguir usando el GOPROXY para módulos públicos.

Ejemplo de configuración:

go env -w GOPRIVATE=github.com/electrocucaracha/*

3. Desplegar tu propio GOPROXY privado

La opción más robusta es montar tu propio GOPROXY privado en tu infraestructura. Lo cual ofrece:

  • Cachea módulos públicos desde proxy.golang.org.
  • Gestiona módulos privados desde tus repositorios internos.
  • Expone un único punto de acceso mediante un repositorio virtual.

Esto te da control total sobre disponibilidad, seguridad y auditoría de dependencias.

Conclusión

Si quieres builds determinísticos, rápidos y seguros en Go, usa GOPROXY:

  • Para proyectos simples, un proxy público es suficiente.
  • Para entornos mixtos, combina GOPROXY + GOPRIVATE.
  • Para máxima robustez, despliega un GOPROXY privado.

Con esta práctica evitas dolores de cabeza con dependencias y aseguras la calidad de tus builds.

¿Cómo funciona vars() en Python? Explicación y ejemplos prácticos

La función integrada vars() en Python devuelve el diccionario de atributos (__dict__) de un objeto. Es una herramienta útil para inspeccionar el estado interno de instancias, clases o contextos de ejecución.

En este artículo, veremos ejemplos prácticos de uso, situaciones comunes y precauciones para evitar errores al usar vars().

Sintaxis de vars()

vars([obj])

obj (opcional): El objeto del que se quiere obtener el diccionario __dict__.
Si no se proporciona, vars() devuelve las variables locales del contexto actual.

Ejemplos prácticos

1. vars() con un objeto que tiene __dict__

class User:
    def __init__(self, name: str, role: str):
        self.name = name
        self.role = role

print(vars(User("Alice", "admin"))) # {'name': 'Alice', 'role': 'admin'}

Aquí, vars() devuelve el diccionario con las propiedades y valores internos de la instancia User.

2. vars() sin argumentos — contexto local

x = 42
y = "Python"

print(vars()) # {'x': 42, 'y': 'Python', ...}

En este caso, vars() devuelve el diccionario de variables locales.
Es equivalente a locals() en la práctica, aunque locals() tiene un comportamiento específico según la implementación de Python (por ejemplo, en CPython).

3. vars() con clases que usan __slots__

class WithoutDict:
    __slots__ = ['data']
    def __init__(self, data: int):
        self.data = data

vars(WithoutDict(5)) # TypeError: vars() argument must have __dict__ attribute

Si la clase define __slots__ sin incluir __dict__, no habrá diccionario de atributos y vars() lanzará TypeError.

Para que funcione, debes incluir explícitamente __dict__ en __slots__:

class WithDictInSlots:
    __slots__ = ['data', '__dict__']
    def __init__(self, data: int):
        self.data = data

print(vars(WithDictInSlots(5))) # {'data': 5}

Es por eso que antes de llamar a vars(), se recomienda compruebar si el objeto tiene un __dict__:

if hasattr(obj, '__dict__'):
    attrs = vars(obj)

Esto evita excepciones y mejora la robustez del código.

Conclusión

vars() es simple, directa y muy útil para depuración rápida e inspección de objetos en Python.
Sin embargo:

  • Comprueba que el objeto tenga __dict__.
  • Usa alternativas como dir() o inspect.getmembers() si necesitas información más completa.
  • Con __slots__ debes incluir explícitamente __dict__ si quieres compatibilidad con vars().

¿Cómo utilizar el operador Walrus?

En Python existen operadores poco conocidos pero extremadamente útiles. Uno de ellos es el operador Walrus (:=), introducido en Python 3.8 mediante el PEP 572. Este operador permite asignar un valor a una variable dentro de una expresión, lo que lo hace ideal para evitar código redundante y escribir aplicaciones más limpias y concisas.

¿Qué problema resuelve?

Antes de Python 3.8, cuando era necesario asignar un valor y evaluarlo dentro de una condición, se solía repetir la misma expresión en diferentes partes del código. Un ejemplo clásico:

line = input("User input: ")
while line != "":
    print(f"Input: {line}")
    line = input("User input: ")

Este patrón obliga a repetir la llamada a input(...), generando duplicación innecesaria.

Con el operador Walrus (:=), es posible realizar la asignación directamente dentro de la condición, eliminando duplicación y mejorando la legibilidad:

while (line := input("User input: ")) != "":
    print(f"Input: {line}")

El resultado es un código más compacto, fácil de mantener y más claro de leer.

¿Dónde más puede usarse?

Además de bucles while, el operador Walrus también puede utilizarse en:

  • List comprehensions cuando se desea filtrar y guardar valores temporales:
result = [y for x in data if (y := process(x)) is not None]
  • Condicionales anidadas, donde calcular un valor varias veces penaliza el rendimiento.

El operador walrus no solo mejora la legibilidad, sino que también puede reducir el costo computacional en estructuras intensivas si se usa correctamente.

¿Cuándo se recomienda usar?

El uso del operador Walrus (:=) es recomendable especialmente cuando:

  • Necesitas asignar y evaluar en una sola expresión
  • Quieres evitar llamadas redundantes a funciones costosas.
  • Buscas mejorar la legibilidad manteniendo el código compacto y expresivo

Consideraciones importantes

  • Compatibilidad: Aunque el soporte oficial de Python 3.8 terminó en 2024 (ver cronograma), aún existen aplicaciones legacy en versiones anteriores. Asegúrate de que tu entorno de ejecución soporte el operador.
  • Legibilidad: No abuses del operador. Si su uso compromete la claridad de la expresión, es mejor dividir el código en pasos separados.

Conclusión

El operador Walrus (:=) es una adición poderosa al lenguaje Python que permite asignar valores dentro de expresiones, especialmente útil en bucles, comprensiones de listas y condiciones complejas. Usado con criterio, te permite escribir código más limpio, conciso y eficiente, sin sacrificar claridad.

Si trabajas con Python 3.8 o superior y aún no lo has integrado en tu flujo de trabajo, este es un buen momento para hacerlo.

Distribución de paquetes Python con build y twine

Diagrama que muestra el flujo del desarrollo de software en Python, destacando las etapas de desarrollo, gestión de dependencias, linting/formato y distribución de paquetes con herramientas como build y twine.

Uno de los procesos clave en el desarrollo de software es la distribución de binarios o paquetes. Este paso facilita el consumo de código, promueve la reutilización y es fundamental para compartir librerías internas, publicar en PyPI o versionar entregables.

Con la rápida adopción de pyproject.toml como archivo de configuración estándar, Python ha modernizado su sistema de empaquetado. En este artículo explico cómo utilizar dos herramientas esenciales para este flujo: build y twine.

¿Qué es build?

El módulo build proporciona una interfaz de alto nivel para generar los artefactos de distribución de un proyecto Python:

  • sdist (Source Distribution)
  • wheel (formato binario compatible con pip)

Ambos formatos se generan a partir de la configuración en pyproject.toml, sin requerir un archivo setup.py.

Uso básico

uvx --from build pyproject-build

Este comando crea los archivos .tar.gz y .whl dentro del directorio dist/.

Ventajas de build

  • No requiere setuptools instalado globalmente
  • Compatible con pyproject.toml
  • Sigue las especificaciones modernas del Python Packaging Authority (PEP 517 y 518)

¿Qué es twine?

twine es una herramienta enfocada en la publicación de paquetes en repositorios como PyPI, desacoplándose por completo de setuptools durante esta etapa. Esto la hace más segura, portable y fácil de integrar en flujos de automatización.

Validación del paquete

Antes de subir un paquete, es buena práctica validar su integridad:

uvx twine check dist/*

Publicación en PyPI

uvx twine upload dist/*

Conclusión

build y twine forman parte del ecosistema moderno de empaquetado en Python. Al reemplazar herramientas obsoletas como setup.py, ofrecen mayor trazabilidad, mejores prácticas y compatibilidad con estándares actuales.

Distribuir correctamente tus proyectos no es un lujo, es parte del trabajo profesional. Automatiza este proceso y enfócate en lo esencial: escribir software de calidad.