Il Naming Non È la Ricerca
Ora conosci sette schemi MOAD. Conoscere i nomi è importante: ti permette di riconoscere uno schema quando lo vedi. Ma il riconoscimento in una lezione controllata differisce dal rilevamento in una base di codice che non hai mai aperto.
Una base di codice non etichetta i suoi difetti. Un MOAD sedimentario non viene fornito con un commento che dica // O(N²) — fix this. Un thundering herd non si annuncia come una cache miss stampede. Li trovi leggendo il codice con una domanda specifica in mente: quale struttura di dati contiene questi valori e quali operazioni vengono eseguite su di essa all'interno di un ciclo?
La rilevazione è un'abilità separata dal riconoscimento. Il riconoscimento dice: sì, quello schema è MOAD-0001. La rilevazione dice: permettimi di trovare tutti i posti in questa base di codice dove lo schema potrebbe esistere, sia che riesca a vedere il codice completo o solo un nome di simbolo.
Prima Scansione
Un primo passaggio usa grep. Ogni MOAD ha un substrato: una struttura di dati o un'API la cui presenza, vicino a determinate operazioni, è un segnale degno di indagine.
MOAD-0001 (Sedimentario): List.contains in un ciclo
# Segnale: test di appartenenza su una variabile di lista all'interno di un ciclo
grep -rn '.contains(' src/ | grep -v HashSet | grep -v TreeSet
grep -rn 'visited =' src/ | grep -v set | grep -v Set
MOAD-0002 (Intertangle): flag mutabile condiviso tra fasi
# Segnale: campo statico mutabile scritto da un sottosistema, letto da un altro
grep -rn 'static ' src/ | grep -v final | grep -v class | grep -v void
MOAD-0003 (Contesto Fugato): ThreadLocal in un executor pooled
# Segnale: ThreadLocal.set() senza ThreadLocal.remove() garantito
grep -rn 'ThreadLocal' src/
grep -rn 'ThreadLocal.set' src/ -l
MOAD-0004 (Segreto Registrato): intestazioni HTTP nell'output del log
# Segnale: chiamata di log con variabile headers vicino agli endpoint di autenticazione
grep -rn 'log.*header' src/
grep -rn 'Authorization' src/ --include='*.log'
MOAD-0005 (Mandria Tuonante): cache miss senza sincronizzazione
# Segnale: cache.get() + null check + cache.put() senza lock
grep -rn 'cache.get' src/ -A4 | grep 'cache.put'
Questi schemi producono candidati, non difetti confermati. Ogni candidato necessita di triage: leggi il codice circostante, verifica il tipo di struttura dati, conferma che l'operazione viene eseguita su larga scala.
Leggere il Codice per la Complessità
Grep trova candidati. La lettura li conferma. Quando apri un file candidato, leggi con una domanda: il costo di questa operazione cresce con la dimensione dell'input?
Per MOAD-0001, il protocollo di conferma:
1. Trova il ciclo esterno. Cosa limita il suo conteggio di iterazioni?
2. Trova l'operazione interna (.contains, .indexOf, 'in'). Su quale struttura di dati viene eseguita?
3. Quella struttura di dati cresce con lo stesso input che guida il ciclo esterno?
4. Se sì: il costo è O(N²) dove N = dimensione dell'input. Difetto confermato.
5. Se no: la struttura interna è limitata (config, enum, piccola costante). Falso positivo.
Un attraversamento di grafo che visita N nodi, controllando una lista visited ad ogni passo: sia il ciclo che la struttura di dati interna crescono con N. Confermato.
Un gestore di richieste che controlla un allowlist di 5 IP di amministrazione: l'allowlist non cresce mai con il volume di richieste. Falso positivo.
Lo stesso protocollo si applica a ogni MOAD: identifica il driver esterno, identifica la struttura interna, chiediti se entrambi scalano insieme.
Surge Score: Dare Priorità ai Tuoi Risultati
Non tutti i difetti confermati garantiscono una correzione immediata. Un MOAD in una libreria con 10.000 dipendenti downstream ha un surge score più alto rispetto allo stesso MOAD in uno strumento interno privato.
Surge score = speedup × in-degree. Speedup: quanto più veloce viene eseguita la correzione su scala di produzione tipica? In-degree: quanti pacchetti o servizi downstream erediterebbero la correzione automaticamente quando l'upstream la unisce?
Un MOAD-0001 confermato nel risolutore di dipendenze di Apache Maven, in esecuzione su grafici di 50.000 nodi, con 1.000+ plugin Maven downstream che ereditano i cambiamenti automaticamente: il surge score è molto alto. Questa correzione appartiene alla testa della tua coda.
Un MOAD-0001 confermato in uno strumento CLI per utente singolo senza dipendenti: surge score vicino a zero. Vale la pena correggerlo, ma non è urgente.
Nodi workaholici vs. ghiottoni. Un nodo con alta betweenness e alto speedup è un workaholic: gestisce il flusso critico e scaricherà le code downstream quando sbloccato. Correggilo solo dopo aver confermato la capacità downstream. Un nodo con alto out-degree e basso speedup è un ghiottone: consuma tutto ciò che gli viene somministrato e non sente dolore. Correggere un workaholic senza preparare la capacità downstream crea MOAD-0005 (mandria tuonante) su scala infrastrutturale.
Da Scansione a Merge: Una Pipeline MOAD
Un difetto confermato con un alto surge score passa attraverso una pipeline. Ogni fase produce un artefatto. Nessuna fase è facoltativa.
scan → lista di candidati (output di grep, risultati di analisi statica)
ticket → descrizione del difetto (numero MOAD, posizione, analisi di complessità)
patch → cambio di codice (swap di strutture di dati, adozione primitiva)
test → test unitario (prova O(1): cronometra la correzione a N=100 e N=10.000)
UNDF → post di divulgazione pubblica (undefect.com, dominio pubblico)
disclose → riferimento CVE o CWE se rilevante per la sicurezza
PR → pull request upstream con patch + test + link UNDF
merge → accettazione del maintainer; la correzione si propaga via version bump
Ogni artefatto alimenta la fase successiva. Una patch senza un test non può essere verificata. Un test senza una divulgazione non può propagarsi ad altre istanze dello stesso schema. Una divulgazione senza una PR upstream trattiene la correzione in un fork.
Un post MOAD (UNDF) è la fase che la maggior parte degli ingegneri omette. Correggono il difetto, inviano una PR, e si considerano soddisfatti. Ma una correzione senza un post denominato significa che ogni futuro ingegnere che incontra lo stesso schema deve riscoprire sia il problema che la correzione in modo indipendente. Un post MOAD chiude il ciclo della conoscenza: nomina lo schema, mostra il metodo di rilevamento e si collega alla patch. I futuri ricercatori trovano la correzione cercando il nome dello schema.
Planet patching su larga scala. Una singola correzione MOAD-0001 in una libreria ampiamente utilizzata si propaga a ogni progetto che la importa. Un post MOAD garantisce che gli ingegneri nei progetti che non aggiorneranno mai quella libreria imparino comunque la correzione. Entrambi i percorsi vengono eseguiti in parallelo.
Scrivere un Ticket di Difetto
Un buon ticket di difetto risponde a cinque domande:
1. Dove: file esatto, classe, funzione e intervallo di righe
2. Cosa: il tipo di struttura di dati e l'operazione su di essa
3. Perché: l'analisi di complessità (O(N²) o peggio, con N definito)
4. Impatto: quali input attivano il comportamento del caso peggiore e su quale scala
5. Correzione: la struttura di dati o primitiva da sostituire
Un ticket che risponde a tutte e cinque è autonomo: un maintainer che non ha mai letto la tua analisi può riprodurre il tuo risultato e verificare la tua correzione. I ticket che saltano (3) o (4) richiedono al maintainer di ripetere l'analisi di complessità prima che possano fare il merge. Quell'attrito riduce la probabilità di merge.
La credibilità si accumula. Una prima PR che include un ticket chiaro, una patch ben mirata e un test di benchmark viene unita. Una seconda PR dello stesso autore viene revisionata con meno attrito. Una terza PR viene revisionata dal maintainer che ha unito i primi due. La reputazione in open source è un registro di artefatti: ogni patch accettata guadagna fiducia per la prossima.
Leggere un Candidato Reale
Ecco un vero candidato MOAD-0001 in Python. Leggilo e completa il protocollo di 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
Domande di triage:
1. Quale struttura di dati è `seen`?
2. Quale operazione viene eseguita su di essa alla riga 6?
3. `seen` cresce con la dimensione dell'input?
4. Il ciclo che guida le chiamate ricorsive cresce anche con la dimensione dell'input?
5. Questo è un MOAD-0001 confermato o un falso positivo?
La Tua Patch
Un difetto confermato con un alto surge score ha bisogno di una patch completa: la correzione del codice, un test che provi il miglioramento e uno schema di post MOAD.
Il test deve essere un test di prestazione, non un test di correttezza. Un test di correttezza passa prima e dopo la correzione — questo è il punto; l'output non cambia. Un test di prestazione a due dimensioni di input prova il miglioramento:
import time
def build_graph(n):
# n pacchetti, ognuno dipendente dal precedente
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')
Prima della correzione, il tempo trascorso cresce quadraticamente con n. Dopo la correzione, cresce linearmente. Stampa entrambi e includi i numeri nella descrizione della tua PR.
Uno schema di post MOAD copre: il nome dello schema, il substrato (risolutore di dipendenze Python), il metodo di rilevamento (grep per in seen dove seen inizia come []), la correzione e un link alla tua PR. Il post va a undefect.com come dominio pubblico. I futuri ingegneri che cercano 'l'appartenenza alla lista Python nel ciclo è lento' lo troveranno.