Uno sviluppatore si trova ad affrontare per la prima volta un ampio codebase legacy. Deve capire cosa succede a un record cliente quando un account viene chiuso: quali programmi lo aggiornano, quali processi batch lo leggono successivamente, quali campi vengono modificati e se qualche sistema a valle dipende dallo stato finale. La prima mossa naturale è quella di effettuare una ricerca. Utilizza grep per cercare il nome del campo, analizza i risultati, apre alcuni file e inizia a leggere. Nel giro di un'ora ha trovato riferimenti in dodici programmi, tre script SQL e un flusso di processi JCL. Ha anche trovato lo stesso nome di campo in diciassette blocchi di commento, quattro stringhe di formato dei log, due fixture di test e una variabile in un sottosistema completamente diverso che per coincidenza condivide lo stesso nome. Non riesce a distinguere, solo dai risultati della ricerca, quali di queste operazioni siano effettivamente letture di dati, quali scritture, quali trasformazioni e quali semplici collisioni di nomi. Sa come si chiama il campo, ma non ha ancora capito cosa ne fa il codice.
La comprensione del codice inizia qui
SMART TS XL Crea un modello strutturale dell'intero codice sorgente, mappando le dipendenze tra ogni linguaggio e piattaforma.
Clicca quiQuesto divario tra trovare una stringa e comprendere il codice non è un divario che una ricerca migliore può colmare. È un divario tra due tipi di indagine fondamentalmente diversi: uno che chiede "dove appare questo testo?" e uno che chiede "cosa fa questo codice?". La ricerca testuale è un'ottima risposta alla prima domanda. Non è affatto una risposta alla seconda domanda, e confondere le due è una delle fonti più costanti di spreco di sforzi, dipendenze mancate e valutazioni di impatto errate nello sviluppo del software. La distinzione è più importante nei grandi sistemi aziendali eterogenei che nelle piccole codebase moderne, perché questi sistemi contengono decenni di struttura accumulata, dipendenze tra linguaggi e relazioni implicite che esistono solo nel comportamento del codice, non in qualsiasi stringa che appare nei suoi file sorgente. Come esaminato nell'analisi di metriche di qualità del codice e il loro impattoLa complessità di una codebase ha un impatto significativo sulla manutenibilità, e nessuna metrica derivata esclusivamente da modelli testuali è in grado di cogliere le relazioni strutturali che governano il comportamento effettivo del codice.
Cosa fa effettivamente la ricerca testuale
La ricerca testuale è un'operazione di corrispondenza di sottostringhe applicata a file trattati come sequenze di caratteri grezzi. La query è una stringa o un pattern. Il risultato è un elenco di posizioni in cui tale pattern compare. Lo strumento non conosce il linguaggio in cui sono scritti i file, non comprende la grammatica che conferisce struttura al testo e non ha un modello delle relazioni tra gli elementi di codice che il testo rappresenta. Una ricerca con grep su un milione di righe di codice sorgente COBOL opera sullo stesso modello di una ricerca con grep su un milione di righe di HTML: sequenze di caratteri nei file, raggruppate per percorso del file, restituite quando la sequenza di caratteri corrisponde.
Questo è estremamente utile per una specifica categoria di attività: trovare dove compare una stringa nota, confermare se un termine specifico viene utilizzato o meno, effettuare un rapido controllo di coerenza sulle convenzioni di denominazione, individuare il file che contiene uno specifico messaggio di errore. Per queste attività, la ricerca testuale è lo strumento giusto perché si tratta effettivamente di trovare stringhe. La velocità, la portabilità e la natura a configurazione zero di grep e dei suoi equivalenti sono caratteristiche che si adattano perfettamente quando la domanda è del tipo "questa stringa esiste in questi file e, se sì, dove?".
Il problema si presenta quando si utilizza la ricerca testuale per domande che non riguardano le stringhe. "Cosa chiama questa funzione?" non è una domanda su dove appare il nome della funzione. È una domanda sul grafo delle chiamate, che è una proprietà strutturale del codice che richiede analisi sintattica e semantica per essere costruita. "Dove è scritto questo campo?" non è una domanda su dove appare il nome del campo. È una domanda sul flusso di dati, che richiede la comprensione della semantica di assegnazione nel linguaggio specifico per poter rispondere. "Cosa si romperà se modifico questa interfaccia?" non è una domanda su dove appare il nome dell'interfaccia. È una domanda sulle relazioni di dipendenza, che richiede la risoluzione di importazioni, ereditarietà e accoppiamento tra moduli per poter rispondere correttamente.
Ognuna di queste domande prende spunto da un nome, il che induce a considerarle come semplici attività di ricerca. Tuttavia, il nome è solo il punto di partenza. La risposta risiede nel modello strutturale del codice, non nel testo dei file sorgente.
Il problema del rumore: troppi risultati che non significano nulla
Il primo tipo di fallimento della ricerca testuale applicata ai compiti di comprensione del codice è la sovrapproduzione: restituisce molti più risultati di quelli rilevanti, senza alcun meccanismo per identificare quali risultati siano strutturalmente significativi e quali siano casuali.
Un identificatore breve come status, id, type, o date possono comparire migliaia di volte in una codebase di grandi dimensioni. Identificatori ancora più lunghi possono entrare in conflitto tra linguaggi e namespace diversi: calculate_tax La ricerca di testo, che può includere il nome di una funzione in un modulo Python, il nome di un paragrafo COBOL, una stored procedure di database, un helper JavaScript e una stringa in una configurazione di logging, produce risultati corrispondenti. Lo sviluppatore che riceve questi risultati deve filtrarli manualmente, applicando la propria conoscenza del codice per determinare quali occorrenze siano rilevanti. Questo filtraggio manuale è di per sé un'attività di comprensione del codice, il che significa che lo sviluppatore sta svolgendo il lavoro che lo strumento avrebbe dovuto fare, senza alcun aiuto da parte dello strumento stesso.
In pratica, gli sviluppatori filtrano basandosi sull'intuizione e sull'esperienza. Riconoscono che un risultato in un file di test probabilmente non corrisponde a una chiamata in produzione. Riconoscono che un risultato all'interno di un blocco di commenti è documentazione, non una chiamata. Scartano i risultati in file che ritengono irrilevanti. Ma questi filtri sono fallibili e non verificabili. Lo sviluppatore che filtra con sicurezza potrebbe sbagliarsi. Lo sviluppatore che filtra con cautela potrebbe impiegarci ore. E in entrambi i casi, il risultato è un insieme di osservazioni che riflette il giudizio dello sviluppatore, non un'analisi strutturale verificata del codice.
Consideriamo un esempio concreto. Uno sviluppatore COBOL cerca il nome di un paragrafo prima di rimuoverlo:
cobolo
SEARCH-RESULTS FOR "CALC-INTEREST":
1. CALC-INTEREST.PGM line 5 : IDENTIFICATION DIVISION.
2. CALC-INTEREST.PGM line 42 : CALC-INTEREST.
3. FINPROCESS.CBL line 178 : PERFORM CALC-INTEREST
4. RPTMONTH.CBL line 91 : * Old routine: CALC-INTEREST replaced by CALC-INT-V2
5. CUSTBATCH.CBL line 234 : PERFORM CALC-INTEREST THRU CALC-INTEREST-EXIT
6. DATADICT.txt line 12 : CALC-INTEREST - computes monthly interest for savings accts
7. TESTHARNESS.CBL line 67 : PERFORM CALC-INTEREST
8. ARCHIVEJOB.CBL line 156 : * PERFORM CALC-INTEREST (disabled 2019-03-14)
Tra questi otto risultati, esattamente due sono chiamanti attivi che si bloccherebbero se il paragrafo venisse rimosso: le righe 3 e 5. La riga 2 è la definizione. Le righe 4 e 8 sono commenti. La riga 6 è una voce del dizionario dati. La riga 7 è un test. Determinare quali due di questi otto risultati rappresentano siti di chiamata attivi richiede la lettura di ciascun file nel contesto, la comprensione della sintassi COBOL e la valutazione di cosa significhi effettivamente "disabilitato" in un commento alla riga 8 per l'esecuzione. La ricerca testuale ha fornito il materiale grezzo. La comprensione del codice ha fornito la risposta.
Il problema del silenzio: risultati rilevanti che non vengono mai restituiti
La seconda modalità di errore è la sottoproduzione: mancano risultati strutturalmente significativi perché non sono espressi in un formato che la ricerca testuale può riconoscere.
Le chiamate indirette sono la causa più comune di risultati mancanti. Quando la funzione A chiama la funzione B e la funzione B chiama la funzione C, ormai obsoleta, una ricerca testuale del nome di C trova la funzione B come chiamante diretto, ma non trova la funzione A come chiamante indiretto. La rilevanza di A dipende dallo scopo della ricerca: se l'obiettivo è comprendere tutto ciò che attiva C, allora A è fondamentale. Se l'obiettivo è solo trovare i chiamanti diretti, allora A è irrilevante. La ricerca testuale non è in grado di fare questa distinzione perché non ha il concetto di grafo delle chiamate. Restituisce qualsiasi testo corrispondente, senza essere a conoscenza di cosa faccia parte il testo corrispondente.
I riferimenti tra linguaggi diversi sono una categoria sistematicamente mancante. Un servizio Java che chiama un programma COBOL per nome attraverso un livello middleware contiene il nome del programma COBOL come stringa letterale, che la ricerca testuale può trovare. Ma lo stesso servizio Java che costruisce il nome del programma dinamicamente, lo legge da un file di configurazione o lo invia tramite un livello di astrazione non contiene affatto il nome. Questi sono chiamanti che la ricerca testuale non può trovare indipendentemente da quanto accuratamente venga applicata. Come esaminato nel contesto di analisi statica su codice offuscato e generato dinamicamenteQuando i percorsi di esecuzione sono espressi indirettamente tramite configurazione, modelli o meccanismi di dispatch a runtime, le relazioni strutturali che rappresentano non sono recuperabili dal solo testo dei file sorgente.
Gli alias di campo e le trasformazioni creano un'altra categoria di errori silenziosi. Un campo COBOL denominato WS-ACCT-BAL che viene scritto in una colonna del database denominata ACCT_BALANCE, successivamente letto da un servizio Java come accountBalancee infine serializzato come account_balance In una risposta JSON, il nome rappresenta quattro diverse stringhe di testo per lo stesso elemento dati. Una ricerca di una qualsiasi di queste stringhe non trova le altre tre. Sapere che tutte e quattro si riferiscono allo stesso concetto aziendale sottostante richiede la comprensione della catena di trasformazione, non la ricerca di tutte le occorrenze di un singolo nome.
Cosa richiede realmente la comprensione del codice
La comprensione del codice, come capacità tecnica, è l'abilità di rispondere a domande sul codice ragionando sulla sua struttura e semantica, piuttosto che sul suo testo esplicito. Richiede la costruzione e l'interrogazione di un modello del codice che rappresenti il suo significato, non solo ciò che dice.
I requisiti tecnici minimi per la comprensione del codice al livello necessario a supportare le attività di sviluppo in grandi sistemi aziendali sono considerevoli. Ciascuno di essi rappresenta una capacità che la ricerca testuale non possiede e che nessuna combinazione di ricerca testuale e lavoro manuale può replicare in modo affidabile su larga scala.
Analisi sintattica: dal testo alla struttura
Il primo passo oltre la ricerca testuale è l'analisi sintattica: leggere il codice sorgente secondo la grammatica del suo linguaggio e produrre una rappresentazione strutturata, in genere un albero sintattico astratto, che codifica le relazioni sintattiche tra gli elementi del codice. Una rappresentazione analizzata di PERFORM CALC-INTEREST THRU CALC-INTEREST-EXIT Non è una stringa; è un oggetto strutturato che identifica questo come un'istruzione PERFORM con un target di intervallo, dove entrambi gli estremi sono nomi di paragrafo nel programma corrente, risolvibili rispetto alla struttura PROCEDURE DIVISION del programma.
L'analisi sintattica è specifica per ogni linguaggio. Un parser COBOL comprende la grammatica COBOL. Un parser Java comprende la grammatica Java. Un parser JCL comprende la sintassi JCL. In un sistema aziendale multilingue, la comprensione del codice richiede un parser per ogni linguaggio presente nell'ambiente, che produca rappresentazioni strutturali su cui è possibile ragionare in modo coerente tra i diversi linguaggi. Come discusso nell'esame dettagliato di Analisi statica di TypeScript su scala aziendaleL'analisi strutturale e semantica, che comprende come è organizzato il codice, come interagiscono i moduli e come fluiscono i dati e il controllo all'interno di un'applicazione, è il punto di partenza per andare oltre il controllo della sintassi e raggiungere una vera e propria intelligenza del codice.
Risoluzione dei simboli: dai nomi alle entità
Dopo l'analisi sintattica, i nomi nel codice sorgente devono essere risolti nelle entità a cui si riferiscono. L'identificatore CALC-INTEREST in un'istruzione PERFORM deve essere risolto nella definizione di paragrafo specifica in un programma o copybook specifico. Il nome del metodo calculateLegacyFee in una chiamata Java deve essere risolto nella definizione del metodo specifico nella classe specifica, tenendo conto dell'ereditarietà e dell'overloading. Il nome della colonna ACCT_BALANCE In una query SQL, il valore deve essere risolto nella colonna specificata all'interno della tabella specificata nello schema del database.
La risoluzione dei simboli è il processo che trasforma un nome da stringa in un riferimento a un'entità di codice specifica e identificabile, dotata di posizione, tipo e un insieme di relazioni con altre entità. Senza la risoluzione dei simboli, tutte le query di codice sarebbero query di testo. Con essa, una query per "tutti i chiamanti di questa funzione" diventa una query strutturale su un grafo risolto di relazioni di chiamata, restituendo solo i risultati che sono effettivamente chiamate alla funzione specifica, e non tutti i file in cui compare il nome della funzione.
La risoluzione dei simboli diventa drasticamente più complessa negli ambienti multilingue, dove lo stesso concetto viene denominato in modo diverso attraverso i confini linguistici. La risoluzione interlinguistica delle equivalenze di campo, esaminata nel contesto più ampio di riduzione del tempo medio di recupero tramite indicizzazione interlinguisticaÈ un prerequisito fondamentale per qualsiasi analisi strutturale che tracci il flusso di dati o di controllo attraverso un confine linguistico. Senza di esso, l'analisi si interrompe al confine e la comprensione che ne deriva è incompleta.
Analisi del flusso di controllo: comprendere i percorsi di esecuzione
L'analisi del flusso di controllo mappa i possibili percorsi di esecuzione all'interno di un programma: quali rami vengono percorsi in determinate condizioni, quali istruzioni sono raggiungibili, quali percorsi di codice sono inattivi e in quale ordine le istruzioni vengono eseguite l'una rispetto all'altra. Queste informazioni sono espresse come un grafo di flusso di controllo, in cui i nodi rappresentano blocchi base di codice sequenziale e gli archi rappresentano trasferimenti di controllo condizionali o incondizionali.
L'analisi del flusso di controllo è ciò che permette di rispondere a domande come "in quali condizioni viene eseguito questo percorso di codice?" e "questo codice è raggiungibile da qualsiasi punto di ingresso?". La ricerca testuale non può rispondere a queste domande perché riguarda i percorsi di esecuzione, non la posizione delle stringhe. Un'istruzione che appare nel codice sorgente può essere eseguita o meno, a seconda delle condizioni che controllano il ramo in cui si trova. Una funzione definita in un modulo può essere chiamata o meno, a seconda che un qualsiasi percorso di esecuzione raggiunga un punto di chiamata. Solo l'analisi del flusso di controllo può determinare queste proprietà. Come esplorato nell'esame di dare priorità ai problemi del codice statico durante la modernizzazioneComprendere quali percorsi di codice vengono effettivamente eseguiti, con quale frequenza e in quali condizioni si attivano è ciò che distingue un'analisi utile da risultati che sembrano significativi ma non riflettono la realtà operativa.
Analisi del flusso di dati: tracciamento dei valori attraverso il codice
L'analisi del flusso di dati traccia il percorso dei valori all'interno di un programma: dove viene assegnata una variabile, dove viene letto il suo valore, quali trasformazioni vengono applicate tra l'assegnazione e l'utilizzo e se il valore di una variabile dipende dal valore di un'altra. Queste informazioni rispondono a domande come "da dove proviene il valore di questo campo?" e "quale parte del codice viene influenzata se il valore di questo campo cambia?".
L'analisi del flusso di dati è il fondamento tecnico per la tracciatura dei campi, l'analisi della contaminazione e il tracciamento delle dipendenze a livello di valore. Opera sul grafo di flusso di controllo del programma, propagando le informazioni sui valori lungo i percorsi di esecuzione e registrando l'origine, il flusso e l'utilizzo dei valori. Il risultato è un grafo di flusso di dati che collega le definizioni agli utilizzi nell'intero spazio di esecuzione del programma, non solo all'interno del testo sequenziale del file sorgente.
Nei sistemi aziendali, l'analisi del flusso di dati deve superare i confini dei linguaggi per essere utile. Un valore che ha origine in un programma COBOL, fluisce attraverso una scrittura nel database e viene successivamente letto da un servizio Java ha un flusso di dati che attraversa due confini di linguaggio. Tracciare tale flusso richiede un'analisi del flusso di dati che comprenda la semantica di assegnazione COBOL, il movimento dei dati SQL e l'assegnazione delle variabili Java come parte della stessa analisi unificata, non come tre analisi separate i cui risultati devono essere collegati manualmente. Come dettagliato nell'analisi di Trasferimento di conoscenze da esperti COBOL a team di sviluppo moderniLa capacità di rendere comprensibili sistemi COBOL complessi agli sviluppatori moderni senza richiedere loro di padroneggiare il linguaggio dipende dalla disponibilità di un'analisi strutturale in grado di rappresentare il comportamento del sistema in una forma che trascenda il testo sorgente.
I compiti in cui la differenza conta di più
La distinzione tra ricerca testuale e comprensione del codice non è puramente accademica. Emerge in specifiche attività di sviluppo ad alto rischio, dove lo strumento sbagliato produce risultati che sembrano completi ma non lo sono, e dove agire sulla base di risultati incompleti ha conseguenze tangibili.
Analisi d'impatto prima di apportare modifiche
Prima di modificare la firma di una funzione, rinominare un campo o cambiare il comportamento di un'utilità condivisa, uno sviluppatore deve sapere cosa verrà influenzato. Questa è l'analisi d'impatto: consiste nell'enumerare ogni componente che dipende dall'elemento modificato, in modo che la modifica possa essere effettuata in sicurezza e tutti i componenti interessati possano essere aggiornati. L'analisi d'impatto è un'attività di comprensione del codice. Richiede la risoluzione delle relazioni di dipendenza tra i componenti, percorrendo tali relazioni dall'elemento modificato verso l'esterno e restituendo ogni componente che verrà influenzato a qualsiasi livello dell'albero delle dipendenze.
La ricerca testuale approssima l'analisi d'impatto individuando dove compare il nome dell'elemento modificato. Tuttavia, non è in grado di distinguere una dipendenza da un commento, una dipendenza diretta da una transitiva, o una dipendenza attiva da un riferimento in codice morto. Uno sviluppatore che si affida alla ricerca testuale per l'analisi d'impatto prima di una modifica significativa sta prendendo una decisione critica per la sicurezza basandosi su un'approssimazione. In una codebase piccola e monolingue, l'approssimazione potrebbe essere sufficientemente accurata. In un sistema aziendale con dipendenze tra linguaggi diversi, librerie condivise utilizzate da molti servizi e decenni di relazioni di chiamata accumulate, il divario tra ciò che restituisce la ricerca testuale e l'impatto effettivo della modifica può essere sostanziale.
Consideriamo la differenza nei risultati ottenuti con questi due approcci per una modifica dello schema di una colonna di database ampiamente utilizzata:
| Cosa deve sapere lo sviluppatore | Risultato della ricerca testuale | Risultato della comprensione del codice |
|---|---|---|
| Programmi che leggono questa rubrica | Tutti i file contenenti il nome della colonna, inclusi i commenti | Solo i programmi con istruzioni SQL SELECT che fanno riferimento a questa colonna |
| Programmi che scrivono questa rubrica | Stessa lista non filtrata | Solo i programmi con istruzioni SQL INSERT o UPDATE che scrivono in questa colonna |
| Servizi dipendenti da questa colonna | Nessuna visibilità interlinguistica | Servizi Java, Python e .NET che mappano la colonna a un campo oggetto |
| Riferimenti a codice morto | Incluso nei risultati, non contrassegnato | Esclusi o segnalati separatamente |
| Dipendenti transitivi | Non visibile | Enumerato a qualsiasi livello di profondità |
| Fiducia nella completezza | Sconosciuto | Verificabile rispetto all'ambito indicizzato |
Onboarding e navigazione del codice
Uno sviluppatore che si trova ad affrontare per la prima volta una codebase di grandi dimensioni deve costruire un modello mentale di ciò che il codice fa: come si connettono i componenti, quali dati fluiscono attraverso il sistema, quali programmi sono punti di ingresso e quali sono utility, e qual è il percorso di esecuzione per un dato processo aziendale. Questo esercizio di costruzione del modello è principalmente un compito di comprensione del codice. La ricerca testuale aiuta a individuare stringhe specifiche, ma non fornisce alcun contesto strutturale: trova dove appare una parola, ma non quale ruolo svolge il codice che la contiene nel sistema.
Gli strumenti di comprensione del codice accelerano l'onboarding rendendo navigabile la struttura del sistema. Un grafico delle chiamate interattivo mostra quali programmi chiamano quali altri. Una traccia del flusso di dati mostra da dove ha origine un campo e dove finisce. Una visualizzazione del flusso di controllo mostra quali condizioni regolano l'esecuzione di quali rami. Una mappa delle dipendenze mostra quali componenti possono essere modificati in modo indipendente e quali richiedono il coordinamento con altri team. Nessuno di questi è il risultato di una ricerca testuale. Sono il risultato dell'analisi strutturale eseguita dagli strumenti di comprensione del codice. Come esaminato nel contesto di Che cos'è l'analisi statica del codice?La capacità di gestire la complessità attraverso un'analisi strutturata, anziché tramite la lettura manuale, è ciò che consente ai team di lavorare efficacemente in sistemi troppo vasti perché un singolo individuo possa comprenderli a fondo.
Identificazione del codice morto e degli elementi inutilizzati
Il codice morto è codice definito ma mai eseguito: funzioni che non vengono mai chiamate, rami che non vengono mai raggiunti, variabili a cui viene assegnato un valore ma che non vengono mai lette. Identificare il codice morto è un'attività di comprensione del codice che richiede la costruzione di un grafo completo delle chiamate e la determinazione di quali elementi definiti non hanno archi di chiamata in entrata da alcun punto di ingresso raggiungibile. La ricerca testuale non può identificare il codice morto perché, per definizione, il codice morto non è referenziato da nessuna parte. L'assenza di un riferimento non è una stringa che la ricerca testuale può trovare.
Per la rimozione delle funzioni deprecate, l'identificazione del codice morto è direttamente rilevante. Alcuni elementi che sembrano essere chiamanti di una funzione deprecata potrebbero essere essi stessi codice morto: funzioni che sono state scritte per chiamare la funzione deprecata ma non vengono mai chiamate a loro volta e quindi non rappresentano alcuna dipendenza attiva. Distinguere i chiamanti attivi dai chiamanti morti richiede la stessa analisi del grafo delle chiamate che identifica il codice morto in generale. Come esaminato nel contesto di tecniche di refactoring essenzialiL'analisi statica dell'utilizzo fornisce informazioni sufficienti per determinare se funzioni, etichette, paragrafi o moduli vengono mai richiamati, e tale analisi è possibile solo attraverso la costruzione di un grafo di chiamate strutturale, non attraverso il conteggio delle occorrenze del testo.
Audit di sicurezza e conformità
L'audit di sicurezza e conformità richiede la tracciabilità dei dati sensibili all'interno del sistema: identificare dove sono archiviate le informazioni personali, quali percorsi di codice possono accedervi, se i controlli di accesso sono correttamente implementati in ogni percorso di esecuzione che conduce a dati sensibili e se tali dati possono fuoriuscire dal sistema tramite log, messaggi di errore o risposte API. Si tratta di attività di analisi del flusso di dati e del flusso di controllo che una semplice ricerca testuale non riesce a rappresentare in modo accurato.
Una ricerca testuale del nome di un campo sensibile trova i file che contengono quel nome. Non può determinare se tali file eseguono accessi autorizzati, non autorizzati o nessun accesso. Non può determinare se esiste un controllo di accesso nel percorso di esecuzione che porta all'accesso al campo. Non può tracciare se il valore del campo viene successivamente scritto in un log o restituito in una risposta API che non dovrebbe contenerlo. L'analisi della contaminazione, che traccia il flusso di valori sensibili attraverso il sistema e identifica dove possono raggiungere output non attendibili, è una funzionalità di analisi del flusso di dati. È ciò che offrono gli strumenti di comprensione del codice orientati alla sicurezza e che una ricerca testuale non può approssimare.
Come SMART TS XL Promuove la comprensione del codice in tutta l'azienda.
SMART TS XL Si basa sul presupposto che i sistemi aziendali richiedano una comprensione strutturale, non il recupero di testo. La sua piattaforma di Software Intelligence analizza il codice sorgente di ogni linguaggio e piattaforma presente nell'ambiente, produce alberi di sintassi astratta specifici per ciascun linguaggio e li risolve in un grafo unificato multi-linguaggio che rappresenta le relazioni strutturali dell'intero sistema. Programmi COBOL, flussi di job JCL, servizi Java, applicazioni .NET, script Python, schemi SQL, moduli TypeScript e artefatti di configurazione sono tutti rappresentati come nodi e archi in questo grafo, con le relazioni espresse come connessioni tipizzate: chiamate, flussi di dati, inclusioni nel copybook, riferimenti allo schema ed equivalenze tra linguaggi.
La funzionalità di ricerca aziendale della piattaforma fornisce il punto di accesso per le attività di comprensione del codice, ma opera in modo fondamentalmente diverso dalla ricerca testuale. I risultati sono organizzati per tipo di relazione e struttura dell'artefatto, non per occorrenza di stringhe. Una query per il nome di un campo restituisce definizioni, letture, scritture, riferimenti SQL e inclusioni nel copybook come tipi di risultato categorizzati separatamente, in modo che uno sviluppatore che chiede "cosa scrive in questo campo?" riceva esattamente le relazioni di scrittura, non un elenco misto di tutti i file in cui compare il nome. Questa organizzazione strutturale dei risultati di ricerca riflette il modello di riferimenti incrociati sottostante e fornisce agli sviluppatori le informazioni specifiche e utilizzabili di cui hanno bisogno senza richiedere loro di filtrare manualmente le occorrenze di stringhe.
Le funzionalità di analisi dell'impatto, attraversamento del grafo delle chiamate, visualizzazione del flusso di controllo e tracciamento del flusso di dati della piattaforma operano tutte sullo stesso modello strutturale unificato. Quando uno sviluppatore identifica una funzione obsoleta, il grafo delle chiamate fornisce tutti i chiamanti a ogni livello della gerarchia. Quando è prevista una modifica dello schema, l'analisi dell'impatto enumera ogni consumatore in ogni linguaggio. Quando uno sviluppatore in fase di onboarding deve comprendere un processo batch, la visualizzazione del flusso di controllo rende navigabile il percorso di esecuzione senza richiedere la lettura sequenziale di centinaia di righe di codice sorgente. Come esaminato nel contesto più ampio di esperienza di sviluppo e metriche DX per codebase legacyLa complessità del codice e l'intricatezza strutturale sono i fattori che determinano la manutenibilità, e gli strumenti che mettono in luce queste proprietà strutturali, anziché limitarsi al testo superficiale, sono ciò che rende i sistemi complessi gestibili su larga scala.
La differenza tra cosa SMART TS XL Ciò che la ricerca testuale fornisce è la differenza tra una domanda a cui si trova risposta e una domanda che ne è scaturita. La ricerca testuale avvia un'indagine. La comprensione del codice la completa.
Il costo continuo della sostituzione della ricerca con la comprensione
Le conseguenze pratiche del considerare la ricerca testuale come un sostituto della comprensione del codice si accumulano silenziosamente in ogni attività di sviluppo che richiede una conoscenza strutturale della codebase. Ogni valutazione d'impatto che si basa sulla ricerca testuale comporta un numero imprecisato di dipendenze non rilevate. Ogni tracciamento dei campi che si ferma al confine di un linguaggio lascia invisibile una parte del sistema. Ogni identificazione di codice morto che conta le occorrenze di stringhe invece di analizzare la raggiungibilità del grafo delle chiamate include falsi positivi e non rileva il codice morto effettivo. Ogni audit di sicurezza che cerca nomi di campi sensibili invece di tracciare il flusso di dati attraverso i percorsi di esecuzione fornisce una garanzia incompleta e non verificabile.
In un codebase piccolo, monolingue e frequentemente modificato, questi costi possono essere gestibili. Gli sviluppatori hanno un contesto sufficiente per filtrare accuratamente i risultati della ricerca, i confini del sistema sono compresi da tutti i membri del team e l'ispezione manuale colma le lacune lasciate dalla ricerca testuale abbastanza rapidamente da evitare errori gravi. In un grande sistema aziendale con più linguaggi, decenni di codice accumulato e strutture di team in cui nessun singolo individuo comprende il sistema nella sua interezza, i costi si moltiplicano. Dipendenze mancate emergono in produzione. Valutazioni d'impatto che infondevano fiducia in sala riunioni producono guasti inaspettati al momento del rilascio. Audit di sicurezza che coprivano ogni occorrenza di stringa non rilevano i percorsi di flusso dei dati che espongono informazioni sensibili. Le conoscenze che erano custodite nella mente degli sviluppatori che nel frattempo hanno lasciato il progetto non possono essere ricostruite tramite la ricerca testuale perché le relazioni strutturali che comprendevano non sono mai state codificate in alcuna stringa nei file sorgente.
Il passaggio dalla ricerca testuale alla comprensione del codice non rappresenta la sostituzione di uno strumento con un altro. La ricerca testuale conserva il suo ruolo per le attività per cui è più adatta: individuazione di stringhe, orientamento rapido, verifica della configurazione e navigazione tra file. La comprensione del codice fornisce l'analisi strutturale che la ricerca testuale non può offrire: grafi delle chiamate, tracce del flusso di dati, analisi dell'impatto, identificazione del codice morto e risoluzione delle dipendenze tra linguaggi diversi. I due strumenti operano a diversi livelli di astrazione, rispondono a diverse categorie di domande e servono a scopi diversi. Il costo di confonderli si traduce in dipendenze non rilevate, valutazioni errate e nel costante accumulo di rischi derivanti dall'apportare modifiche significative a sistemi complessi con un modello incompleto del loro effettivo funzionamento.