Wyszukiwanie tekstu a rozumienie kodu

Dlaczego wyszukiwanie tekstowe nie jest tym samym, co rozumienie kodu

Programista otwiera po raz pierwszy dużą bazę kodu legacy. Musi zrozumieć, co dzieje się z rekordem klienta po zamknięciu konta: które programy go aktualizują, które zadania wsadowe odczytują go później, które pola są modyfikowane w trakcie i czy jakikolwiek system niższego rzędu zależy od stanu końcowego. Naturalnym pierwszym krokiem jest wyszukiwanie. Przeszukuje grepem nazwę pola, skanuje wyniki, otwiera kilka plików i zaczyna odczytywać. W ciągu godziny znajduje odniesienia w dwunastu programach, trzech skryptach SQL i strumieniu zadań JCL. Znaleźli również tę samą nazwę pola w siedemnastu blokach komentarzy, czterech ciągach formatujących dziennika, dwóch elementach testowych i zmiennej w zupełnie niezwiązanym podsystemie, który akurat ma tę samą nazwę. Na podstawie samych wyników wyszukiwania nie są w stanie stwierdzić, które z tych operacji to faktyczne odczyty danych, które zapisy, które transformacje, a które przypadkowe kolizje nazw. Wiedzą, jak nazywa się to pole. Nie rozumieją jeszcze, co kod z nim robi.

Zrozumienie kodu zaczyna się tutaj

SMART TS XL buduje strukturalny model całej bazy kodu, mapując zależności w każdym języku i na każdej platformie.

Kliknij tutaj

Ta luka między znalezieniem ciągu znaków a zrozumieniem kodu nie jest luką, którą można zamknąć dzięki lepszemu wyszukiwaniu. Jest to luka między dwoma zasadniczo różnymi rodzajami zapytań: jednym, które pyta „gdzie pojawia się ten tekst?” i drugim, które pyta „co ten kod robi?”. Wyszukiwanie tekstowe jest doskonałą odpowiedzią na pierwsze pytanie. Nie jest to jednak odpowiedź na drugie pytanie w ogóle, a łączenie tych dwóch jest jednym z najczęstszych źródeł marnowania wysiłku, pominiętych zależności i błędnych ocen wpływu w rozwoju oprogramowania. To rozróżnienie ma większe znaczenie w dużych, heterogenicznych systemach korporacyjnych niż w małych, współczesnych bazach kodu, ponieważ systemy te zawierają dekady nagromadzonej struktury, zależności międzyjęzykowych i niejawnych relacji, które istnieją tylko w zachowaniu kodu, a nie w żadnym ciągu znaków pojawiającym się w jego plikach źródłowych. Jak zbadano w analizie metryk jakości kodu i ich wpływu , złożoność bazy kodu znacząco wpływa na łatwość utrzymania, a żadna metryka wyprowadzona wyłącznie z wzorców tekstowych nie odzwierciedla relacji strukturalnych, które rządzą rzeczywistym zachowaniem kodu.

Co właściwie robi wyszukiwanie tekstowe

Wyszukiwanie tekstowe to operacja dopasowywania podciągów stosowana do plików traktowanych jako surowe sekwencje znaków. Zapytanie jest ciągiem znaków lub wzorcem. Wynikiem jest lista lokalizacji, w których występuje ten wzorzec. Narzędzie nie zna języka, w którym zapisane są pliki, nie rozumie gramatyki, która nadaje tekstowi strukturę, ani modelu relacji między elementami kodu reprezentowanymi przez tekst. Przeszukanie miliona wierszy kodu źródłowego w języku COBOL działa w oparciu o ten sam model, co przeszukanie miliona wierszy kodu HTML: sekwencje znaków w plikach, pogrupowane według ścieżki do pliku, zwracane po dopasowaniu sekwencji znaków.

Jest to niezwykle przydatne w przypadku określonych kategorii zadań: znajdowania miejsca występowania znanego ciągu znaków, potwierdzania obecności lub braku określonego terminu, szybkiego sprawdzania poprawności konwencji nazewnictwa, lokalizowania pliku zawierającego określony komunikat o błędzie. W przypadku tych zadań wyszukiwanie tekstowe jest właściwym narzędziem, ponieważ w rzeczywistości polegają one na wyszukiwaniu ciągów znaków. Szybkość, przenośność i brak konieczności konfiguracji grep i jego odpowiedników to cechy, które idealnie sprawdzają się w przypadku pytania „czy ten ciąg znaków występuje w tych plikach, a jeśli tak, to gdzie?”.

Problem pojawia się, gdy wyszukiwanie tekstowe jest używane w przypadku pytań, które nie dotyczą ciągów znaków. Pytanie „Co wywołuje tę funkcję?” nie dotyczy miejsca, w którym pojawia się nazwa funkcji. Jest to pytanie o graf wywołań, który jest strukturalną właściwością kodu, wymagającą analizy składniowej i semantycznej do skonstruowania. Pytanie „Gdzie jest zapisane to pole?” nie dotyczy miejsca, w którym pojawia się nazwa pola. Jest to pytanie o przepływ danych, na które odpowiedź wymaga zrozumienia semantyki przypisania w danym języku. Pytanie „Co się zepsuje, jeśli zmienię ten interfejs?” nie dotyczy miejsca, w którym pojawia się nazwa interfejsu. Jest to pytanie o relacje zależności, na które odpowiedź wymaga rozwiązania importów, dziedziczenia i sprzężenia międzymodułowego.

Każde z tych pytań ma nazwę jako punkt wyjścia, co skłania do traktowania ich jako zadań wyszukiwania. Nazwa jest jednak tylko punktem wejścia. Odpowiedź tkwi w strukturalnym modelu kodu, a nie w tekście plików źródłowych.

Problem szumu: zbyt wiele wyników, które nic nie znaczą

Pierwszym typem awarii wyszukiwania tekstowego stosowanego do zadań związanych ze zrozumieniem kodu jest nadprodukcja: zwracanie znacznie większej liczby wyników niż jest to istotne, przy braku mechanizmu pozwalającego określić, które wyniki mają znaczenie strukturalne, a które są przypadkowe.

Krótki identyfikator, taki jak status, id, typelub date mogą pojawiać się tysiące razy w dużej bazie kodu. Nawet dłuższe identyfikatory kolidują ze sobą w różnych językach i przestrzeniach nazw: calculate_tax Jako nazwa funkcji w module Pythona, nazwa akapitu w języku COBOL, procedura składowana w bazie danych, pomocnik JavaScript i ciąg znaków w konfiguracji logowania – wszystkie te elementy generują pasujące wyniki wyszukiwania tekstowego. Programista, który otrzymuje te wyniki, musi je ręcznie przefiltrować, stosując własną interpretację kodu, aby określić, które wystąpienia są istotne. To ręczne filtrowanie samo w sobie jest zadaniem zrozumienia kodu, co oznacza, że ​​programista wykonuje pracę, którą narzędzie powinno wykonać, bez pomocy ze strony narzędzia.

W praktyce programiści filtrują, kierując się intuicją i doświadczeniem. Uznają, że wynik w pliku testowym prawdopodobnie nie jest wywołaniem produkcyjnym. Uznają, że wynik w bloku komentarza to dokumentacja, a nie wywołanie. Odrzucają wyniki w plikach, które uważają za nieistotne. Jednak te filtry są zawodne i nieweryfikowalne. Programista, który filtruje pewnie, może się mylić. Programista, który filtruje ostrożnie, może spędzić na tym wiele godzin. W obu przypadkach wynikiem jest zestaw ustaleń odzwierciedlający osąd programisty, a nie zweryfikowana analiza strukturalna kodu.

Rozważmy konkretny przykład. Programista COBOL wyszukuje nazwę akapitu przed jej usunięciem:

kobol

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)

Spośród tych ośmiu wyników dokładnie dwa to aktywne wywołania, które zostałyby przerwane po usunięciu akapitu: wiersze 3 i 5. Wiersz 2 to definicja. Wiersze 4 i 8 to komentarze. Wiersz 6 to wpis w słowniku danych. Wiersz 7 to schemat testowy. Określenie, które dwa z tych ośmiu wyników reprezentują aktywne miejsca wywołań, wymaga przeczytania każdego pliku w kontekście, zrozumienia składni języka COBOL i oceny, co tak naprawdę oznacza „disabled” w komentarzu w wierszu 8 dla wykonania. Wyszukiwanie tekstowe dostarczyło surowego materiału. Zrozumienie kodu dostarczyło odpowiedzi.

Problem ciszy: trafne wyniki, które nigdy nie są zwracane

Drugim trybem awarii jest niedoprodukcja: brak wyników, które mają istotne znaczenie strukturalne, ponieważ nie są wyrażone w formie, którą można odnaleźć w wyszukiwaniu tekstowym.

Wywołania pośrednie są najczęstszym źródłem brakujących wyników. Gdy funkcja A wywołuje funkcję B, a funkcja B wywołuje wycofaną funkcję C, wyszukiwanie tekstowe nazwy funkcji C znajduje funkcję B jako bezpośrednią funkcję wywołującą, ale nie znajduje funkcji A jako pośredniej funkcji wywołującej. To, czy A jest trafnym wynikiem, zależy od celu wyszukiwania: jeśli celem jest zrozumienie wszystkiego, co wyzwala funkcję C, to A jest kluczowe. Jeśli celem jest jedynie znalezienie bezpośrednich funkcji wywołujących, to A jest nieistotne. Wyszukiwanie tekstowe nie jest w stanie dokonać tego rozróżnienia, ponieważ nie zna koncepcji grafu wywołań. Zwraca dowolny pasujący tekst, nie wiedząc, czego częścią jest pasujący tekst.

Odwołania międzyjęzykowe to systematycznie brakująca kategoria. Usługa Java, która wywołuje program COBOL po nazwie za pośrednictwem warstwy pośredniczącej, zawiera nazwę programu COBOL jako literał ciągu, który może zostać odnaleziony przez wyszukiwanie tekstowe. Jednak ta sama usługa Java, która dynamicznie konstruuje nazwę programu, odczytuje ją z pliku konfiguracyjnego lub wysyła za pośrednictwem warstwy abstrakcji, w ogóle nie zawiera tej nazwy. Są to obiekty wywołujące, których wyszukiwanie tekstowe nie może znaleźć, niezależnie od tego, jak dokładnie jest stosowane. Jak zbadano w kontekście analizy statycznej zaciemnionego i dynamicznie generowanego kodu , gdy ścieżki wykonywania są wyrażane pośrednio za pomocą konfiguracji, szablonów lub mechanizmów wysyłania w czasie wykonywania, strukturalnych relacji, które reprezentują, nie da się odzyskać z samego tekstu plików źródłowych.

Aliasy pól i transformacje tworzą kolejną kategorię cichych chybień. Pole COBOL o nazwie WS-ACCT-BAL który jest zapisywany do kolumny bazy danych o nazwie ACCT_BALANCE, następnie odczytywane przez usługę Java jako accountBalancei ostatecznie zserializowane jako account_balance w odpowiedzi JSON reprezentują cztery różne ciągi tekstowe dla tego samego elementu danych. Przeszukanie dowolnego z tych ciągów pomija pozostałe trzy. Wiedza o tym, że wszystkie cztery odnoszą się do tej samej podstawowej koncepcji biznesowej, wymaga zrozumienia łańcucha transformacji, a nie znalezienia wszystkich wystąpień pojedynczej nazwy.

Czego tak naprawdę wymaga zrozumienie kodu

Rozumienie kodu, jako umiejętność techniczna, to umiejętność odpowiadania na pytania dotyczące kodu poprzez wyciąganie wniosków z jego struktury i semantyki, a nie z tekstu powierzchownego. Wymaga to zbudowania i przeszukiwania modelu kodu, który odzwierciedla jego znaczenie, a nie tylko to, co mówi.

Minimalne wymagania techniczne dotyczące zrozumienia kodu na poziomie niezbędnym do obsługi zadań programistycznych w dużych systemach korporacyjnych są znaczące. Każde z nich reprezentuje funkcję, której nie oferuje wyszukiwanie tekstowe i której żadna kombinacja wyszukiwania tekstowego i ręcznego wysiłku nie jest w stanie wiarygodnie odtworzyć na dużą skalę.

Analiza składniowa: od tekstu do struktury

Pierwszym krokiem poza wyszukiwaniem tekstu jest analiza składniowa: odczyt kodu źródłowego zgodnie z gramatyką jego języka i wygenerowanie ustrukturyzowanej reprezentacji, zazwyczaj abstrakcyjnego drzewa składniowego, które koduje relacje składniowe między elementami kodu. Przetworzona reprezentacja PERFORM CALC-INTEREST THRU CALC-INTEREST-EXIT nie jest ciągiem znaków; jest to obiekt strukturalny identyfikujący to jako instrukcję PERFORM z zakresem docelowym, gdzie oba punkty końcowe są nazwami akapitów w bieżącym programie, które można rozwiązać za pomocą struktury PROCEDURE DIVISION programu.

Analiza składniowa jest specyficzna dla danego języka. Parser języka COBOL rozumie gramatykę języka COBOL. Parser języka Java rozumie gramatykę języka Java. Parser języka JCL rozumie składnię języka JCL. W wielojęzycznym systemie korporacyjnym, zrozumienie kodu wymaga parsera dla każdego języka obecnego w środowisku, generującego reprezentacje strukturalne, które można wnioskować w spójny sposób w różnych językach. Jak omówiono w szczegółowym omówieniu analizy statycznej języka TypeScript w skali przedsiębiorstwa , analiza strukturalna i semantyczna, która rozumie organizację kodu, interakcje modułów oraz przepływ sterowania i danych przez aplikację, stanowi punkt wyjścia do wyjścia poza sprawdzanie składni i do prawdziwej inteligencji kodu.

Rozdzielczość symboli: od nazw do jednostek

Po analizie składniowej nazwy w kodzie źródłowym muszą zostać rozwiązane na jednostki, do których się odnoszą. Identyfikator CALC-INTEREST w poleceniu PERFORM musi zostać rozwiązana do definicji konkretnego akapitu w konkretnym programie lub kopii. Nazwa metody calculateLegacyFee w wywołaniu Java musi zostać rozwiązana do konkretnej definicji metody w konkretnej klasie, uwzględniając dziedziczenie i przeciążanie. Nazwa kolumny ACCT_BALANCE w zapytaniu SQL musi zostać rozwiązane do konkretnej kolumny w konkretnej tabeli w schemacie bazy danych.

Rozpoznawanie symboli to proces przekształcania nazwy z ciągu znaków w referencję do konkretnego, identyfikowalnego encji kodu z lokalizacją, typem i zestawem relacji z innymi encjami. Bez rozpoznawania symboli wszystkie zapytania o kod są zapytaniami tekstowymi. Z tym mechanizmem zapytanie o „wszystkie wywołujące tę funkcję” jest zapytaniem strukturalnym skierowanym do rozwiązanego grafu relacji wywołań, zwracającym tylko wyniki będące faktycznymi wywołaniami konkretnej funkcji, a nie wszystkie pliki, w których występuje nazwa funkcji.

Rozpoznawanie symboli staje się znacznie bardziej złożone w środowiskach wielojęzycznych, gdzie to samo pojęcie jest nazywane inaczej w różnych językach. Międzyjęzykowe rozwiązywanie ekwiwalencji pól, analizowane w szerszym kontekście skrócenia średniego czasu odzyskiwania poprzez indeksowanie międzyjęzykowe , jest warunkiem wstępnym każdej analizy strukturalnej, która śledzi dane lub przepływ sterowania przez granicę językową. Bez niego analiza kończy się na granicy, a uzyskane w ten sposób zrozumienie jest niepełne.

Analiza przepływu sterowania: zrozumienie ścieżek wykonania

Analiza przepływu sterowania mapuje możliwe ścieżki wykonania w programie: które gałęzie są wybierane w jakich warunkach, które instrukcje są osiągalne, które ścieżki kodu są martwe oraz w jakiej kolejności instrukcje są wykonywane względem siebie. Informacje te są wyrażane w postaci grafu przepływu sterowania, gdzie węzły reprezentują podstawowe bloki sekwencyjnego kodu, a krawędzie reprezentują warunkowe lub bezwarunkowe przekazanie sterowania.

Analiza przepływu sterowania umożliwia odpowiedź na pytania takie jak „w jakich warunkach ta ścieżka kodu jest wykonywana?” i „czy ten kod jest osiągalny z dowolnego punktu wejścia?”. Wyszukiwanie tekstowe nie może odpowiedzieć na te pytania, ponieważ dotyczą one ścieżek wykonywania, a nie tego, gdzie pojawiają się ciągi znaków. Instrukcja pojawiająca się w kodzie źródłowym może zostać wykonana lub nie, w zależności od warunków, które ograniczają gałąź, w której się znajduje. Funkcja zdefiniowana w module może zostać wywołana lub nie, w zależności od tego, czy jakakolwiek ścieżka wykonywania osiągnie miejsce wywołania. Tylko analiza przepływu sterowania może określić te właściwości. Jak zbadano w badaniu priorytetyzacji problemów statycznego kodu podczas modernizacji , zrozumienie, które ścieżki kodu są faktycznie wykonywane, jak często są uruchamiane i w jakich warunkach są aktywowane, oddziela analizę wykonalną od ustaleń, które wyglądają na istotne, ale nie odzwierciedlają rzeczywistości operacyjnej.

Analiza przepływu danych: śledzenie wartości za pomocą kodu

Analiza przepływu danych śledzi, jak wartości przemieszczają się w programie: gdzie zmienna jest przypisywana, gdzie odczytywana jest jej wartość, jakie transformacje są do niej stosowane między przypisaniem a użyciem oraz czy wartość jednej zmiennej zależy od wartości innej. Informacje te odpowiadają na pytania takie jak „skąd pochodzi wartość tego pola?” i „na jaki kod wpływa zmiana wartości tego pola?”.

Analiza przepływu danych stanowi techniczną podstawę śledzenia pól, analizy skażeń i śledzenia zależności na poziomie wartości. Działa ona na grafie przepływu sterowania programu, propagując informacje o wartościach wzdłuż ścieżek wykonania i rejestrując, skąd wartości pochodzą, gdzie przepływają i gdzie są wykorzystywane. Rezultatem jest graf przepływu danych, który łączy definicje z zastosowaniami w całej przestrzeni wykonawczej programu, a nie tylko w sekwencyjnym tekście pliku źródłowego.

W systemach korporacyjnych analiza przepływu danych musi przekraczać granice językowe, aby była użyteczna. Wartość pochodząca z programu COBOL, przepływająca przez zapis w bazie danych, a następnie odczytywana przez usługę Java, charakteryzuje się przepływem danych przekraczającym granice dwóch języków. Śledzenie tego przepływu wymaga analizy przepływu danych, która rozumie semantykę przypisań COBOL, ruch danych SQL i przypisywanie zmiennych Java jako część tej samej, ujednoliconej analizy, a nie jako trzy oddzielne analizy, których wyniki muszą być ręcznie połączone. Jak szczegółowo opisano w analizie transferu wiedzy z małych i średnich przedsiębiorstw COBOL do nowoczesnych zespołów programistycznych , możliwość uczynienia złożonych systemów COBOL zrozumiałymi dla współczesnych programistów bez konieczności opanowania języka zależy od analizy strukturalnej, która potrafi przedstawić zachowanie systemu w formie wykraczającej poza tekst źródłowy.

Zadania, w których różnica ma największe znaczenie

Rozróżnienie między wyszukiwaniem tekstu a rozumieniem kodu nie jest kwestią akademicką. Pojawia się ono w konkretnych, ryzykownych zadaniach programistycznych, w których niewłaściwe narzędzie generuje wyniki, które wyglądają na kompletne, ale takie nie są, a działanie na podstawie niekompletnych wyników ma wymierne konsekwencje.

Analiza wpływu przed wprowadzeniem zmiany

Przed modyfikacją sygnatury funkcji, zmianą nazwy pola lub zmianą działania współdzielonego narzędzia, programista musi wiedzieć, na co ta zmiana wpłynie. To jest analiza wpływu: enumeracja każdego komponentu zależnego od zmienianego elementu, aby zmiana mogła zostać bezpiecznie wprowadzona, a wszystkie komponenty, których to dotyczy, mogły zostać zaktualizowane. Analiza wpływu to zadanie polegające na rozumieniu kodu. Wymaga ona rozwiązania relacji zależności między komponentami, przejścia przez te relacje od zmienionego elementu na zewnątrz i zwrócenia każdego komponentu, który zostanie zmieniony na dowolnym poziomie drzewa zależności.

Wyszukiwanie tekstowe przybliża analizę wpływu, znajdując miejsce, w którym pojawia się nazwa zmienionego elementu. Nie potrafi jednak odróżnić zależności od komentarza, zależności bezpośredniej od przechodniej ani zależności aktywnej od referencji w martwym kodzie. Programista, który opiera analizę wpływu na wyszukiwaniu tekstowym przed wprowadzeniem znaczącej zmiany, podejmuje decyzję krytyczną dla bezpieczeństwa w oparciu o przybliżenie. W małej, jednojęzycznej bazie kodu przybliżenie może być wystarczająco dokładne. W systemie korporacyjnym z zależnościami międzyjęzykowymi, bibliotekami współdzielonymi wykorzystywanymi przez wiele usług i skumulowanymi przez dekady relacjami wywołań, różnica między wynikami wyszukiwania tekstowego a rzeczywistym wpływem zmiany może być znacząca.

Rozważ różnicę w tym, co te dwa podejścia zwracają w przypadku zmiany schematu powszechnie używanej kolumny bazy danych:

Co musi wiedzieć deweloperWynik wyszukiwania tekstuWynik zrozumienia kodu
Programy czytające tę kolumnęWszystkie pliki zawierające nazwę kolumny, łącznie z komentarzamiTylko programy z instrukcjami SQL SELECT odwołującymi się do tej kolumny
Programy, które piszą tę kolumnęTa sama niefiltrowana listaTylko programy z instrukcjami SQL INSERT lub UPDATE zapisującymi do tej kolumny
Usługi zależne od tej kolumnyBrak widoczności międzyjęzykowejUsługi Java, Python i .NET mapujące kolumnę na pole obiektu
Odniesienia do martwego koduUwzględniono w wynikach, nieoznaczonoWykluczone lub oznaczone osobno
Zależności przechodnieNiewidoczneWyliczone do dowolnej głębokości
Pewność kompletnościAutor nieznanyMożliwość weryfikacji względem zakresu indeksowanego

Wdrażanie i nawigacja po kodzie

Programista, który dopiero zaczyna pracę z dużą bazą kodu, musi zbudować mentalny model tego, co kod robi: jak łączą się komponenty, jakie dane przepływają przez system, które programy są punktami wejścia, a które narzędziami, oraz jak wygląda ścieżka wykonania dla danego procesu biznesowego. To ćwiczenie z budowania modelu polega głównie na zrozumieniu kodu. Wyszukiwanie tekstowe pomaga w lokalizowaniu konkretnych ciągów znaków, ale nie dostarcza kontekstu strukturalnego: znajduje miejsce wystąpienia słowa, ale nie określa roli, jaką kod je zawierający odgrywa w systemie.

Narzędzia do rozumienia kodu przyspieszają onboarding, umożliwiając nawigację po strukturze systemu. Interaktywny graf wywołań pokazuje, które programy wywołują które inne. Ślad przepływu danych pokazuje, skąd pochodzi pole i gdzie kończy działanie. Wizualizacja przepływu sterowania pokazuje, jakie warunki decydują o tym, które gałęzie są wykonywane. Mapa zależności pokazuje, które komponenty można bezpiecznie modyfikować niezależnie, a które wymagają koordynacji z innymi zespołami. Żaden z nich nie jest produktem wyszukiwania tekstu. Są one produktem analizy strukturalnej przeprowadzanej przez narzędzia do rozumienia kodu. W kontekście statycznej analizy kodu , zdolność do poruszania się po złożoności poprzez analizę strukturalną, a nie poprzez ręczne czytanie, pozwala zespołom efektywnie pracować w systemach zbyt dużych, aby ktokolwiek mógł je ogarnąć.

Identyfikacja martwego kodu i nieużywanych elementów

Martwy kod to kod, który jest zdefiniowany, ale nigdy nie jest wykonywany: funkcje, które nigdy nie są wywoływane, gałęzie, do których nigdy nie docierają, zmienne, do których są przypisywane, ale nigdy nie są odczytywane. Identyfikacja martwego kodu to zadanie z zakresu rozumienia kodu, które wymaga zbudowania kompletnego grafu wywołań i określenia, które zdefiniowane elementy nie mają krawędzi wywołań przychodzących z żadnego osiągalnego punktu wejścia. Wyszukiwanie tekstowe nie jest w stanie zidentyfikować martwego kodu, ponieważ z definicji jest on odwoływany znikąd. Brak odwołania nie jest ciągiem znaków, który wyszukiwanie tekstowe może znaleźć.

W przypadku usuwania przestarzałych funkcji, identyfikacja martwego kodu jest bezpośrednio istotna. Niektóre elementy, które wydają się być elementami wywołującymi przestarzałą funkcję, same mogą być martwym kodem: funkcjami, które zostały napisane w celu wywołania przestarzałej funkcji, ale same nigdy nie są wywoływane, a zatem nie stanowią żywej zależności. Odróżnienie żywych elementów wywołujących od martwych wymaga tej samej analizy grafu wywołań, która zazwyczaj identyfikuje martwy kod. Jak zbadano w kontekście podstawowych technik refaktoryzacji , statyczna analiza użycia dostarcza wystarczających informacji, aby określić, czy funkcje, etykiety, akapity lub moduły są kiedykolwiek wywoływane, a analiza ta jest możliwa jedynie poprzez konstrukcję strukturalnego grafu wywołań, a nie poprzez zliczanie wystąpień w tekście.

Audyt bezpieczeństwa i zgodności

Audyt bezpieczeństwa i zgodności wymaga śledzenia wrażliwych danych w systemie: identyfikacji miejsca przechowywania danych osobowych, ścieżek kodu umożliwiających do nich dostęp, sprawdzenia, czy kontrole dostępu są prawidłowo stosowane w każdej ścieżce wykonania prowadzącej do danych wrażliwych oraz czy dane wrażliwe mogą wydostać się z systemu poprzez logowanie, komunikaty o błędach lub odpowiedzi API. Są to zadania analizy przepływu danych i sterowania, które wyszukiwanie tekstowe słabo aproksymuje.

Wyszukiwanie tekstowe nazwy pola wrażliwego znajduje pliki zawierające tę nazwę. Nie pozwala określić, czy pliki te korzystają z autoryzowanego dostępu, nieautoryzowanego dostępu, czy też nie. Nie pozwala również ustalić, czy w ścieżce wykonania prowadzącej do dostępu do pola występuje kontrola dostępu. Nie pozwala również na śledzenie, czy wartość pola jest następnie zapisywana w dzienniku lub zwracana w odpowiedzi API, która nie powinna jej zawierać. Analiza skażenia, która śledzi przepływ wartości wrażliwych w systemie i identyfikuje miejsca, w których mogą one dotrzeć do niezaufanych wyników, jest funkcją analizy przepływu danych. Jest to funkcja oferowana przez narzędzia do analizy kodu uwzględniające bezpieczeństwo, której wyszukiwanie tekstowe nie jest w stanie przybliżyć.

W jaki sposób SMART TS XL Zapewnia zrozumienie kodu w całym przedsiębiorstwie

SMART TS XL opiera się na założeniu, że systemy korporacyjne wymagają zrozumienia struktur, a nie wyszukiwania tekstu. Platforma Software Intelligence analizuje kod źródłowy z każdego języka i platformy w środowisku, generuje dla każdego z nich abstrakcyjne drzewa składniowe i przekształca je w ujednolicony graf międzyjęzykowy, który reprezentuje zależności strukturalne całego systemu. Programy COBOL, strumienie zadań JCL, usługi Java, aplikacje .NET, skrypty Pythona, schematy SQL, moduły TypeScript i artefakty konfiguracji są reprezentowane na tym grafie jako węzły i krawędzie, a relacje wyrażone są jako połączenia typizowane: wywołania, przepływy danych, inkluzje copybooków, odwołania do schematów i ekwiwalencje międzyjęzykowe.

Funkcja wyszukiwania korporacyjnego platformy stanowi punkt wejścia do zadań związanych ze zrozumieniem kodu, ale działa zasadniczo inaczej niż wyszukiwanie tekstowe. Wyniki są uporządkowane według typu relacji i struktury artefaktu, a nie według wystąpienia ciągu znaków. Zapytanie o nazwę pola zwraca definicje, odczyty, zapisy, odwołania SQL i inkluzje copybook jako oddzielnie sklasyfikowane typy wyników. Dzięki temu programista pytający „co zapisuje w tym polu?” otrzymuje dokładnie relacje zapisu, a nie mieszaną listę wszystkich plików, w których występuje dana nazwa. Ta strukturalna organizacja wyników wyszukiwania odzwierciedla podstawowy model odsyłaczy i dostarcza programistom konkretnych, przydatnych informacji, bez konieczności ręcznego filtrowania wystąpień ciągu znaków.

Analiza wpływu platformy, przechodzenie przez graf wywołań, wizualizacja przepływu sterowania i możliwości śledzenia przepływu danych działają w oparciu o ten sam zunifikowany model strukturalny. Gdy programista identyfikuje przestarzałą funkcję, graf wywołań udostępnia listę wszystkich wywołujących na każdym poziomie hierarchii. Gdy planowana jest zmiana schematu, analiza wpływu wylicza każdego konsumenta w każdym języku. Gdy programista wdrażający musi zrozumieć proces wsadowy, wizualizacja przepływu sterowania sprawia, że ​​ścieżka wykonania jest nawigowalna, bez konieczności czytania setek wierszy kodu źródłowego po kolei. Jak wynika z szerszego kontekstu doświadczenia programistów i metryk DX dla starszych baz kodu , złożoność kodu i zawiłość strukturalna to czynniki decydujące o utrzymywalności, a narzędzia, które eksponują te właściwości strukturalne, a nie tylko tekst powierzchniowy, sprawiają, że złożone systemy są łatwe w zarządzaniu na dużą skalę.

Różnica między tym, co SMART TS XL To, co zapewnia wyszukiwanie tekstowe, to różnica między pytaniem, na które udzielono odpowiedzi, a pytaniem, na które rozpoczęto. Wyszukiwanie tekstowe rozpoczyna dochodzenie. Zrozumienie kodu je kończy.

Ciągły koszt zastępowania zrozumienia poszukiwaniem

Praktyczne konsekwencje traktowania wyszukiwania tekstowego jako substytutu rozumienia kodu kumulują się po cichu w każdym zadaniu programistycznym wymagającym znajomości struktury kodu. Każda ocena wpływu oparta na wyszukiwaniu tekstowym niesie ze sobą nieznaną liczbę pominiętych zależności. Każdy ślad pola, który zatrzymuje się na granicy języka, pozostawia część systemu niewidoczną. Każda identyfikacja martwego kodu, która zlicza wystąpienia ciągów znaków zamiast analizować osiągalność grafu wywołań, zawiera fałszywe alarmy i pomija prawdziwie martwy kod. Każdy audyt bezpieczeństwa, który wyszukuje wrażliwe nazwy pól zamiast śledzić przepływ danych przez ścieżki wykonania, zapewnia pewność, która jest niekompletna i nieweryfikowalna.

W małej, jednojęzycznej, często modyfikowanej bazie kodu, koszty te mogą być do opanowania. Programiści mają wystarczający kontekst, aby precyzyjnie filtrować wyniki wyszukiwania, granice systemu są zrozumiałe dla wszystkich członków zespołu, a ręczna inspekcja wypełnia lukę pozostawioną przez wyszukiwanie tekstowe wystarczająco szybko, aby uniknąć poważnych błędów. W dużym systemie korporacyjnym z wieloma językami, dekadami nagromadzonego kodu i strukturami zespołowymi, które oznaczają, że nikt nie rozumie całości, koszty się kumulują. Pominięte zależności ujawniają się w środowisku produkcyjnym. Oceny wpływu, które wzbudzały zaufanie na sali konferencyjnej, powodują niespodziewane awarie w wydaniu. Audyty bezpieczeństwa, które obejmowały każde wystąpienie ciągu znaków, pomijają ścieżki przepływu danych, które ujawniają wrażliwe dane. Wiedzy, która była przechowywana w głowach programistów, którzy od tego czasu odeszli, nie da się odtworzyć za pomocą wyszukiwania tekstowego, ponieważ zrozumiałe przez nich relacje strukturalne nigdy nie zostały zakodowane w żadnym ciągu znaków w plikach źródłowych.

Przejście od wyszukiwania tekstowego do rozumienia kodu nie oznacza zastąpienia jednego narzędzia innym. Wyszukiwanie tekstowe zachowuje swoją rolę w zadaniach, do których się nadaje: lokalizowaniu ciągów znaków, szybkiej orientacji, sprawdzaniu konfiguracji i nawigacji po plikach. Rozumienie kodu zapewnia analizę strukturalną, której wyszukiwanie tekstowe nie jest w stanie zapewnić: grafy wywołań, ślady przepływu danych, analizę wpływu, identyfikację martwego kodu i rozwiązywanie zależności międzyjęzykowych. Te dwa narzędzia działają na różnych poziomach abstrakcji, odpowiadają na różne kategorie pytań i służą różnym celom. Koszt ich połączenia to pominięte zależności, błędne oceny i stałe narastanie ryzyka wynikające z wprowadzania istotnych zmian w złożonych systemach z niekompletnym modelem ich faktycznego działania.