Calidad del Software - Davidcuama/via-coinogas GitHub Wiki
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é.
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 |
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.
- 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(reglasN), que Ruff ejecuta en cada commit y en el pipeline. El estándar no depende de que alguien lo recuerde.
- PEP 8 – Style Guide for Python Code: https://peps.python.org/pep-0008/
- Django coding style: https://docs.djangoproject.com/en/stable/internals/contributing/writing-code/coding-style/
- pep8-naming (reglas
Nde Ruff): https://docs.astral.sh/ruff/rules/#pep8-naming-n
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 |
-
Una sola herramienta, una sola configuración. Linter, verificador de nombramiento y formateador comparten
pyproject.toml. Menos dependencias, menos conflictos entre herramientas. - Velocidad. Está escrito en Rust y analiza todo el proyecto en milisegundos, lo que permite ejecutarlo antes de cada commit sin fricción.
-
Corrección automática.
ruff check --fixcorrige imports, sintaxis obsoleta y errores estéticos;ruff formatdeja el código con estilo uniforme (compatible con Black). Esto cubre el punto adicional de la rúbrica sobre formateadores. -
Enforza el estándar de nombramiento. Las reglas
Nverifican PEP 8 en clases, funciones, variables y argumentos. Esto cubre el punto adicional de la rúbrica sobre nombramiento enforzado por la herramienta. -
Incluye análisis de seguridad. Las reglas
S(equivalentes a Bandit) detectan secretos en duro, uso inseguro desubprocess, SQL concatenado, etc. Fue precisamente esta regla la que señaló elSECRET_KEYy la contraseña de base de datos escritos ensettings.py, que se movieron a variables de entorno. - 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.
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
asserto una contraseña de ejemplo dentro de un test no es un riesgo.
| 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-filesAl 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.
-
Código propio: reglas
Sde Ruff (flake8-bandit) en cada commit y en CI. -
Dependencias:
pip-auditen CI consulta la base de datos de vulnerabilidades de PyPI (OSV) contrarequirements.txtcon versiones fijadas. -
Secretos: el hook
detect-private-keyde pre-commit y el archivo.envfuera de control de versiones (.gitignore);.env.exampledocumenta las variables sin exponer valores reales.
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:
-
mainsiempre está en estado funcional: nunca se hace commit directo sobre ella. - Cada historia de usuario tiene su rama, creada desde
main, con el nombrefeature/HU-XX-descripcion-corta(por ejemplofeature/HU-16-consecutivo-radicacion). El trabajo de calidad o infraestructura usa el prefijochore/. - Los commits siguen la convención
HU-XX: descripción breve en imperativo(ochore(ámbito): …para trabajo transversal). - Al terminar, se abre un pull request hacia
mainque referencia la issue conCloses #N, de modo que GitHub cierra la historia automáticamente al fusionar. - El PR se fusiona solo cuando (a) el pipeline de CI está en verde y (b) al menos un compañero lo aprobó.
- La rama se elimina después de fusionar.
-
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 #Nmantienen 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
mainsin 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.
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)yPruebas 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.
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.