Dług techniczny jest nieuchronną cechą systemów oprogramowania — konsekwencją niedoskonałej wiedzy w momencie projektowania, zmieniających się wymagań w czasie i praktycznej konieczności kompromisów między szybkością a doskonałością. W oprogramowaniu komercyjnym dług techniczny jest zarządzany poprzez refaktoryzację, przepisywanie lub wymianę systemu — cykle, które zazwyczaj rozgrywają się przez trzy do siedmiu lat. W oprogramowaniu obronnym horyzont czasowy jest zasadniczo różny. Główne systemy obronne regularnie działają przez 20 do 40 lat. System zaprojektowany i zakodowany w 2005 roku może nadal działać w 2045 roku, pracując na sprzęcie trzeciej generacji, obsługiwanym przez inżynierów, którzy nie byli jeszcze w sile roboczej, gdy pisano kod.

Ten rozszerzony horyzont czasowy tworzy problem długu technicznego, który jest jakościowo różny od tego, do czego zaprojektowane są komercyjne podejścia do zarządzania oprogramowaniem. Strategie działające dla pięcioletniego produktu SaaS nie przekładają się bezpośrednio na system obronny oczekiwany do służby przez trzy dekady. W tym artykule zbadano, dlaczego dług techniczny akumuluje się inaczej w systemach obronnych, kategorie długu o największym znaczeniu oraz podejścia stosowane przez doświadczone programy oprogramowania obronnego do zarządzania długiem bez naruszania ciągłości operacyjnej.

Dlaczego systemy obronne akumulują więcej długu technicznego

Systemy obronne akumulują dług techniczny szybciej niż systemy komercyjne w porównywalnych kategoriach złożoności, z powodów strukturalnych zakorzenionych w sposobie funkcjonowania zamówień obronnych i wsparcia eksploatacyjnego.

Koszty ogólne zarządzania zmianami zniechęcają do refaktoryzacji. Zmiany oprogramowania obronnego zazwyczaj wymagają formalnych wniosków o zmiany, ocen wpływu, zatwierdzenia przez organy programowe i testów regresji na pełnym zestawie testowym. Ten narzut jest odpowiedni dla zmian wpływających na zachowanie operacyjne, ale dotyczy również wewnętrznej refaktoryzacji, która nie zmienia zewnętrznego zachowania. Gdy programista identyfikuje kod wymagający restrukturyzacji dla utrzymywalności, ścieżka najmniejszego oporu to pozostawienie go bez zmian i napisanie nowego kodu, który go obchodzi — akumulując dług zamiast go spłacać.

Koszty ponownej akredytacji zniechęcają do zmian architektonicznych. Zmiany w systemach oprogramowania obronnego często wywołują częściową lub pełną reakredytację — oceny bezpieczeństwa i niezawodności, które muszą być powtarzane przy zmianie oprogramowania. Znacząca refaktoryzacja architektoniczna może wymagać reakredytacji całego systemu, co jest kosztowne i czasochłonne. Tworzy to silną zachętę do zachowania istniejącej architektury, nawet gdy nie jest już ona odpowiednia, i wdrażania nowych możliwości jako uzupełnienia istniejących struktur, a nie ich zamienników.

Utrata wiedzy jest strukturalna i nieuchronna. Przez 30-letni okres trwania programu pierwotny zespół programistyczny zostanie całkowicie rozproszony — poprzez zwykłą mobilność zawodową, przejście na emeryturę i naturalne odejścia. Nie jest to porażka zarządzania wiedzą; jest to nieuchronna konsekwencja długości programu. Dokumentacja, która wydawała się adekwatna, gdy była pisana z uwzględnieniem niejawnej wiedzy posiadanej przez autorów, staje się nieadekwatna, gdy ta wiedza odchodzi. Decyzje architektoniczne podjęte z powodów, które nie są już oczywiste, stają się „świętymi krowami" — kodem, którego nikt nie chce zmieniać, ponieważ nikt nie rozumie konsekwencji.

Ewolucja technologiczna tworzy dług zależności. Komponent zawierający najlepsze algorytmy kryptograficzne w 2005 roku może używać przestarzałych algorytmów do 2025 roku. Framework aktywnie utrzymywany w 2010 roku może być porzucony i podatny na ataki do 2030 roku. Oprogramowanie nadal działa — być może bardzo niezawodnie — podczas gdy jego poziom bezpieczeństwa pogarsza się, a utrzymywalność maleje, akumulując dług, który jest niewidoczny, dopóki nie stanie się krytyczny.

Rodzaje długu technicznego istotne w obronie

Dług kodu jest najbardziej widoczną kategorią: kod złożony, słabo udokumentowany, niespójnie ustrukturyzowany lub napisany w sposób utrudniający modyfikację i podatny na błędy. Dług kodu zwiększa koszt utrzymania i wskaźnik defektów. W systemach obronnych dług kodu jest szczególnie niebezpieczny, ponieważ konsekwencje defektów są wyższe, a pula osób utrzymujących, które rozumieją kod, jest często mała i kurczy się.

Dług architektoniczny jest mniej widoczny, ale bardziej znaczący. Powstaje, gdy projekt strukturalny systemu nie odpowiada już kontekstowi operacyjnemu, któremu służy — zazwyczaj dlatego, że wymagania ewoluowały znacznie od czasu pierwotnego projektu. Dług architektoniczny przejawia się jako rosnąca złożoność przy dodawaniu nowych możliwości, kruchość przy wprowadzaniu zmian i trudności z integracją z nowymi systemami.

Dług zależności obejmuje skumulowane ryzyko wynikające z przestarzałych komponentów stron trzecich: wersje systemów operacyjnych na progu lub po przekroczeniu daty końca wsparcia, biblioteki z odnotowanymi niezałatanymi podatnościami, implementacje kryptograficzne używające przestarzałych algorytmów i protokoły komunikacyjne nie uznawane już za bezpieczne. Dług zależności jest szczególnie niebezpieczny w systemach obronnych, ponieważ może nie być natychmiast widoczny — system działa poprawnie, podczas gdy jego poziom podatności pogarsza się.

Dług dokumentacyjny to luka między udokumentowanym zrozumieniem systemu a rzeczywistym zachowaniem systemu. W długotrwałych systemach dług dokumentacyjny akumuluje się poprzez zmiany wdrażane bez odpowiednich aktualizacji dokumentacji, poprzez niezadokumentowane obejścia stające się funkcjami i poprzez odejście personelu posiadającego wiedzę, która nigdy nie została uchwycona na piśmie.

Efekt narastania długu: Kategorie długu technicznego oddziałują na siebie. Dług architektoniczny utrudnia rozwiązanie długu kodu, ponieważ refaktoryzacja w źle ustrukturyzowanym systemie jest bardziej ryzykowna. Dług zależności sprawia, że rozwiązanie długu architektonicznego jest droższe, ponieważ aktualizacja zależności może wymagać zmian architektonicznych w celu uwzględnienia zmian API naruszających zgodność. Dług dokumentacyjny pogarsza wszystkie inne rodzaje długu, ponieważ osoby utrzymujące pracujące bez odpowiedniej dokumentacji z większym prawdopodobieństwem wprowadzą nowy dług, próbując spłacić istniejący.

Wzorzec Strangler Fig dla stopniowej refaktoryzacji bez przestojów

Wzorzec Strangler Fig — nazwany na cześć gatunku drzewa, który rośnie wokół i ostatecznie zastępuje swojego gospodarza — jest główną strategią architektoniczną dla refaktoryzacji systemów obronnych, które nie mogą zostać wyłączone do wymiany. Wzorzec działa poprzez stopniowe budowanie nowej funkcjonalności obok istniejącej, progresywne kierowanie ruchu do nowej implementacji w miarę jej walidacji i ostateczne wycofanie starej implementacji, gdy nowa ją całkowicie zastąpiła.

W praktyce dla systemów obronnych wzorzec zazwyczaj obejmuje: identyfikację ograniczonej możliwości w istniejącym systemie, którą można wdrożyć niezależnie; zbudowanie zamiennika jako oddzielnego komponentu lub usługi; wprowadzenie warstwy przechwytującej (fasady, proxy lub routera komunikatów), która może kierować żądania do starej lub nowej implementacji; oraz stopniowe przesuwanie ruchu do nowej implementacji w miarę jak testy walidują ją względem zachowania starej implementacji. Stara implementacja nie jest usuwana, dopóki nowa nie zostanie zwalidowana względem pełnego zestawu testów operacyjnych i w reprezentatywnych warunkach operacyjnych.

Podejście Strangler Fig jest wolniejsze niż całkowite przepisanie, ale znacznie mniej ryzykowne — w dowolnym momencie migracji stara implementacja może zostać przywrócona do pełnego działania poprzez dostosowanie warstwy przechwytującej. Dla systemów obronnych, w których ciągłość operacyjna nie może zostać przerwana, ta odwracalność nie jest udogodnieniem — jest wymogiem. Całkowite przepisywanie systemów obronnych ma słabą historię, głównie dlatego, że niejawna wiedza osadzona w starym systemie — w tym zamierzona i niezamierzona obsługa przypadków brzegowych — rzadko jest w pełni uchwycona w specyfikacjach i dlatego nie jest wiernie odtworzona w przepisaniu.

Ramy priorytetyzacji: ryzyko versus koszt w kontekście krytycznym dla misji

Nie cały dług techniczny w systemie obronnym wymaga pilnego rozwiązania, a zasoby dostępne do spłaty długu są zawsze ograniczone. Praktyczne ramy priorytetyzacji dla długu systemu obronnego uwzględniają dwa wymiary: ryzyko operacyjne stanowione przez dług, jeśli nie zostanie rozwiązany, oraz koszt (w wysiłku, wpływie na harmonogram i narzucie akredytacyjnym) jego rozwiązania.

Dług wysokiego ryzyka i niskiego kosztu powinien być rozwiązywany natychmiast niezależnie od jego widoczności: ta kategoria obejmuje znane podatności bezpieczeństwa z dostępnymi łatkami, słabości kryptograficzne z prostym usunięciem i luki dokumentacyjne tworzące ryzyko operacyjne w krótkim terminie. Dług niskiego ryzyka i niskiego kosztu powinien być rozwiązywany oportunistycznie — gdy powiązana praca tworzy otwarcie do spłaty długu bez dodatkowego wpływu na harmonogram. Dług niskiego ryzyka i wysokiego kosztu — taki jak wielka refaktoryzacja architektoniczna komponentów działających odpowiednio — powinien być odraczany, chyba że istnieje strategiczny czynnik napędowy. Dług wysokiego ryzyka i wysokiego kosztu — zazwyczaj głębokie problemy architektoniczne tworzące ryzyko operacyjne — wymaga planowania i zasobowania na poziomie programu.

Priorytetyzacja musi również uwzględniać zależności między elementami długu i koszt czasowy narastania: dług, który jest dziś tani do rozwiązania, może być drogi do rozwiązania za pięć lat, jeśli narastał z innymi elementami długu lub jeśli okno możliwości zamknęło się. Najlepszy czas na rozwiązanie długu jest prawie zawsze wcześniej, niż wydaje się pilny — drugi najlepszy czas to teraz.