English· Español· Deutsch· Nederlands· Français· 日本語· ქართული· 繁體中文· 简体中文· Português· Русский· العربية· हिन्दी· Italiano· 한국어· Polski· Svenska· Türkçe· Українська· Tiếng Việt· Bahasa Indonesia

un

invitado
1 / ?

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]

Capas de sedimento MOAD-0001: código copiado a lo largo de décadas a medida que N crece [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.

Código sedimentario: código correcto que se vuelve costoso a medida que la escala cambia a su alrededor. Da un ejemplo concreto de software que hayas usado o escrito donde algo funcionaba a pequeña escala y se convirtió en un cuello de botella a gran escala. ¿Qué cambió: el código, el tamaño de los datos o el patrón de uso?

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

DominioPatrónContenedor secuencialContenedor correcto
Recorrido de grafosif (!stack.contains(n)) en DFS/TarjanArrayListHashSet
Deduplicación de rutas/eventosTAILQ_FOREACH strcmp por solicitudlista enlazadaHashMap
Seguimiento de colisionesfindLinearSearch() por tick de físicaarrayunordered_set
Filtro de asignación de recursosList.contains() en filtro de streamArrayListHashSet
Lista abierta de A* pathfindingLocalVector::find() por vecinovectorí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. listset. ArrayListHashSet. ArraySet. vectorunordered_set. slicemap[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]

Auditas una base de código JavaScript y encuentras esto dentro de un bucle que recorre todos los vecinos de un grafo: `if (!openSet.includes(neighbor)) openSet.push(neighbor)`. ¿Es esto MOAD-0001? ¿Con qué reemplazarías `openSet` y cómo cambia la complejidad de O(?) a O(?)? [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.

Corriges una implementación de Tarjan SCC reemplazando `onStack = []` con `onStack = set()`. Las pruebas pasan. Un revisor de código pregunta: '¿y si el orden importa para onStack?' ¿Cómo respondes?

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).