Czy istnieje zasada inżynierii oprogramowania, która wiąże koszty ponownego użycia i regresji w systemie produkcyjnym?

12

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:

  1. „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.

  2. 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).

Sokole Oko
źródło
2
Składasz niezręczne stwierdzenie, że „ obserwujemy […], że im więcej„ ponownie wykorzystujemy ”, tym bardziej rosną koszty testów akceptacyjnych - czy możesz to rozwinąć? Czy test akceptacji nie powinien być niezależny od szczegółów implementacji, takich jak hierarchie dziedziczenia, i zamiast tego powinien na przykład analizować scenariusze użytkowania?
amon
2
Twoje pytanie jest interesujące, ale zawiera pewne bardzo stronnicze założenia, takie jak „ponowne użycie jest konsekwencją OO” lub wspomnianej części @amon. Sugeruję, aby całkowicie usunąć część „OO”, ponieważ twoje podstawowe pytanie jest niezależne od języka programowania lub jakiegokolwiek programowania OO. I nie jest jasne, jaki rodzaj „ponownego użycia” masz na myśli - „ponowne użycie” jest szerokim pojęciem, ponowne użycie kopiuj-wklej różni się od ponownego użycia komponentu lub biblioteki.
Doc Brown
Dziękuję, to jest pomocne. Usunąłem odniesienia do OO - i rozwinąłem pomysł dotknięcia współdzielonego kodu (lub kodu, który stanie się wspólnym kodem).
hawkeye
1
W obecnej formie odpowiedziałbym tylko „nie, nie sądzę” na twoje pytanie - które, jak sądzę, nie byłoby dla ciebie bardzo satysfakcjonujące. A może interesują Cię zasady i praktyki pozwalające faktycznie obniżyć koszty testowania podczas budowy dużego systemu, takiego jak Twój, z własnymi komponentami wielokrotnego użytku?
Doc Brown

Odpowiedzi:

11

nie chcemy modyfikować kodu, który mógłby być udostępniany, ponieważ spowoduje to duży wpływ testu regresji

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 :

Publiczne interfejsy API, takie jak diamenty, są wieczne. Masz jedną szansę, aby zrobić to dobrze, więc daj z siebie wszystko.


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.concurrentpakiety ( 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ą Stringreprezentację, która wiązała się ze zmianą substringwydajności. Komentarze twórcy funkcji w Reddit pokazują, ile wysiłku włożono w tę zmianę:

Wstępna analiza wyszła z grupy GC w 2007 roku ...

Wewnętrznie zespół ds. Wydajności Oracle utrzymuje zestaw reprezentatywnych i ważnych aplikacji oraz testów porównawczych, których używają do oceny zmian wydajności. Ten zestaw aplikacji był kluczowy w ocenie zmiany podciągów. Przyjrzeliśmy się uważnie zarówno zmianom wydajności, jak i zmianie powierzchni. Nieuchronnie, podobnie jak w przypadku każdej znaczącej zmiany, w niektórych aplikacjach wystąpiły regresje, aw innych - zyski. Zbadaliśmy regresje, aby sprawdzić, czy wydajność jest nadal akceptowalna i czy zachowana jest poprawność ...

Moja odpowiedź nie jest wyczerpująca, ale stanowi bardzo krótkie podsumowanie prawie sześciu miesięcy poświęconej pracy ...

komar
źródło
Wygląda na to, że oboje pracowaliśmy nad odpowiedzią równolegle, z wieloma podobnymi myślami ...
Doc Brown,
@DocBrown tak, to ciekawe, że odpowiedzieliśmy również prawie jednocześnie, około godziny po tym, jak pytanie zostało zredagowane w kształt
gnat
9

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.

Doktor Brown
źródło
0

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.

ozz
źródło