Nombrar No Es Encontrar
Ya conoces siete patrones MOAD. Saber los nombres importa: te permite reconocer un patrón cuando lo ves. Pero el reconocimiento en una lección controlada difiere de la detección en una base de código que nunca has abierto.
Una base de código no etiqueta sus defectos. Un MOAD sedimentario no viene con un comentario que diga // O(N²) — arreglalo. Una multitud ensordecedora no se anuncia a sí misma como una avalancha de pérdida de caché. Los encuentras leyendo código con una pregunta específica en mente: ¿qué estructura de datos contiene estos valores, & qué operaciones se ejecutan contra ella dentro de un bucle?
La detección es una habilidad separada del reconocimiento. El reconocimiento dice: sí, ese patrón es MOAD-0001. La detección dice: déjame encontrar todos los lugares en esta base de código donde ese patrón podría existir, ya sea que pueda ver el código completo o solo un nombre de símbolo.
Primer Escaneo
Un primer paso usa grep. Cada MOAD tiene un sustrato: una estructura de datos o API cuya presencia, cerca de ciertas operaciones, es una señal que vale la pena investigar.
MOAD-0001 (Sedimentario): List.contains en un bucle
# Signal: membership test on a list variable inside a loop
grep -rn '.contains(' src/ | grep -v HashSet | grep -v TreeSet
grep -rn 'visited =' src/ | grep -v set | grep -v Set
MOAD-0002 (Intertangle): shared mutable flag across phases
# Signal: static mutable field written by one subsystem, read by another
grep -rn 'static ' src/ | grep -v final | grep -v class | grep -v void
MOAD-0003 (Leaked Context): ThreadLocal in a pooled executor
# Signal: ThreadLocal.set() without guaranteed ThreadLocal.remove()
grep -rn 'ThreadLocal' src/
grep -rn 'ThreadLocal.set' src/ -l
MOAD-0004 (Logged Secret): HTTP headers in log output
# Signal: log call with headers variable near auth endpoints
grep -rn 'log.*header' src/
grep -rn 'Authorization' src/ --include='*.log'
MOAD-0005 (Thundering Herd): cache miss with no synchronization
# Signal: cache.get() + null check + cache.put() without lock
grep -rn 'cache.get' src/ -A4 | grep 'cache.put'
Estos patrones producen candidatos, no defectos confirmados. Cada candidato necesita triage: lee el código circundante, verifica el tipo de estructura de datos, confirma que la operación se ejecuta a escala.
Leer Código para Complejidad
Grep encuentra candidatos. Leer los confirma. Cuando abres un archivo candidato, lees con una pregunta: ¿el costo de esta operación crece con el tamaño de entrada?
Para MOAD-0001, el protocolo de confirmación:
1. Find the outer loop. What bounds its iteration count?
2. Find the inner operation (.contains, .indexOf, 'in'). What data structure does it run against?
3. Does that data structure grow with the same input that drives the outer loop?
4. If yes: the cost is O(N²) where N = input size. Confirmed defect.
5. If no: the inner structure is bounded (config, enum, small constant). False positive.
Un recorrido de grafos visitando N nodos, verificando una lista visited en cada paso: tanto el bucle como la estructura de datos interna crecen con N. Confirmado.
Un manejador de solicitud verificando una lista blanca de 5 IPs de administrador: la lista blanca nunca crece con el volumen de solicitudes. Falso positivo.
El mismo protocolo se aplica a cada MOAD: identifica el controlador externo, identifica la estructura interna, pregunta si ambas escalan juntas.
Surge Score: Priorizando tus Hallazgos
No todos los defectos confirmados justifican parches inmediatos. Un MOAD en una biblioteca con 10,000 dependientes downstream tiene una surge score más alta que el mismo MOAD en una herramienta interna privada.
Surge score = speedup × in-degree. Speedup: ¿cuánto más rápido se ejecuta la solución a la escala de producción típica? In-degree: ¿cuántos paquetes o servicios downstream heredarían la solución automáticamente cuando el upstream la fusione?
Un MOAD-0001 confirmado en el resolvedor de dependencias de Apache Maven, ejecutándose en grafos de 50,000 nodos, con 1,000+ complementos Maven downstream que heredan cambios automáticamente: la surge score es muy alta. Esta solución pertenece al frente de tu cola.
Un MOAD-0001 confirmado en una herramienta CLI de usuario único sin dependientes: surge score cerca de cero. Vale la pena arreglarlo, pero no es urgente.
Nodos workaholic vs. glutton. Un nodo con alta betweenness & alto speedup es un workaholic: maneja flujo crítico & inundará colas downstream cuando se desbloquee. Aplica parches solo después de confirmar la capacidad downstream. Un nodo con alto out-degree & bajo speedup es un glutton: consume todo lo que se le alimenta & no siente dolor. Parchar un workaholic sin preparar la capacidad downstream crea MOAD-0005 (multitud ensordecedora) a escala de infraestructura.
Escanear a Fusionar: Una Tubería MOAD
Un defecto confirmado con una surge score alta se mueve a través de una tubería. Cada etapa produce un artefacto. Ninguna etapa es opcional.
scan → candidate list (grep output, static analysis results)
ticket → defect description (MOAD number, location, complexity analysis)
patch → code change (data structure swap, primitive adoption)
test → unit test (O(1) proof: time the fix at N=100 and N=10,000)
UNDF → public disclosure post (undefect.com, public domain)
disclose → CVE or CWE reference if security-relevant
PR → upstream pull request with patch + test + UNDF link
merge → maintainer acceptance; fix propagates via version bump
Cada artefacto alimenta la siguiente etapa. Una solución sin una prueba no puede verificarse. Una prueba sin una divulgación no puede propagarse a otras instancias del mismo patrón. Una divulgación sin una PR upstream deja la solución atrapada en un fork.
Un MOAD post (UNDF) es la etapa que la mayoría de los ingenieros omiten. Aplican parches al defecto, envían una PR, & se consideran listos. Pero una solución sin un post nombrado significa que cada futuro ingeniero que encuentre el mismo patrón debe redescubrir tanto el problema como la solución de forma independiente. Un MOAD post cierra el bucle de conocimiento: nombra el patrón, muestra el método de detección, & vincula al parche. Los investigadores futuros encuentran la solución buscando el nombre del patrón.
Parchar el planeta a escala. Una única solución MOAD-0001 en una biblioteca ampliamente utilizada se propaga a cada proyecto que la importa. Un MOAD post asegura que los ingenieros en proyectos que nunca actualicen esa biblioteca aún aprendan la solución. Ambos caminos corren en paralelo.
Escribiendo un Ticket de Defecto
Un buen ticket de defecto responde cinco preguntas:
1. Where: exact file, class, function, and line range
2. What: the data structure type and the operation against it
3. Why: the complexity analysis (O(N²) or worse, with N defined)
4. Impact: what inputs trigger worst-case behavior, and at what scale
5. Fix: the data structure or primitive to substitute
Un ticket que responda a los cinco es autosuficiente: un mantenedor que nunca ha leído tu análisis puede reproducir tu hallazgo & verificar tu solución. Los tickets que saltan (3) o (4) requieren que el mantenedor repita tu análisis de complejidad antes de poder fusionar. Esa fricción reduce la probabilidad de fusión.
La credibilidad se compone. Una primera PR que incluya un ticket claro, una solución bien dirigida, & una prueba de referencia se fusiona. Una segunda PR del mismo autor se revisa con menos fricción. Una tercera PR se revisa por el mantenedor que fusionó las dos primeras. La reputación en código abierto es un libro mayor de artefactos: cada solución aceptada gana confianza para la siguiente.
Leyendo un Candidato Real
Aquí hay un candidato real MOAD-0001 en Python. Léelo & completa el protocolo de triage.
class DependencyResolver:
def resolve(self, package, resolved=None, seen=None):
if resolved is None:
resolved = []
if seen is None:
seen = []
if package in seen:
return
seen.append(package)
for dep in self.registry.get_dependencies(package):
self.resolve(dep, resolved, seen)
resolved.append(package)
return resolved
Preguntas de triage:
1. What data structure is `seen`?
2. What operation runs against it on line 6?
3. Does `seen` grow with input size?
4. Does the loop that drives recursive calls also grow with input size?
5. Is this a confirmed MOAD-0001 or a false positive?
Tu Solución
Un defecto confirmado con una surge score alta necesita una solución completa: la corrección de código, una prueba que demuestre la mejora, & un esquema MOAD post.
La prueba debe ser una prueba de desempeño, no una prueba de corrección. Una prueba de corrección pasa antes & después de la solución — ese es el punto; la salida no cambia. Una prueba de desempeño en dos tamaños de entrada demuestra la mejora:
import time
def build_graph(n):
# n packages, each depending on the previous one
return {f'pkg{i}': [f'pkg{i-1}'] if i > 0 else [] for i in range(n)}
for n in [100, 1000, 5000]:
registry = build_graph(n)
resolver = DependencyResolver(registry)
start = time.perf_counter()
resolver.resolve(f'pkg{n-1}')
elapsed = time.perf_counter() - start
print(f'n={n}: {elapsed:.4f}s')
Antes de la solución, el tiempo transcurrido crece cuadráticamente con n. Después de la solución, crece linealmente. Imprime ambos & incluye los números en tu descripción PR.
Un esquema MOAD post cubre: el nombre del patrón, el sustrato (resolvedor de dependencias de Python), el método de detección (grep para in seen donde seen comienza como []), la solución, & un vínculo a tu PR. El post va a undefect.com como dominio público. Los futuros ingenieros que busquen 'prueba de membresía de lista de Python en bucle lento' lo encontrarán.