ADR-020 — Gate de análisis estático del backend (Checkstyle, PMD, SpotBugs, FindSecBugs)¶
Status: accepted (operador aprobó el gate, 2026-08-22)
Context: el ledger uscha (QA-LEDGER.json) marcaba backend.static_gate como
UNMEASURED — el placeholder java-qa-gate quedó pendiente desde el bootstrap del
proyecto (CLAUDE.md §"Project adapter", línea "Static gate: <e.g. mvn -q verify
-Pqa → ...>"). landing y gestor ya reportan static-gate real (eslint + tsc,
QA-LEDGER.json), pero backend no tenía ningún linter/SAST corriendo. Este es un
sistema con actas legales de sorteo (ver ADR-004) y datos personales de clientes
(padrón, DNI, celular) — necesita un piso de linting + SAST, no solo tests unitarios.
Alternatives:
Sin gate — descartada: es exactamente el estado actual (UNMEASURED) que este
ADR viene a cerrar; el ledger no puede medir lo que no se ejecuta.
SonarQube — hay integración disponible en el toolkit (skill
sonarqube:sonar-*), pero requiere levantar/mantener un server (SonarQube
Community o Cloud) y autenticar el repo contra él; para un backend de un solo
módulo Maven es una pieza de infraestructura adicional a operar y actualizar,
desproporcionada frente al problema (medir el backend, no correr una plataforma).
spotbugs-maven-plugin + findsecbugs-plugin: corren dentro del build existente,
sin servicio externo, producen XML que el ledger ya sabe parsear
(qa_ledger.py ingest-gate busca exactamente target/checkstyle-result.xml,
target/pmd.xml, target/spotbugsXml.xml), y son la misma familia de herramientas
que ya usa el ecosistema Java/Maven sin agregar infraestructura.
Decision: agregar un perfil Maven qa a backend/pom.xml, atado a la fase
verify (no a test, para que mvn test — el comando de todos los días — siga
rápido y sin este costo). El perfil corre:
maven-checkstyle-plugin con un ruleset propio de alta señal
(backend/config/checkstyle.xml), no sun_checks (demasiado ruidoso para este
tamaño de proyecto): imports con wildcard, catch vacío, pares equals/hashCode,
llaves obligatorias, imports sin usar, System.out/System.err prohibidos en
prod. Magic numbers y javadoc quedan OFF (no aportan señal aquí).
maven-pmd-plugin con ruleset propio (backend/config/pmd-ruleset.xml):
bestpractices + errorprone núcleo, excluyendo reglas ruidosas para este
codebase (LawOfDemeter, OnlyOneReturn, guard de log statements, reglas de
comentarios).
spotbugs-maven-plugin (con el plugin findsecbugs-plugin sumado a su classpath),
effort=Max, threshold=Medium, salida XML.
Ninguno de los tres falla el build (failOnViolation=false / failOnError=false):
el ledger es el gate, no Maven — el perfil solo tiene que producir las tres XML;
la severidad se evalúa en qa_ledger.py ingest-gate (FindSecBugs pisa a
HIGH/SECURITY, SpotBugs prio1→HIGH, Checkstyle error→HIGH, PMD prio1→BLOCKER,
prio2→HIGH), igual que ya ocurre con eslint/tsc en landing/gestor.
Consequences:
mvn test no cambia: sigue sin correr análisis estático, mismo tiempo que hoy.
mvn verify -Pqa (o -Pqa explícito en CI/ledger) es el único comando nuevo, y es
el que documenta CLAUDE.md §"Project adapter" → "Static gate".
Los hallazgos nuevos entran al gate del ledger: los normalizados a
BLOCKER/CRITICAL/HIGH se arreglan en el mismo cambio (ver Verification); el resto
(MEDIUM/LOW) va a ISSUES-DEFERRED.md bajo "static-gate backend (ADR-020,
2026-08-22)", sin bloquear.
Una falsa alarma verificada (no un hallazgo real) puede suprimirse con
@SuppressFBWarnings/suppression file puntual, siempre con una razón de una línea
junto a la supresión — nunca para ocultar un hallazgo real.
spotbugs-maven-plugin corre sobre bytecode target Java 17 (maven.compiler.release
=17) aunque el JDK de build sea 25; se fija la versión de com.github.spotbugs:
spotbugs compatible con ambos (4.8.x) para evitar el mismo tipo de fricción de
versión que ya se resolvió en ADR-018 con Jackson 3/Boot 4.
backend/pom.xml: perfil qa (activation manual, -Pqa) atado a verify, con
maven-checkstyle-plugin, maven-pmd-plugin, spotbugs-maven-plugin (+
findsecbugs-plugin como dependencia del plugin).
backend/config/checkstyle.xml (ruleset de alta señal, nuevo).
[x] mvn -q test verde (mismo set de tests, sin regresión de tiempo).
[x] mvn -q verify -Pqa genera target/checkstyle-result.xml, target/pmd.xml y
target/spotbugsXml.xml, los tres no vacíos y parseables.
[x] qa_ledger.py ingest-gate --repo backend --iteration 6 corre limpio a nivel gate
(0 hallazgos HIGH/CRITICAL/BLOCKER sin resolver tras el pase de fixes).
[x] qa_ledger.py readiness --record refleja backend.static_gate medido (deja de
estar UNMEASURED).