Niebezpieczeństwa związane z ogromnym monolitycznym zastosowaniem


20

Wielki projekt, nad którym pracuję od kilku lat, to aplikacja kontrolna (i wszystko) zaawansowanego urządzenia, stanowiącego serce jego oprogramowania układowego.

Urządzenie jest dość zaawansowane, ma więcej funkcji niż mogłem powiedzieć z pamięci, a 98% z nich jest obsługiwanych przez ten jeden ogromny plik wykonywalny. Z jednej strony program jest dość łatwy w utrzymaniu, dobrze zmodularyzowany w środku, odpowiednio udokumentowany, istnieje rozsądny podział funkcji na katalogi i pliki i tak dalej.

Ale ostatecznie wszystko jest skupione w jednej aplikacji, która robi wszystko, od zdalnej komunikacji z bazą danych, obsługi ekranu dotykowego, obsługi kilkunastu różnych protokołów komunikacyjnych, pomiarów, kilku algorytmów sterowania, przechwytywania wideo, godziny wschodu słońca i daty Wielkanocy (poważnie, i są one potrzebne do bardzo poważnych celów!) ... Ogólnie rzecz biorąc, rzeczy, które są bardzo cienko powiązane, często powiązane tylko przez niektóre dane, które przeciekają między odległymi modułami.

Można to zrobić jako kilka oddzielnych plików wykonywalnych komunikujących się ze sobą, powiedzmy, ponad gniazdami, w bardziej szczegółowym celu, może być ładowanych / rozładowywanych w razie potrzeby i tak dalej. Nie ma konkretnego powodu, dla którego jest tak wykonany.

Z jednej strony działa i działa dobrze. Projekt jest prostszy, bez utrzymywania kompilacji wielu plików binarnych. Struktura wewnętrzna jest również łatwiejsza, gdy można po prostu wywołać metodę lub odczytać zmienną zamiast rozmawiać przez gniazda lub pamięć współdzieloną.

Ale z drugiej strony, rozmiar, skala tej rzeczy po prostu mnie przeraża, mam wrażenie, że pilotuję Titanica. Zawsze uczono mnie modularyzacji, a zlepianie wszystkiego w jeden gigantyczny plik wydaje się błędne. Jednym z problemów, jaki znam, jest poważna awaria jednego (nawet nieznacznego) modułu, który powoduje awarię wszystkich - ale jakość kodu zapewnia, że ​​tak się nie dzieje w wersjach wydanych. W przeciwnym razie separacja wewnętrzna i programowanie obronne zapewnia, że ​​nadal będzie działało poprawnie, nawet jeśli z jakiegoś powodu połowa wewnętrznych modułów ulegnie awarii.

Jakie inne niebezpieczeństwa przeoczyłem? Dlaczego mnie to przeraża? Czy to tylko irracjonalny strach przed nieznanym? Czy robienie poważnych dużych projektów w ten sposób jest akceptowaną praktyką? Albo uspokój moje obawy, albo daj mi dobry powód do przefakturowania wersji 2.0 na wiele mniejszych plików binarnych.


7
Jeśli słońce za wcześnie wstanie, Anglia wypłynie w morze.
Maks.

1
Lub ciśnienie w jądrze Ziemi nieznacznie wzrasta.
StuperUser

1
@Maxpm Widzę, co tam zrobiłeś ... xkcd.com/687
teto 13.07.11

3
@Maxpm: Na szczęście nasza aplikacja nie napędza wschodów i zachodów słońca. Ufamy, że księżniczka Celestia nie zawiedzie swojej pracy.
SF.

1
@rwong: 1. Urządzenie jest zatrzymywane w celu aktualizacji. Mimo że przetrwałby aktualizacje w locie, nie chcemy żadnych nieoczekiwanych wyników na wypadek, gdyby aktualizacja z jakiegokolwiek powodu się nie udała. 2. Jednordzeniowy ARM9 z wbudowanym systemem Linux. Istnieje drugi, nadzorujący procesor działający bez systemu operacyjnego, sprawdzenie, czy główny procesor generuje rozsądną moc wyjściową.
SF.

Odpowiedzi:


5

Poza drobnym komentarzem na końcu (drugi, nadzorujący procesor) możesz opisywać moją firmę. Tak, my też potrzebujemy Wielkanocy.

Cóż, jesteśmy trochę dalej. Podzieliliśmy duży plik wykonywalny i próbowaliśmy użyć standardowych komponentów do standardowych bitów. Niezupełnie duża poprawa, na którą możesz liczyć. W rzeczywistości wydajność staje się poważnym problemem nawet w przypadku mocniejszego sprzętu. A koszty utrzymania tak naprawdę nie spadły, ponieważ jest mnóstwo kodu do serializacji i synchronizacji danych na wąskich interfejsach.

Lekcja, której się nauczyłem? Posiadanie jednego pliku wykonywalnego jest sprawdzonym rozwiązaniem dla małych systemów i mamy dziesięciolecia doświadczenia w zarządzaniu nim. Wszystkie nasze narzędzia obsługują to natywnie. Modułowość można wykonać również w ramach jednego pliku wykonywalnego, a gdy trzeba iść na kompromis w sprawie modułowości z innych powodów, włamanie pozostaje niewielkie.


Dzięki - żadne „najlepsze praktyki, których należy przestrzegać / poświęcone” i „wyważanie potencjalnych korzyści w porównaniu z potencjalnymi wadami”, nigdy nie są tak pomocne, jak ktoś z pierwszej ręki z drugiej strony ogrodzenia.
SF.

23

Urządzenie jest dość zaawansowane, ma więcej funkcji niż mogłem powiedzieć z pamięci, a 98% z nich jest obsługiwanych przez ten jeden ogromny plik wykonywalny. Z jednej strony program jest dość łatwy w utrzymaniu, dobrze zmodularyzowany w środku, odpowiednio udokumentowany, istnieje rozsądny podział funkcji na katalogi i pliki itd.

Sam to powiedziałeś - jest łatwy w utrzymaniu, dobrze zmodularyzowany ...

Moim zdaniem i większość kierownictwa zgodzi się ze mną w tej sprawie, nigdy nie należy wprowadzać zmian ze względu na zmiany. Nie ma nic złego w tym programie (twoim opisie) poza tym, że nie jest zgodny z najnowszymi trendami programistycznymi (które są wymienione w innych odpowiedziach).

Tak, to większy program ... wiele z nich było wcześniej. Jest również dobrze udokumentowany, więc wystarczy przestudiować jego wewnętrzną organizację, a zobaczysz w niej logikę. Wątpię, aby wszyscy ci, którzy je napisali, byli niekompetentni. Więc zbadaj go trochę, naucz się ... potem utrzymuj go takim, jaki jest, aż pojawi się solidny powód do przepisania go. Twój menedżer będzie Ci wdzięczny za dobry stosunek kosztów do efektów.

Jakie inne niebezpieczeństwa przeoczyłem? Dlaczego mnie to przeraża? Czy to tylko irracjonalny strach przed nieznanym? Czy robienie poważnych dużych projektów w ten sposób jest akceptowaną praktyką? Albo uspokój moje obawy, albo daj mi dobry powód do przefakturowania wersji 2.0 na wiele mniejszych plików binarnych.

Przeraża cię, ponieważ jest duży - ale z drugiej strony nikt nie oczekuje, że nauczysz się wszystkiego na pamięć w ciągu tygodnia. Pracuj więc, kawałek po kawałku, kawałek po kawałku, aż zrozumiesz. Na koniec dowiesz się, że prawdopodobnie jest dobrze zorganizowany i jak jest zorganizowany.

Gdybyś go przepisał, zrobiłbyś to samo, ale jednocześnie zrujnowałbyś go dla wszystkich, którzy byli przyzwyczajeni do poprzedniej organizacji kodu. W ten sposób tylko Ty musisz się „dostosować”.


16

Czułbym się lepiej, gdybyś powiedział, że wszystko zostało zapakowane w testy jednostkowe. Jeśli tego nie zrobisz, powinieneś zbudować zestaw testowy, zanim nawet pomyślisz o ponownej analizie aplikacji.

Posiadanie zestawu testów poprawi twoje zrozumienie aplikacji i powie ci, kiedy zmiana w aplikacji coś zepsuje.


2
Ale dotyczy to obu możliwych architektur ...
SF.

3
Tak, to robi ...
Robert Harvey,

10
... i dlatego ta odpowiedź nie dodaje niczego do dyskusji poza promowaniem testów jednostkowych.
benzado

2
@benzado: Co w scenariuszu PO ma ogromne znaczenie.
Robert Harvey

2
@ironcode: Rzuć okiem na książkę typu „Skuteczna praca ze starszym kodem”. Pierwszą rzeczą, którą mówi w tej książce jest: „Jeśli nie masz już pakietu testów jednostkowych z wysokim kodem, napisz je najpierw, zanim zrobisz cokolwiek innego. ” Powiedziałbym, że rada jest bardziej niż istotna tutaj, biorąc pod uwagę, że OP dokładnie zapytał „Jakie inne niebezpieczeństwa przeoczyłem”.
Robert Harvey

8

Jeśli kod jest dobrze zmodularyzowany z niskim sprzężeniem i wysoką kohezją, jest skutecznie dzielony. Narzut komunikacji powinien być niższy w ramach jednego procesu niż w przypadku wielu procesów.

W przypadku systemów wbudowanych jeden proces może wyeliminować potrzebę implementacji O / S w celu uruchomienia wszystkich tworzonych procesów. Trzeba też zaimplementować system, aby sprawdzić, czy wszystkie procesy działają, i zrestartować je, jeśli to możliwe. Jeśli nie byłoby to możliwe, musiałbyś odpowiednio zareagować.

Podział kodu na osobne pliki wykonywalne zwiększyłby również ogólny rozmiar kodu źródłowego, ponieważ konieczne byłoby zaimplementowanie całego kodu klient / serwer między modułami. Potrzebne byłyby również więcej przypadków testowych do obsługi tego kodu oraz przypadków, gdy nie było procesu serwera. Duże projekty są duże, co eliminuje zbędne zawiłości, jak się wydaje, tutaj zrobiono, dzięki czemu są tak małe, jak mogą być.

Testowanie jest tutaj rodzajem czerwonego śledzia, ponieważ problemy będą nadal występować podczas testowania lub bez niego. Z pewnością należy zachęcać do dobrych testów z dowolnym wyborem projektu. Oczekuję, że testowanie jest prostsze dzięki kodowi monolitycznemu.

Wymuszanie awarii w razie potrzeby jest również łatwiejsze dzięki monolitycznemu plikowi wykonywalnemu.


1
+1, inżynieria polega na kompromisach, mniejsze pliki binarne też mają swój koszt
benzado

+1 Zgadzam się z całego serca. Nie podano konkretnego powodu, aby uzasadnić wiele plików wykonywalnych. Komunikacja i koordynacja między plikami wykonywalnymi ma swoją cenę.
Erick Robertson

+1: Jeśli się nie zepsuło, nie naprawiaj go.
Bob Murphy

1

Gdybym miał pracować nad tak wielką monolityczną aplikacją, to, co by mnie przeraziło, to brak enkapsulacji, niska kohezja, wysokie sprzężenie i jak to wszystko przetestować. Wielkie monolity są często zaproszeniem do porzucenia właściwej architektury i rozpoczęcia zejścia w wielką kulę błota .

Kiedy to się dzieje, testowanie staje się koszmarem i jesteś na dobrej drodze do starzenia się.

Jeśli jednak kod jest dobrze ustrukturyzowany i dobrze utrzymany, a zasady wysokiej kohezji, niskiego sprzężenia i właściwej enkapsulacji są przestrzegane, to nie widzę powodu, aby przekształcać to w mniejsze jednostki.

Jednak chcesz mieć dobre testy.


1

Idealnie odpowiedź brzmi: tak. Posiadanie wielu plików binarnych może zwiększyć stabilność.

... wspomniałeś jednak również, że kod w bazie kodu monolitycznego jest dobrze napisany i zmodularyzowany, co oznacza, że ​​nie masz do czynienia z ogromnym splątanym bałaganem, tylko ogromną bazą kodu ...

Ogólnie rzecz biorąc, powiedziałbym, że stosownym powiedzeniem jest: „Nie pozwól, aby doskonały był wrogiem dobra”. Chociaż uważam, że masz rację, twierdząc, że monolityczna baza kodu jest zła, uważam również, że nie warto się tym martwić.

Rozsądne byłoby posuwanie się naprzód, umieszczając nowe fragmenty kodu we własnych plikach binarnych, ale powrót do poprzedniej wersji i refaktoryzacja prawdopodobnie nie są warte twojego czasu.


1

Jeśli używasz jednego procesu, istnieje szansa, że ​​dziki wskaźnik z jednego z modułów podrzędnych zniszczy krytyczny fragment pamięci (i spowoduje awarię całego systemu).

Jeśli używasz różnych procesów, przynajmniej nie używasz tego samego stosu lub stosu wywołań, a może być również łatwiej zrestartować pojedynczy podproces.

Struktura wewnętrzna jest również łatwiejsza, gdy można po prostu wywołać metodę lub odczytać zmienną zamiast rozmawiać przez gniazda lub pamięć współdzieloną.

Redukowanie wszystkiego w wielu procesach zmusi cię również do wyczyszczenia projektu i unikniesz, aby każdy moduł zaczął używać wewnętrznych metod innych modułów.

Biorąc to pod uwagę, jest to prawdopodobnie trudne zadanie.

To, co możesz zacząć, to zdecydować, że każdy nowy moduł powinien być odizolowanym procesem.


Zakładasz bardzo dużo o sprzęcie wykonawczym i środowisku systemu operacyjnego, szczególnie gdy dotyczy to oprogramowania układowego i urządzeń wbudowanych.
Patrick Hughes,

0

Kiedy wchodzę do królestwa, w którym projekt jest zbyt duży, aby za jednym zamachem za jednym zamachem, buduję mapę do powieszenia na ścianie, która przedstawia wszystkie duże sekcje i ich pasowanie. Następnie, kiedy pracuję, mogę odnieść się do modułu, na którym się skupiam, i wizualnie przypomnieć, jak pasuje do całości.

Jest całkiem możliwe, że złożoność i niebezpieczeństwa związane z utrzymywaniem systemu kompilacji i wersji dla wielu rozłączonych plików binarnych znacznie przewyższą Twój niepokój z powodu tak dużego i monolitycznego projektu.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.