Calidad del Software - Davidcuama/via-coinogas GitHub Wiki

Calidad del Software

Esta sección documenta las decisiones de calidad del proyecto VIA Compras: el estándar de nombramiento, la herramienta de análisis estático y formateo, la estrategia de ramas y el pipeline de integración continua. Toda la configuración vive en el repositorio (pyproject.toml, .pre-commit-config.yaml, .github/workflows/ci.yml); aquí se explica qué se eligió y por qué.


1. Estándar de nombramiento

1.1 Estándar adoptado

El proyecto sigue PEP 8 —la guía de estilo oficial de Python— complementada con la guía de estilo de Django, que es el framework sobre el que está construida la aplicación. No se inventó una convención propia: se adoptó la de la comunidad para que cualquier desarrollador Python/Django la reconozca sin leer documentación adicional.

Elemento Convención Ejemplo en el proyecto
Módulos y paquetes snake_case, cortos, sin guiones requerimientos/, cargar_catalogos.py
Clases y modelos PascalCase, sustantivo en singular Requerimiento, CentroCosto, SecuenciaRadicacion
Funciones y métodos snake_case, verbo en imperativo siguiente_consecutivo(), campos_con_error()
Variables y atributos snake_case fecha_requerida, ultimo_numero
Constantes UPPER_SNAKE_CASE a nivel de módulo JUSTIFICACION_MAX_LENGTH, CAMPOS_OBLIGATORIOS
Campos de modelo snake_case; claves foráneas nombradas por la entidad, sin sufijo _id area, centro_costo, prioridad
Vistas basadas en clase <Modelo><Acción>View RequerimientoCreateView, RequerimientoConfirmacionView
Formularios <Modelo>Form RequerimientoForm
Plantillas <app>/<modelo>_<acción>.html requerimientos/requerimiento_form.html
Nombres de URL snake_case con namespace de la app requerimientos:crear, requerimientos:confirmacion
Pruebas Clase <Tema>Tests, método test_<comportamiento_esperado> test_recargar_la_confirmacion_no_duplica_el_requerimiento
Comandos de gestión snake_case, verbo cargar_catalogos

1.2 Idioma

Los identificadores del dominio (modelos, campos, vistas, URL, mensajes al usuario) están en español, porque el vocabulario del negocio de Coinogas —requerimiento, radicación, consecutivo, centro de costo— es en español y el código debe leerse igual que el formato ADM-F-22 y las historias de usuario. Lo que es propio del framework o del lenguaje (Meta, clean, save, CreateView) conserva su nombre en inglés. Se evita mezclar idiomas dentro de un mismo identificador.

1.3 Justificación

  • Es el estándar oficial del lenguaje. PEP 8 es mantenido por la Python Software Foundation y es el que aplica cualquier herramienta del ecosistema.
  • Django lo asume. Los nombres automáticos que genera el framework (requerimientos_requerimiento, id_solicitante, requerimiento_form.html) siguen esta convención; seguirla evita fricciones.
  • Es verificable automáticamente. Las reglas de nombramiento de PEP 8 están codificadas en el plugin pep8-naming (reglas N), que Ruff ejecuta en cada commit y en el pipeline. El estándar no depende de que alguien lo recuerde.

1.4 Referencias


2. Análisis estático de código y formateo

2.1 Herramienta: Ruff

Se adoptó Ruff como única herramienta de análisis estático y formateo. Ruff reúne en un solo binario lo que antes requería varias herramientas separadas:

Necesidad Herramienta tradicional En Ruff
Errores lógicos y código muerto Pyflakes reglas F
Estilo PEP 8 pycodestyle reglas E, W
Orden de imports isort reglas I
Estándar de nombramiento pep8-naming reglas N
Buenas prácticas de Django flake8-django reglas DJ
Análisis de seguridad Bandit reglas S
Formateo automático Black ruff format

2.2 Por qué Ruff

  1. Una sola herramienta, una sola configuración. Linter, verificador de nombramiento y formateador comparten pyproject.toml. Menos dependencias, menos conflictos entre herramientas.
  2. Velocidad. Está escrito en Rust y analiza todo el proyecto en milisegundos, lo que permite ejecutarlo antes de cada commit sin fricción.
  3. Corrección automática. ruff check --fix corrige imports, sintaxis obsoleta y errores estéticos; ruff format deja el código con estilo uniforme (compatible con Black). Esto cubre el punto adicional de la rúbrica sobre formateadores.
  4. Enforza el estándar de nombramiento. Las reglas N verifican PEP 8 en clases, funciones, variables y argumentos. Esto cubre el punto adicional de la rúbrica sobre nombramiento enforzado por la herramienta.
  5. Incluye análisis de seguridad. Las reglas S (equivalentes a Bandit) detectan secretos en duro, uso inseguro de subprocess, SQL concatenado, etc. Fue precisamente esta regla la que señaló el SECRET_KEY y la contraseña de base de datos escritos en settings.py, que se movieron a variables de entorno.
  6. Adopción amplia. Es la herramienta recomendada hoy por proyectos como FastAPI, pandas, Django REST Framework y por el propio equipo de Django para nuevas contribuciones.

2.3 Configuración elegida

Se partió del conjunto recomendado por la comunidad y se agregaron los plugins relevantes para Django (modo estricto). Configuración en pyproject.toml:

[tool.ruff]
line-length = 100
target-version = "py312"

[tool.ruff.lint]
select = ["E", "W", "F", "I", "N", "UP", "B", "DJ", "S", "C4", "SIM", "RUF"]
ignore = ["RUF012"]   # las clases Meta de Django usan listas como atributos por diseño

[tool.ruff.lint.per-file-ignores]
"*/migrations/*" = ["E501", "I001"]        # las genera Django
"*/tests.py" = ["S101", "S105", "S106"]     # assert y datos de ejemplo son válidos en pruebas

[tool.ruff.format]
quote-style = "double"

Decisiones puntuales:

  • Longitud de línea 100 en lugar de los 79 de PEP 8 original: es el valor que ya usaba el código del equipo y el que recomienda la guía de Django (hasta 119 para código, 79 para docstrings). 100 es un punto medio legible en pantallas actuales.
  • Migraciones excluidas de estilo: las genera el framework y no se editan a mano.
  • Reglas de seguridad relajadas solo en pruebas: un assert o una contraseña de ejemplo dentro de un test no es un riesgo.

2.4 Cómo se ejecuta

Momento Mecanismo Qué hace
Al escribir código Extensión de Ruff para VS Code / PyCharm (opcional) Señala problemas en el editor
Antes de cada commit pre-commit (.pre-commit-config.yaml) ruff check --fix + ruff format; el commit no se crea si queda algún error
En cada push / PR GitHub Actions (.github/workflows/ci.yml) ruff check, ruff format --check, pip-audit, manage.py check, migraciones al día y pruebas sobre PostgreSQL

Comandos manuales:

ruff check .            # análisis estático + nombramiento
ruff check --fix .      # lo mismo, corrigiendo lo que es seguro corregir
ruff format .           # formatear todo el proyecto
pre-commit run --all-files

2.5 Resultado de la primera ejecución

Al aplicar Ruff sobre el código existente se encontraron 11 hallazgos (imports desordenados, líneas largas, archivo sin salto de línea final, SECRET_KEY en duro) y 11 archivos con estilo inconsistente (mezcla de comillas simples y dobles). Todo quedó corregido en un único commit de calidad sin cambios funcionales, y desde entonces el pipeline exige cero hallazgos.

2.6 Análisis de seguridad y vulnerabilidades (extra)

  • Código propio: reglas S de Ruff (flake8-bandit) en cada commit y en CI.
  • Dependencias: pip-audit en CI consulta la base de datos de vulnerabilidades de PyPI (OSV) contra requirements.txt con versiones fijadas.
  • Secretos: el hook detect-private-key de pre-commit y el archivo .env fuera de control de versiones (.gitignore); .env.example documenta las variables sin exponer valores reales.

3. Estrategia de ramas: GitHub Flow

3.1 Descripción

El equipo trabaja con GitHub Flow: una única rama principal siempre desplegable (main) y ramas cortas por historia de usuario que se integran mediante pull request.

main ──●──────────●─────────────●──────────●──▶
        \        /  \          /  \        /
         HU-02 ─●    HU-03 ──●     HU-16 ─●

Reglas:

  1. main siempre está en estado funcional: nunca se hace commit directo sobre ella.
  2. Cada historia de usuario tiene su rama, creada desde main, con el nombre feature/HU-XX-descripcion-corta (por ejemplo feature/HU-16-consecutivo-radicacion). El trabajo de calidad o infraestructura usa el prefijo chore/.
  3. Los commits siguen la convención HU-XX: descripción breve en imperativo (o chore(ámbito): … para trabajo transversal).
  4. Al terminar, se abre un pull request hacia main que referencia la issue con Closes #N, de modo que GitHub cierra la historia automáticamente al fusionar.
  5. El PR se fusiona solo cuando (a) el pipeline de CI está en verde y (b) al menos un compañero lo aprobó.
  6. La rama se elimina después de fusionar.

3.2 Justificación

  • Se ajusta al tamaño del equipo y del proyecto. Somos cuatro personas entregando en sprints de tres semanas; GitFlow (con develop, release/*, hotfix/*) agrega ramas y ceremonias que no aportan cuando hay un solo entorno y una sola versión en curso.
  • Encaja con el backlog en GitHub Issues. Una rama por HU y un PR con Closes #N mantienen la trazabilidad historia → código → revisión → cierre de la issue, que es justamente lo que evalúa la rúbrica en "Gestión técnica del desarrollo".
  • Fuerza la revisión de pares. No existe forma de llegar a main sin que alguien más lea el código.
  • Es lo que el equipo ya practicaba. Los PR #36 a #45 del Sprint 1 siguieron exactamente este esquema; documentarlo formaliza la práctica en lugar de imponer una nueva.

3.3 Protección de la rama main (extra)

En Settings → Branches → Add branch protection rule para main:

  • ☑ Require a pull request before merging → Require approvals: 1
  • ☑ Require status checks to pass before merging → seleccionar Calidad de código (Ruff), Vulnerabilidades en dependencias (pip-audit) y Pruebas automatizadas (Django)
  • ☑ Require branches to be up to date before merging
  • ☑ Do not allow bypassing the above settings

Con esto, GitHub bloquea el botón de fusión hasta que el pipeline esté "green" y exista una aprobación, tal como pide la rúbrica.


4. Pipeline de integración continua

Archivo: .github/workflows/ci.yml. Se ejecuta en cada push a main y en cada pull request, con tres trabajos en paralelo:

Trabajo Pasos
Calidad de código (Ruff) ruff check (linter + nombramiento + seguridad) y ruff format --check
Vulnerabilidades en dependencias pip-audit -r requirements.txt
Pruebas automatizadas (Django) Levanta PostgreSQL 15 como servicio, manage.py check, makemigrations --check (no hay migraciones olvidadas) y manage.py test

Las pruebas corren sobre PostgreSQL —no sobre SQLite— porque el bloqueo de filas que garantiza la unicidad del consecutivo (HU-16) solo existe en el motor real; la prueba de concurrencia se omite automáticamente en SQLite.

Al cierre del Sprint 1 la suite tiene 58 pruebas automatizadas y el pipeline pasa en verde sobre PostgreSQL sin omitir ninguna.

⚠️ **GitHub.com Fallback** ⚠️