Cómo se forma el sedimento de código [BLOCK_TYPE SECTION/STEP]
La roca sedimentaria se forma por deposición, no por explosión. Cada capa: correcta para su época. Cada capa: más antigua que la de arriba. Las capas más antiguas se calcificaron antes de que el ecosistema madurara a su alrededor. Ningún error las causó. El tiempo las causó. [BLOCK_TYPE SECTION/STEP]
MOAD-0001 funciona de la misma manera. [BLOCK_TYPE SECTION/STEP]
[BLOCK_TYPE SECTION/STEP]
La historia de la formación
Un recorrido de grafos escrito en 1993:
List<Node> visited = new ArrayList<>();
Stack<Node> stack = new Stack<>();
stack.push(start);
while (!stack.isEmpty()) {
Node n = stack.pop();
if (!visited.contains(n)) { // escaneo lineal O(N)
visited.add(n);
for (Node neighbor : n.getNeighbors())
stack.push(neighbor);
}
}
Este código: correcto. Pruebas: aprobadas. En 1993, los grafos tenían 50 nodos.
50 nodos: 50 × 25 promedio escaneados = 1,250 operaciones. Invisible.
El código entró en el control de versiones. Fue copiado en tutoriales. Fue envuelto en bibliotecas. Fue distribuido en herramientas de compilación, gestores de paquetes y resolutores de dependencias. Para 2024, el mismo patrón se ejecutaba en grafos de dependencias con 50,000 nodos.
50,000 nodos: 50,000 × 25,000 promedio escaneados = 1,250,000,000 operaciones.
Una compilación de 1 segundo se convierte en 17 minutos.
El código no cambió. El mundo a su alrededor creció. Cada capa del sedimento: correcta al depositarse. Costosa al excavarse.
Tu Sedimento
El código sedimentario no contiene un error lógico. Contiene una suposición de rendimiento que dejó de cumplirse cuando cambió la escala. El código produce resultados correctos; solo cambió el costo.
Esta distinción importa para el diagnóstico. Un error lógico produce respuestas incorrectas. Un defecto sedimentario produce respuestas correctas a un costo inaceptable.
Cinco formas de MOAD-0001
MOAD-0001 aparece en cinco formas documentadas en más de 60 ecosistemas de software. La estructura: una prueba de pertenencia que usa un contenedor secuencial, anidada dentro de un bucle sobre la misma colección o una relacionada.
Las cinco formas
| Dominio | Patrón | Contenedor secuencial | Contenedor correcto |
|---|---|---|---|
| Recorrido de grafos | if (!stack.contains(n)) en DFS/Tarjan | ArrayList | HashSet |
| Deduplicación de rutas/eventos | TAILQ_FOREACH strcmp por solicitud | lista enlazada | HashMap |
| Seguimiento de colisiones | findLinearSearch() por tick de física | array | unordered_set |
| Filtro de asignación de recursos | List.contains() en filtro de stream | ArrayList | HashSet |
| Lista abierta de A* pathfinding | LocalVector::find() por vecino | vector | índice almacenado en heap |
Los cinco comparten la misma estructura: una comprobación de pertenencia anidada en un bucle, usando un contenedor que requiere un escaneo lineal para responder a la pregunta de pertenencia.
La lista de palabras clave de escaneo
Auditar MOAD-0001 significa buscar palabras clave de comprobación de pertenencia dentro de bucles:
- Python: in con una variable list, .count(), list.index()
- Java: ArrayList.contains(), List.contains(), LinkedList.contains()
- JavaScript: Array.includes(), Array.indexOf(), Array.find()
- C++: std::vector::find(), bucle manual con comparación ==
[BLOCK_TYPE SECTION/STEP]
- Go: slices.Contains(), bucle manual sobre un slice
[BLOCK_TYPE SECTION/STEP]
La solución en cada caso: reemplaza el contenedor secuencial por un contenedor hash. list → set. ArrayList → HashSet. Array → Set. vector → unordered_set. slice → map[T]struct{}.
[BLOCK_TYPE SECTION/STEP]
Una palabra clave. Una sustitución. Cero cambio de comportamiento. Aceleración de 1 000× con N=1 000. [BLOCK_TYPE SECTION/STEP]
Auditar un fragmento de código [BLOCK_TYPE SECTION/STEP]
Aplica el reconocimiento de patrones MOAD-0001 a un fragmento de código real. [BLOCK_TYPE SECTION/STEP]
Una línea, cuatro lenguajes
La solución para MOAD-0001 en cuatro lenguajes:
# Python
visited = [] # ANTES: pertenencia O(N)
visited = set() # DESPUÉS: O(1) membership
// Java
List<Node> visited = new ArrayList<>(); // ANTES
Set<Node> visited = new HashSet<>(); // DESPUÉS
// JavaScript
const visited = []; // ANTES: Array.includes() O(N)
const visited = new Set(); // DESPUÉS: Set.has() O(1)
// visited.push(n) → visited.add(n)
// visited.includes(n) → visited.has(n)
// Go
visited := []NodeID{} // ANTES: slices.Contains() O(N)
visited := make(map[NodeID]struct{}) // DESPUÉS: búsqueda en mapa O(1)
// _, ok := visited[n] para comprobar pertenencia
// visited[n] = struct{}{} para inserción
En todos los casos: mismo comportamiento, misma salida, mismos tests aprobados. Solo cambia la tasa de crecimiento.
Lo que la corrección NO cambia
- El comportamiento lógico del algoritmo: depth-first sigue visitando en profundidad
- Correctitud de salida: los mismos nodos se visitan en el mismo orden (dentro de DFS)
- Resultados de las pruebas: cualquier prueba que verifique la correctitud sigue aprobando
Qué SÍ cambia la corrección
- Prueba de pertenencia: O(N) → O(1)
- Bucle total: O(N²) → O(N)
- En N=1.000: 1.000× más rápido
- En N=10.000: 10.000× más rápido
Un límite: el orden
Los contenedores hash (set, HashSet, unordered_set) no preservan el orden de inserción. En Python 3.7+, dict sí preserva el orden de inserción; set simple no. En Java, HashSet no preserva el orden; LinkedHashSet sí.
Si el orden importa junto con la pertenencia: mantén dos estructuras. Un set (o HashSet) para pruebas de pertenencia O(1). Una list (o ArrayList) separada para recorrido ordenado. O usa LinkedHashSet en Java, que proporciona ambas.
Para MOAD-0001 en recorrido de grafos: visited responde una pregunta binaria (¿este nodo ya se ha visto?). El orden no importa para la pregunta de pertenencia. El orden de recorrido proviene de la pila o cola, no de visited.
Pertenencia vs Orden
En el algoritmo de componentes fuertemente conectados de Tarjan, onStack rastrea qué nodos permanecen en la pila de llamadas DFS actual. Debe responder dos preguntas: (1) pertenencia — ¿este nodo está actualmente en la pila? (2) al final de un camino DFS, extraer nodos en orden hasta que aparezca este nodo.
Una implementación ingenua usa una List para onStack, respondiendo la pregunta de pertenencia con contains() — O(N) por verificación, O(N²) en total para grafos grandes.
Por qué esto es una divulgación, no un parche
MOAD-0001 existe en más de 1.000 sitios verificados en más de 60 ecosistemas de software. Recorrido de grafos en herramientas de compilación, deduplicación de rutas en demonios de enrutamiento de red, detección de colisiones en motores de juegos, asignación de recursos en planificadores de sistemas operativos.
Cada sitio: código correcto. Cada sitio: crecimiento O(N²) esperando a que N cruce un umbral.
El pipeline de divulgación
Un parche sin divulgación upstream corrige un solo sitio. El patrón reaparece en la siguiente versión, la siguiente biblioteca, el siguiente puerto a otro lenguaje. Divulgación: nombrar el patrón, documentarlo como CWE-407 (Complejidad Algorítmica: Complejidad Algorítmica Ineficiente), publicar las heurísticas de reconocimiento y la corrección de una línea. Así, cada desarrollador que lea la divulgación podrá reconocer y corregir su propia instancia.
Hamming lo llamó «dar un pez vs. enseñar a pescar». El parche da un pez. La divulgación —MOAD-0001 nombrado, patrón documentado, corrección generalizada— enseña la heurística.
La Extensión Permacomputadora
Hamming trabajaba en soluciones puntuales: arreglar este filtro, mejorar este código. Unhamming añade la capa de divulgación: nombrar el patrón, publicar la heurística y entregarla al común.
El principio de conocimiento compuesto se aplica aquí a escala de ecosistema. Un investigador nombra un patrón. Cien desarrolladores lo reconocen en sus propias bases de código. Mil líneas de código O(N²) se convierten en O(N). La infraestructura se vuelve más rápida para todos los que construyen sobre ella.
Este es el dragón que crece: mismo fuego (trabajar en problemas importantes, conocimiento compuesto, pensamiento sistémico), vuelo diferente (divulgación abierta, propiedad común, sin necesidad de patrocinador).