Pracowałem nad dużym systemem transakcji finansowych dla banku, który opiekował się emeryturami i inwestycjami. Po 15 latach zmian funkcji koszt ręcznego testu regresji wzrósł do 200 000 USD na wydanie. (10 mln LOC, transakcje 10 mln USD dziennie). System ten współpracuje również z 19 innymi systemami w firmie, przenoszącymi wiele danych. Ten system został wdrożony w Javie.
Obserwujemy jednak, że im więcej „ponownie wykorzystujemy”, tym bardziej rosną koszty testów regresji. (Powodem jest to, że musisz „przetestować kod, którego dotykasz” - a kod ponownie użyty / udostępniony wpływa na wiele miejsc po jego dotknięciu. Więc pomimo „OSUSZANIA - nie powtarzaj się” - tj. Nie kopiuj i nie wklejaj kodu - obserwujemy zachętę finansową do kopiowania i wklejania kodu. Ma to na celu obniżenie kosztów testów regresji, ponieważ nie chcemy modyfikować kodu, który mógłby być udostępniany, ponieważ spowoduje to duży wpływ testu regresji).
Moje pytanie brzmi, czy istnieje zasada inżynierii oprogramowania, która opisuje związek między ponownym użyciem a kosztami testu regresji?
Powodem, dla którego zadałem to pytanie, jest to, że prawdopodobnie rozkładanie systemu na mniejsze części do przetestowania jest opłacalne.
Założenia:
„Test regresji” oznacza „test akceptacji” - tj. Inną grupę poświęcającą czas na pisanie nowych i ponowne wykorzystywanie starych testów systemu w imieniu firmy, w tym konfiguracji środowiska i danych.
Wiem, że reakcja szarpnięcia kolana na duży koszt testu regresji to „bardziej zautomatyzowane testy”. To dobra zasada. W tym środowisku istnieje kilka wyzwań.
(a) Zautomatyzowane testy są mniej przydatne ponad granicami systemu, chyba że system ten ma również wysoki zakres testów automatycznych. (Sfera wpływu wpływ).
(b) Kiedy Twój system jest już duży i złożony, uzyskanie tempa czasu programisty lub inwestycji kapitałowych w wysoki zakres testów automatycznych jest trudne z kulturowego punktu widzenia.
(c) Koszt utrzymania automatycznych testów jest ukryty w projekcie i dlatego można je łatwo odrzucić na poziomie projektu.
(d) To tylko kulturowa rzeczywistość pracy w banku.
(e) Pracuję nad rozwiązaniem tego problemu w inny sposób (rozkład).
źródło
Odpowiedzi:
Powyżej brzmi mi dobrze. Im ważniejszy jest kod, im bardziej jest on dzielony, tym wyższe są wymagania jakościowe, tym bardziej należy włączyć zapewnienie jakości, gdy się zmienia.
Ponieważ twój system jest zaimplementowany w Javie, możesz zobaczyć przykład powyżej w standardowych bibliotekach Java (JDK). Jego główne wydania są rzadkie i towarzyszą im bardzo pracochłonne testy. Nawet drobne wydania przechodzą przez bardzo obszerny pakiet testów JCK, aby zweryfikować brak regresji.
Możesz myśleć, że to w jakiś sposób hamuje ewolucję współdzielonego kodu i ... tak, to jest w porządku. Im większy wpływ i ryzyko wiąże się ze zmianą kodu, tym bardziej ostrożnie powinieneś to robić, tym więcej wysiłku trzeba poświęcić na testowanie jego wydań.
Idealnie, jakość wydań powszechnie udostępnianego kodu powinna być taka, aby wcale nie wymagał dużych zmian (z wyjątkiem rzadkich ulepszeń). Ten tok myślenia znajduje odzwierciedlenie w słynnym cytacie Joshua Blocha :
W związku z powyższym wygląda na to, że niektóre z problemów, które opisujesz, są spowodowane nieefektywną strategią tworzenia kodu współdzielonego. Szczególnie kłopotliwe wydaje się to, że w przypadku ponownie wykorzystywanego kodu brane są pod uwagę tylko dwie opcje: albo skopiuj ten kod, albo dołącz go natychmiast do „wspólnych” bibliotek współdzielonych.
Ograniczanie się tylko do tych dwóch opcji jest niepotrzebne, i znowu możesz znaleźć przykłady tego, jak można to zrobić lepiej w samym JDK, którego używasz. Spójrz na
java.util.concurrent
pakiety ( JSR 166 ) - do czasu wydania Java 5 były to osobne biblioteki, a nie część podstawowych wydań JDK.Pomyśl o tym, jest to trzecia opcja, którą przeoczyłeś i dość pragmatyczna, którą musisz wziąć pod uwagę przy „tworzeniu” nowego wspólnego kodu. Kiedy po prostu wymyślisz kod, który można współdzielić między 2-3 komponentami, nic nie zmusza cię do natychmiastowego włączenia go do podstawowego API systemu.
Możesz spakować i zwolnić ten „niedojrzały” wspólny kod jako osobną bibliotekę, tak jak to zostało zrobione z równoległymi narzędziami Java. W ten sposób uwalnia Cię od konieczności pełnego testowania regresji, ponieważ możesz użyć tylko stosunkowo niewielkiej ilości zaangażowanych komponentów. W rezultacie masz większą swobodę w modyfikowaniu i ulepszaniu tego współdzielonego kodu oraz w testowaniu jego działania w środowisku produkcyjnym.
Gdy twoja biblioteka dojrzeje i ustabilizuje się na tyle, aby dać ci pewność, że dalsze zmiany w niej są mało prawdopodobne, możesz rozważyć włączenie jej do podstawowych bibliotek systemu, podobnie jak współbieżne narzędzia zostały ostatecznie dołączone do JDK.
Konkretny przykład, ile wysiłku (w tym testowania) może być włożony w zmianę mocno ponownie wykorzystywanego kodu, można znaleźć ponownie w JDK. W wersji 7u6 zmieniono wewnętrzną
String
reprezentację, która wiązała się ze zmianąsubstring
wydajności. Komentarze twórcy funkcji w Reddit pokazują, ile wysiłku włożono w tę zmianę:źródło
Nie sądzę, aby istniały jakieś dane do obliczenia „kosztu testów regresyjnych / LOC wbudowanego ponownie użytego kodu”. I nie sądzę, żeby ktokolwiek kiedykolwiek zainwestował tyle czasu i pieniędzy, aby zbudować ten sam „duży” system dwa razy, jedną wersję z dużą ilością podzespołów, a drugą bez, aby przeprowadzić poważne badania w tym zakresie.
Ale widziałem wcześniej problemy spowodowane ponownym użyciem, podobnie jak twoje, i być może interesują Cię przemyślenia na temat tego, jak lepiej sobie z tym poradzić.
Po pierwsze, nie jest to ponowne użycie, które jest twoim problemem - jest to raczej próba zbudowania własnych komponentów wielokrotnego użytku i używania ich w całym systemie. Jestem pewien, że wielokrotnie używasz dużych pakietów oprogramowania, w których nie występują problemy: pomyśl o całym stosie Java, którego używasz, a może o komponentach innych firm (zakładając, że jesteś zadowolony z tych komponentów). Ale czym różni się to oprogramowanie, na przykład biblioteki Java, podczas gdy twoje własne komponenty wielokrotnego użytku powodują tyle dodatkowych kosztów testowania regresji? Oto niektóre punkty, które mogą być inne:
składniki te są bardzo dojrzałe i stabilne
są opracowywane i w pełni testowane w izolacji przez zupełnie inną organizację
aby (ponownie) ich używać, nie musisz ich zmieniać (w rzeczywistości nie możesz tego zrobić, nawet jeśli tego chcesz, ponieważ nie utrzymujesz kodu źródłowego)
nie dostajesz codziennie nowej wersji, tylko drobne aktualizacje (maksymalnie na miesiąc) lub poważne aktualizacje w odstępach rocznych
większość aktualizacji jest zaprojektowana tak, aby były w 100% kompatybilne w dół, szczególnie niewielkie aktualizacje
Aby więc Twoje komponenty wielokrotnego użytku były bardziej skuteczne, powinieneś dostosować niektóre z powyższych rzeczy do własnego rozwoju:
za każdy komponent wielokrotnego użytku, ponosisz wyraźną odpowiedzialność za utrzymanie i upewnij się, że wszyscy ludzie, którzy ponownie użyją komponentu, mogą natychmiast otrzymać poprawkę, jeśli pojawią się problemy.
ustanowić ścisłe zasady dotyczące wersji i wydania. Opracowując komponent wielokrotnego użytku, nie udostępniaj go „każdemu” każdego dnia (przynajmniej jeśli nie oznaczałoby to przeprowadzenia pełnego testu regresji w wysokości 200 000 USD w systemie). Zamiast tego pozwól, aby nowe wersje były publikowane tylko od czasu do czasu, i zapewnij mechanizmy, które pozwolą użytkownikowi tego komponentu odroczyć zmianę do nowej wersji.
im częściej komponent jest ponownie wykorzystywany, tym ważniejsze jest to, że zapewnia stabilny interfejs i zachowanie kompatybilne w dół.
elementy wielokrotnego użytku wymagają bardzo kompletnych zestawów testowych do przetestowania ich w izolacji.
Wiele z tych rzeczy spowoduje, że koszt budowy samego komponentu wzrośnie, ale także zmniejszy koszty zmian spowodowanych nieudanymi regresjami.
źródło
Chociaż może wystąpić „zauważalny” wzrost kosztów ze względu na konieczność przeprowadzenia większej liczby testów, ten rodzaj refaktoryzacji zwykle sprawia, że kod jest łatwiejszy w utrzymaniu w przyszłości, gdy zmniejszasz zadłużenie techniczne w systemie.
Powinno to, miejmy nadzieję, zmniejszyć przyszłe błędy i ułatwić wdrożenie nowych funkcji lub zmian w istniejących funkcjach.
Mówiąc prościej, mam na myśli, że powinny one zająć mniej czasu i dlatego kosztują mniej.
Zredukuj, łatwiej i mniej są tutaj dość mglistymi terminami, a jakiekolwiek przyszłe oszczędności (lub raczej nadzieja na oszczędności) są niemożliwe do obliczenia, ponieważ jeszcze się nie wydarzyły.
Prostsza baza kodu powinna umożliwić nowym pracownikom lub istniejącym pracownikom przejście do projektu szybciej, szczególnie w przypadku dużych systemów.
Może również zmniejszyć rotację personelu, ponieważ można poprawić morale obecnych członków projektu.
Oczywiście nie ma gwarancji, że dostaniesz te korzyści, ale są to rzeczy, które należy rozważyć obok kosztów (takich jak zwiększone testy), które można zmierzyć.
W rzeczywistości lepszy kod powinien z czasem obniżyć koszty testowania, nawet jeśli początkowy wzrost wynika z tego, co opisujesz.
źródło