Mamy więc w naszym projekcie ten ogromny plik źródłowy mainmodule.cpp (11000 linii?) I za każdym razem, gdy go dotykam, kulę się.
Ponieważ ten plik jest tak centralny i duży, gromadzi coraz więcej kodu i nie mogę wymyślić dobrego sposobu, aby faktycznie zaczął się kurczyć.
Plik jest używany i aktywnie zmieniany w kilku (> 10) wersjach konserwacyjnych naszego produktu, więc naprawdę trudno jest go refaktoryzować. Gdybym miał „po prostu” podzielić to, powiedzmy na początek, na 3 pliki, to scalenie zmian z wersji serwisowych stanie się koszmarem. A także, jeśli podzielisz plik o tak długiej i bogatej historii, śledzenie i sprawdzanie starych zmian w SCC
historii nagle staje się znacznie trudniejsze.
Plik zawiera w zasadzie „klasę główną” (główne wysyłanie i koordynację pracy wewnętrznej) naszego programu, więc za każdym razem, gdy dodawana jest funkcja, wpływa ona również na ten plik i za każdym razem, gdy rośnie. :-(
Co byś zrobił w tej sytuacji? Jakieś pomysły na przeniesienie nowych funkcji do osobnego pliku źródłowego bez zakłócania SCC
przepływu pracy?
(Uwaga na temat narzędzi: używamy C ++ z Visual Studio
; Używamy AccuRev
jako, SCC
ale myślę, że rodzaj SCC
nie ma tu tak naprawdę znaczenia; Używamy Araxis Merge
do faktycznego porównywania i łączenia plików)
źródło
Odpowiedzi:
Znajdź w pliku pewien kod, który jest względnie stabilny (nie zmienia się szybko i nie różni się znacznie między gałęziami) i może stać się niezależną jednostką. Przenieś to do własnego pliku, a co za tym idzie, do jego własnej klasy we wszystkich gałęziach. Ponieważ jest stabilny, nie spowoduje to (wielu) „niezręcznych” połączeń, które muszą zostać zastosowane do innego pliku niż ten, w którym zostały pierwotnie utworzone, podczas scalania zmiany z jednej gałęzi do drugiej. Powtarzać.
Znajdź w pliku jakiś kod, który zasadniczo dotyczy tylko niewielkiej liczby gałęzi i może być samodzielny. Nie ma znaczenia, czy zmienia się szybko, czy nie, z powodu małej liczby gałęzi. Przenieś to do własnych klas i plików. Powtarzać.
Więc pozbyliśmy się kodu, który jest wszędzie taki sam, i kodu specyficznego dla niektórych gałęzi.
To pozostawia jądro źle zarządzanego kodu - jest potrzebny wszędzie, ale jest inny w każdej gałęzi (i / lub zmienia się stale, tak że niektóre gałęzie działają za innymi), a jednak jest w jednym pliku, w którym jesteś bezskutecznie próbuje połączyć się między oddziałami. Przestań to robić. Plik należy rozgałęzić na stałe , być może przez zmianę nazwy w każdej gałęzi. To już nie jest „główne”, to „główne dla konfiguracji X”. OK, więc tracisz możliwość zastosowania tej samej zmiany do wielu gałęzi przez scalanie, ale w każdym razie jest to rdzeń kodu, w którym scalanie nie działa zbyt dobrze. Jeśli i tak musisz ręcznie zarządzać połączeniami, aby poradzić sobie z konfliktami, nie jest stratą, aby zastosować je niezależnie w każdym oddziale.
Myślę, że mylicie się, twierdząc, że rodzaj SCC nie ma znaczenia, ponieważ na przykład możliwości scalania gita są prawdopodobnie lepsze niż narzędzie scalania, którego używasz. Zatem główny problem, „łączenie jest trudne” występuje w różnych momentach dla różnych SCC. Jednak prawdopodobnie nie będziesz w stanie zmienić SCC, więc problem jest prawdopodobnie nieistotny.
źródło
Scalanie nie będzie tak wielkim koszmarem, jak będzie, gdy w przyszłości otrzymasz plik 30000 LOC. Więc:
Jeśli nie możesz po prostu przestać kodować podczas procesu refaktoryzacji, możesz zostawić ten duży plik tak, jak jest , przynajmniej na chwilę, bez dodawania do niego więcej kodu: ponieważ zawiera on jedną „klasę główną”, możesz ją odziedziczyć i zachować odziedziczoną klasę ( es) z przeciążonymi funkcjami w kilku nowych małych i dobrze zaprojektowanych plikach.
źródło
Wydaje mi się, że napotykasz tutaj szereg zapachów kodu. Przede wszystkim wydaje się, że główna klasa narusza zasadę otwartego / zamkniętego . Wygląda również na to, że wykonuje zbyt wiele obowiązków . Z tego powodu zakładam, że kod jest bardziej kruchy niż powinien.
Chociaż rozumiem twoje obawy dotyczące identyfikowalności po refaktoryzacji, spodziewałbym się, że ta klasa jest raczej trudna w utrzymaniu i ulepszeniu oraz że wszelkie wprowadzane przez ciebie zmiany mogą powodować skutki uboczne. Zakładam, że ich koszt przewyższa koszt refaktoryzacji klasy.
W każdym razie, ponieważ zapachy kodu pogarszają się z czasem, przynajmniej w pewnym momencie ich koszt przewyższy koszt refaktoryzacji. Z twojego opisu zakładam, że przekroczyłeś punkt krytyczny.
Refaktoryzację należy wykonać małymi krokami. Jeśli to możliwe, dodaj automatyczne testy, aby zweryfikować bieżące zachowanie przed refaktoryzacją czegokolwiek. Następnie wybierz małe obszary izolowanej funkcjonalności i wyodrębnij je jako typy, aby przekazać odpowiedzialność.
W każdym razie to brzmi jak duży projekt, więc powodzenia :)
źródło
Jedyne rozwiązanie, jakie kiedykolwiek wyobrażałem sobie w przypadku takich problemów, jest następujące. Rzeczywistym zyskiem opisywanej metody jest postęp ewolucji. Żadnych obrotów tutaj, w przeciwnym razie bardzo szybko będziesz miał kłopoty.
Wstaw nową klasę CPP powyżej oryginalnej klasy głównej. Na razie zasadniczo przekierowałby wszystkie wywołania do bieżącej klasy głównej, ale miał na celu uczynienie interfejsu API tej nowej klasy tak przejrzystym i zwięzłym, jak to możliwe.
Po wykonaniu tej czynności masz możliwość dodania nowych funkcjonalności w nowych klasach.
Jeśli chodzi o istniejące funkcjonalności, musisz je stopniowo przenosić w nowych klasach, gdy stają się wystarczająco stabilne. Utracisz pomoc SCC dla tego fragmentu kodu, ale niewiele można na to poradzić. Po prostu wybierz odpowiedni czas.
Wiem, że to nie jest idealne, ale mam nadzieję, że może pomóc, a proces musi być dostosowany do twoich potrzeb!
Dodatkowe informacje
Zauważ, że Git to SCC, który może śledzić fragmenty kodu z jednego pliku do drugiego. Słyszałem o tym dobre rzeczy, więc może to pomóc, gdy stopniowo przenosisz swoją pracę.
Git jest zbudowany wokół pojęcia obiektów blob, które, jeśli dobrze rozumiem, reprezentują fragmenty plików kodu. Przenieś te elementy w różnych plikach, a Git je znajdzie, nawet jeśli je zmodyfikujesz. Oprócz wideo Linusa Torvaldsa wspomnianego w komentarzach poniżej, nie byłem w stanie znaleźć czegoś jasnego na ten temat.
źródło
Konfucjusz mówi: „pierwszym krokiem do wyjścia z dziury jest zaprzestanie kopania dziury”.
źródło
Niech zgadnę: dziesięciu klientów z rozbieżnymi zestawami funkcji i menedżerem sprzedaży, który promuje „personalizację”? Wcześniej pracowałem nad takimi produktami. Mieliśmy zasadniczo ten sam problem.
Zdajesz sobie sprawę, że posiadanie ogromnego pliku to kłopot, ale jeszcze większy problem to dziesięć wersji, które musisz utrzymywać na bieżąco. To wielokrotna konserwacja. SCC może to ułatwić, ale nie może to naprawić.
Zanim spróbujesz rozbić plik na części, musisz ponownie zsynchronizować dziesięć gałęzi, abyś mógł zobaczyć i ukształtować cały kod na raz. Możesz wykonać tę gałąź pojedynczo, testując obie gałęzie na tym samym głównym pliku kodu. Aby wymusić niestandardowe zachowanie, możesz użyć #ifdef i znajomych, ale lepiej, o ile to możliwe, użyć zwykłego if / else wobec zdefiniowanych stałych. W ten sposób kompilator zweryfikuje wszystkie typy i najprawdopodobniej i tak wyeliminuje „martwy” kod obiektu. (Możesz jednak wyłączyć ostrzeżenie o martwym kodzie).
Gdy istnieje tylko jedna wersja tego pliku udostępniana niejawnie przez wszystkie gałęzie, łatwiej jest rozpocząć tradycyjne metody refaktoryzacji.
#Ifdefs są przede wszystkim lepsze dla sekcji, w których kod, którego dotyczy problem, ma sens tylko w kontekście innych dostosowań dla poszczególnych gałęzi. Można argumentować, że stanowią one również okazję do tego samego schematu łączenia oddziałów, ale nie szaleją. Poproszę jeden kolosalny projekt na raz.
W krótkim okresie plik będzie się powiększał. To jest wporządku. To, co robisz, to łączenie rzeczy, które muszą być razem. Następnie zaczniesz widzieć obszary, które są wyraźnie takie same, niezależnie od wersji; można je pozostawić w spokoju lub zreformować do woli. Inne obszary będą się wyraźnie różnić w zależności od wersji. W tym przypadku masz wiele opcji. Jedną z metod jest delegowanie różnic do obiektów strategii dla poszczególnych wersji. Innym jest uzyskanie wersji klienckich ze wspólnej klasy abstrakcyjnej. Ale żadna z tych transformacji nie jest możliwa, o ile masz dziesięć „wskazówek” rozwoju w różnych gałęziach.
źródło
Nie wiem, czy to rozwiąże twój problem, ale myślę, że chcesz zrobić migrację zawartości pliku do mniejszych plików niezależnych od siebie (podsumowane). Dostaję też, że masz około 10 różnych wersji oprogramowania i musisz je wszystkie wspierać bez bałaganu.
Przede wszystkim nie ma sposobu, aby było to łatwe i rozwiąże się w ciągu kilku minut burzy mózgów. Funkcje połączone w pliku są niezbędne dla twojej aplikacji, a samo ich odcięcie i migracja do innych plików nie uratuje twojego problemu.
Myślę, że masz tylko te opcje:
Nie migruj i pozostań przy tym, co masz. Ewentualnie rzuć pracę i zacznij pracę nad poważnym oprogramowaniem z dobrym wzornictwem. Ekstremalne programowanie nie zawsze jest najlepszym rozwiązaniem, jeśli pracujesz nad długim projektem z wystarczającymi środkami, aby przetrwać awarię lub dwie.
Opracuj układ, w którym chciałbyś, aby Twój plik wyglądał po podzieleniu. Utwórz niezbędne pliki i zintegruj je z aplikacją. Zmień nazwę funkcji lub przeładuj je, aby wziąć dodatkowy parametr (może po prostu zwykłą wartość logiczną?). Gdy będziesz musiał popracować nad kodem, przenieś funkcje, nad którymi musisz pracować, do nowego pliku i zamapuj wywołania funkcji starych funkcji na nowe funkcje. Powinieneś nadal mieć swój główny plik w ten sposób i nadal być w stanie zobaczyć zmiany, które zostały w nim wprowadzone, gdy dojdzie do konkretnej funkcji, wiesz dokładnie, kiedy został zlecony na zewnątrz i tak dalej.
Spróbuj przekonać współpracowników dobrym ciastem, że przepływ pracy jest przereklamowany i że musisz przerobić niektóre części aplikacji, aby robić poważne interesy.
źródło
Dokładnie ten problem został rozwiązany w jednym z rozdziałów książki „Skutecznie współpracując ze starszym kodem” ( http://www.amazon.com/Working-Effectively-Legacy-Michael-Feathers/dp/0131177052 ).
źródło
Myślę, że najlepiej byłoby stworzyć zestaw klas poleceń , które będą mapowane na punkty API mainmodule.cpp.
Po ich wprowadzeniu będziesz musiał przebudować istniejącą bazę kodu, aby uzyskać dostęp do tych punktów API za pośrednictwem klas poleceń. Gdy to zrobisz, możesz dowolnie refaktoryzować implementację każdego polecenia do nowej struktury klas.
Oczywiście, z jedną klasą 11 KLOC, kod tam jest prawdopodobnie bardzo sprzężony i kruchy, ale tworzenie indywidualnych klas poleceń pomoże znacznie bardziej niż jakiejkolwiek innej strategii proxy / elewacji.
Nie zazdroszczę temu zadaniu, ale z biegiem czasu ten problem będzie się nasilał, jeśli nie zostanie rozwiązany.
Aktualizacja
Sugeruję, że wzór Dowodzenia jest lepszy niż Fasada.
Preferowane jest utrzymywanie / organizowanie wielu różnych klas dowodzenia na (względnie) monolitycznej fasadzie. Mapowanie pojedynczej elewacji na plik 11 KLOC prawdopodobnie będzie musiało zostać podzielone na kilka różnych grup.
Po co zawracać sobie głowę próbą znalezienia tych grup fasad? Dzięki wzorowi poleceń będziesz mógł grupować i organizować te małe klasy w sposób organiczny, dzięki czemu masz dużo większą elastyczność.
Oczywiście obie opcje są lepsze niż pojedynczy 11 KLOC i rosnący plik.
źródło
Jedna ważna rada: nie mieszaj refaktoryzacji z poprawkami błędów. To, czego chcesz, to wersja Twojego programu, która jest identyczna z poprzednią wersją, z tym wyjątkiem, że kod źródłowy jest inny.
Jednym ze sposobów może być podzielenie najmniejszej funkcji / części na własny plik, a następnie dołączenie jej wraz z nagłówkiem (przekształcając main.cpp w listę #include, która sama w sobie brzmi jak kod * Nie jestem Guru C ++), ale przynajmniej teraz jest podzielony na pliki).
Następnie możesz spróbować przełączyć wszystkie wersje serwisowe na „nowy” plik main.cpp lub jakąkolwiek inną strukturę. Znowu: Żadnych innych zmian ani poprawek błędów, ponieważ śledzenie ich jest mylące jak diabli.
Inna sprawa: ile tylko możesz chcieć zrobić jedno wielkie przejście do refaktoryzacji całej rzeczy za jednym razem, możesz odgryźć więcej niż możesz przeżuć. Może po prostu wybierz jedną lub dwie „części”, weź je do wszystkich wydań, a następnie dodaj trochę wartości dla swojego klienta (w końcu Refaktoryzacja nie dodaje bezpośredniej wartości, więc jest to koszt, który musi być uzasadniony), a następnie wybierz inną jedna lub dwie części.
Oczywiście wymaga to pewnej dyscypliny w zespole, aby faktycznie używać podzielonych plików, a nie tylko dodawać nowe rzeczy do main.cpp przez cały czas, ale znowu, próba zrobienia jednego ogromnego refaktora może nie być najlepszym rozwiązaniem.
źródło
Rofl, to przypomina mi moją starą pracę. Wygląda na to, że zanim dołączyłem, wszystko było w jednym wielkim pliku (także C ++). Następnie podzielili go (w całkowicie losowych punktach za pomocą dołączeń) na około trzy (wciąż ogromne pliki). Jakość tego oprogramowania była, jak można się spodziewać, okropna. Projekt wyniósł około 40 tys. LOC. (nie zawiera prawie żadnych komentarzy, ale DUŻO duplikatu kodu)
W końcu zrobiłem kompletną przeróbkę projektu. Zacząłem od powtórzenia najgorszej części projektu od zera. Oczywiście miałem na myśli możliwy (mały) interfejs między tą nową częścią a resztą. Następnie wstawiłem tę część do starego projektu. Nie zmieniłem starego kodu, aby utworzyć niezbędny interfejs, ale po prostu go zastąpiłem. Potem zrobiłem małe kroki, przepisując stary kod.
Muszę powiedzieć, że zajęło to około pół roku i w tym czasie nie było rozwijania starej bazy kodu oprócz poprawek błędów.
edytować:
Rozmiar pozostawał na poziomie około 40 000 LOC, ale nowa aplikacja zawierała o wiele więcej funkcji i prawdopodobnie mniej błędów w swojej początkowej wersji niż 8-letnie oprogramowanie. Jednym z powodów przepisywania było również to, że potrzebowaliśmy nowych funkcji i wprowadzenie ich do starego kodu było prawie niemożliwe.
Oprogramowanie było dla systemu wbudowanego, drukarki etykiet.
Kolejną kwestią, którą powinienem dodać, jest teoretycznie projekt C ++. Ale to wcale nie był OO, mógł to być C. Nowa wersja była zorientowana obiektowo.
źródło
OK, więc przeważnie przepisywanie API kodu produkcyjnego to zły pomysł na początek. Dwie rzeczy muszą się wydarzyć.
Po pierwsze, musisz faktycznie poprosić swój zespół o zawieszenie kodu w bieżącej produkcyjnej wersji tego pliku.
Po drugie, musisz wziąć tę wersję produkcyjną i utworzyć gałąź, która zarządza kompilacjami za pomocą dyrektyw przetwarzania wstępnego, aby podzielić duży plik. Podział kompilacji za pomocą dyrektyw preprocesora JUST (#ifdefs, #include, #endifs) jest łatwiejszy niż przekodowanie API. Jest to zdecydowanie łatwiejsze dla umów SLA i ciągłego wsparcia.
Tutaj możesz po prostu wyciąć funkcje odnoszące się do konkretnego podsystemu w klasie i umieścić je w pliku powiedzmy mainloop_foostuff.cpp i dołączyć go do mainloop.cpp w odpowiednim miejscu.
LUB
Bardziej czasochłonnym, ale solidnym sposobem byłoby opracowanie wewnętrznej struktury zależności z podwójną pośrednią reakcją na uwzględnienie rzeczy. Umożliwi to podzielenie rzeczy i zadbanie o współzależności. Należy zauważyć, że takie podejście wymaga kodowania pozycyjnego i dlatego powinno być połączone z odpowiednimi komentarzami.
Takie podejście obejmowałoby komponenty, które są używane na podstawie kompilowanego wariantu.
Podstawowa struktura polega na tym, że plik mainclass.cpp będzie zawierał nowy plik o nazwie MainClassComponents.cpp po bloku instrukcji, takich jak:
Podstawowa struktura pliku MainClassComponents.cpp byłaby tam, aby opracować zależności w ramach podskładników w następujący sposób:
A teraz dla każdego komponentu tworzysz plik component_xx.cpp.
Oczywiście używam liczb, ale powinieneś użyć czegoś bardziej logicznego na podstawie twojego kodu.
Korzystanie z preprocesora pozwala dzielić rzeczy bez martwienia się o zmiany API, co jest koszmarem w produkcji.
Po ustabilizowaniu produkcji możesz rozpocząć pracę nad przeprojektowaniem.
źródło
Rozumiem twój ból :) Byłem też w kilku takich projektach i to nie jest ładne. Nie ma na to łatwej odpowiedzi.
Jednym z podejść, które może Ci się przydać, jest rozpoczęcie dodawania bezpiecznych zabezpieczeń we wszystkich funkcjach, to znaczy sprawdzanie argumentów, warunków wstępnych / następczych w metodach, a następnie dodawanie testów jednostkowych wszystkich w celu uchwycenia bieżącej funkcjonalności źródeł. Gdy już to zrobisz, będziesz lepiej przygotowany do ponownego uwzględnienia kodu, ponieważ pojawią się komunikaty i błędy wyskakujące z ostrzeżeniem, jeśli coś zapomniałeś.
Czasami jednak czasami refaktoryzacja może przynieść więcej bólu niż korzyści. Wtedy może być lepiej pozostawić oryginalny projekt w stanie pseudoobsługowym i zacząć od zera, a następnie stopniowo dodawać funkcjonalność od bestii.
źródło
Nie powinieneś zajmować się zmniejszaniem rozmiaru pliku, ale raczej zmniejszaniem rozmiaru klasy. Sprowadza się to do prawie tego samego, ale sprawia, że patrzysz na problem z innej perspektywy (jak sugeruje @Brian Rasmussen , twoja klasa wydaje się mieć wiele obowiązków).
źródło
To, co masz, to klasyczny przykład znanego antipatternu projektowego o nazwie kropelka . Poświęć trochę czasu na przeczytanie artykułu, który tu wskazuję, a może znajdziesz coś przydatnego. Poza tym, jeśli ten projekt jest tak duży, jak wygląda, powinieneś rozważyć projekt, aby zapobiec rozrastaniu się kodu, którego nie można kontrolować.
źródło
To nie jest odpowiedź na duży problem, ale teoretyczne rozwiązanie konkretnego jego fragmentu:
Dowiedz się, gdzie chcesz podzielić duży plik na podfile. Umieść komentarze w jakimś specjalnym formacie w każdym z tych punktów.
Napisz dość trywialny skrypt, który w tych punktach podzieli plik na podfile. (Być może specjalne komentarze mają osadzone nazwy plików, które skrypt może wykorzystać jako instrukcje dotyczące podziału). Powinien zachować komentarze w ramach podziału.
Uruchom skrypt. Usuń oryginalny plik.
Kiedy potrzebujesz scalić z gałęzi, najpierw odtwórz duży plik, łącząc elementy z powrotem, wykonaj scalenie, a następnie ponownie podziel.
Ponadto, jeśli chcesz zachować historię plików SCC, spodziewam się, że najlepszym sposobem, aby to zrobić, jest poinformowanie systemu kontroli źródła, że poszczególne pliki części są kopiami oryginału. Następnie zachowa historię sekcji, które były przechowywane w tym pliku, chociaż oczywiście zapisze również, że duże części zostały „usunięte”.
źródło
Jednym ze sposobów na podzielenie go bez zbytniego niebezpieczeństwa byłoby historyczne spojrzenie na wszystkie zmiany linii. Czy niektóre funkcje są bardziej stabilne niż inne? Gorące punkty zmian, jeśli chcesz.
Jeśli linia nie została zmieniona przez kilka lat, możesz prawdopodobnie przenieść ją do innego pliku bez większego zmartwienia. Rzuciłbym okiem na źródło opatrzone adnotacją ostatniej wersji, która dotknęła daną linię, i sprawdziłam, czy są jakieś funkcje, które można wyciągnąć.
źródło
Wow, brzmi świetnie. Myślę, że warto wyjaśnić swojemu szefowi, że potrzeba dużo czasu na refaktoryzację bestii. Jeśli się nie zgadza, rezygnacja jest opcją.
W każdym razie to, co sugeruję, to w zasadzie wyrzucenie całej implementacji i zgrupowanie jej w nowe moduły, nazwijmy te „usługi globalne”. „Moduł główny” przekaże tylko te usługi i ŻADNY nowy kod, który napiszesz, użyje ich zamiast „modułu głównego”. Powinno to być wykonalne w rozsądnym czasie (ponieważ jest to głównie kopiowanie i wklejanie), nie psujesz istniejącego kodu i możesz to zrobić po jednej wersji serwisowej na raz. A jeśli nadal masz czas, możesz poświęcić go na refaktoryzację wszystkich starych, zależnych modułów, aby korzystać z usług globalnych.
źródło
Moje współczucia - w mojej poprzedniej pracy spotkałem się z podobną sytuacją z plikiem, który był kilka razy większy niż ten, z którym masz do czynienia. Rozwiązaniem było:
Klasy zbudowane w kroku 3. iteracje prawdopodobnie wzrosną, aby pochłonąć więcej kodu, który jest odpowiedni dla ich nowo wyczyszczonej funkcji.
Mógłbym również dodać:
0: kup książkę Michaela Feathersa na temat pracy ze starszym kodem
Niestety ten rodzaj pracy jest zbyt powszechny, ale z mojego doświadczenia wynika, że wielką zaletą jest sprawienie, aby działający, ale okropny kod był coraz mniej okropny przy jednoczesnym utrzymaniu jego działania.
źródło
Zastanów się, w jaki sposób przepisać całą aplikację w bardziej rozsądny sposób. Może przepisać małą jego część jako prototyp, aby sprawdzić, czy Twój pomysł jest wykonalny.
Jeśli udało Ci się znaleźć realne rozwiązanie, odpowiednio zmodyfikuj aplikację.
Jeśli wszystkie próby stworzenia bardziej racjonalnej architektury zawiodą, to przynajmniej wiesz, że rozwiązaniem jest prawdopodobnie przedefiniowanie funkcjonalności programu.
źródło
Moje 0,05 eurocenta:
Przeprojektuj cały bałagan, podziel go na podsystemy, biorąc pod uwagę wymagania techniczne i biznesowe (= wiele równoległych ścieżek konserwacji z potencjalnie różną bazą kodu dla każdego, oczywiście istnieje potrzeba dużej modyfikacji itp.).
Przy podziale na podsystemy przeanalizuj miejsca, które uległy największej zmianie, i oddziel je od niezmiennych części. Powinno to pokazać problemy. Oddziel najbardziej zmieniające się części do ich własnych modułów (np. Dll) w taki sposób, aby interfejs API modułu mógł być nienaruszony i nie trzeba cały czas łamać BC. W ten sposób możesz wdrożyć różne wersje modułu dla różnych gałęzi konserwacji, jeśli to konieczne, bez zmiany rdzenia.
Przeprojektowanie prawdopodobnie będzie musiało być osobnym projektem, próba zrobienia tego z ruchomym celem nie będzie działać.
Co do historii kodu źródłowego, moim zdaniem: zapomnij o nowym kodzie. Ale przechowuj gdzieś historię, aby w razie potrzeby ją sprawdzić. Założę się, że na początku nie będziesz go potrzebować.
Najprawdopodobniej musisz uzyskać wpisowe do zarządzania dla tego projektu. Być może możesz kłócić się z szybszym czasem programowania, mniej błędów, łatwiejszym utrzymaniem i mniejszym chaosem. Coś w stylu „Proaktywnie zapewniamy odporność i żywotność naszych kluczowych zasobów oprogramowania” :)
W ten sposób zacznę przynajmniej rozwiązywać ten problem.
źródło
Zacznij od dodania do niego komentarzy. W odniesieniu do tego, gdzie wywoływane są funkcje i czy można przenosić różne rzeczy. To może wprawić sprawy w ruch. Naprawdę musisz ocenić, jak kruchy jest jego kod. Następnie przenieś wspólne elementy funkcjonalności. Małe zmiany na raz.
źródło
Kolejną książką, która może Cię zainteresować / jest pomocna, jest Refaktoryzacja .
źródło
Coś, co uważam za przydatne do zrobienia (i robię to teraz, chociaż nie na skalę, z którą się zmierzysz), to wyodrębnienie metod jako klas (refaktoryzacja obiektów metod). Metody różniące się w zależności od wersji staną się różnymi klasami, które można wstrzyknąć do wspólnej bazy w celu zapewnienia różnych potrzebnych zachowań.
źródło
Uznałem to zdanie za najciekawszą część twojego postu:
> Plik jest używany i aktywnie zmieniany w kilku (> 10) wersjach konserwacyjnych naszego produktu, więc naprawdę trudno jest go refaktoryzować
Po pierwsze, zalecałbym użycie systemu kontroli źródła do opracowania tych wersji serwisowych 10+, które obsługują rozgałęzianie.
Po drugie, stworzyłbym dziesięć oddziałów (po jednym dla każdej wersji konserwacji).
Czuję, że już się kulisz! Ale albo kontrola źródła nie działa w twojej sytuacji z powodu braku funkcji lub nie jest używana poprawnie.
Teraz przejdź do gałęzi, nad którą pracujesz - przerób ją według własnego uznania, mając pewność, że nie zakłócisz pozostałych dziewięciu gałęzi produktu.
Byłbym trochę zaniepokojony, że masz tak wiele w swojej funkcji main ().
We wszystkich projektach, które piszę, używałbym main () tylko do inicjowania podstawowych obiektów - takich jak obiekt symulacji lub aplikacji - te klasy powinny być kontynuowane.
Zainicjowałbym również obiekt rejestrowania aplikacji, który będzie używany globalnie w całym programie.
Wreszcie, w głównej części dodaję także kod wykrywania wycieków w blokach preprocesora, który zapewnia, że jest włączony tylko w kompilacjach DEBUG. To wszystko, co dodałbym do main (). Main () powinien być krótki!
Mówisz tak
> Plik zawiera w zasadzie „główną klasę” (główne wysyłanie i koordynowanie pracy wewnętrznej) naszego programu
Wygląda na to, że te dwa zadania można podzielić na dwa osobne obiekty - koordynatora i dyspozytora pracy.
Po ich podzieleniu możesz zepsuć „przepływ pracy SCC”, ale wygląda na to, że ścisłe przestrzeganie przepływu pracy SCC powoduje problemy z konserwacją oprogramowania. Porzuć to teraz i nie oglądaj się za siebie, bo jak tylko to naprawisz, zaczniesz spać spokojnie.
Jeśli nie jesteś w stanie podjąć decyzji, walcz o nią z menedżerem - z tego powodu aplikacja musi zostać ponownie rozpatrzona - i to źle! Nie przejmuj się odpowiedzią!
źródło
Jak już to opisałeś, głównym problemem jest różnica między podziałem przed i po podziale, łączenie poprawek błędów itp. Narzędzie wokół niego. Nie zajmie to dużo czasu, aby na stałe zakodować skrypt w Perlu, Ruby itp., Aby wydrzeć większość szumu wynikającego z różnic przed podziałem przed połączeniem po podziale. Rób wszystko, co jest najłatwiejsze pod względem obsługi hałasu:
Możesz nawet sprawić, że za każdym razem, gdy nastąpi zameldowanie, rozpocznie się konkatenacja i masz coś przygotowanego do odróżnienia się od wersji z jednym plikiem.
źródło
źródło
„Plik zawiera w zasadzie„ główną klasę ”(główne wysyłanie i koordynowanie pracy wewnętrznej) naszego programu, więc za każdym razem, gdy dodawana jest funkcja, wpływa ona również na ten plik i za każdym razem, gdy rośnie.”
Jeśli ten duży PRZEŁĄCZNIK (który moim zdaniem istnieje) stanie się głównym problemem związanym z konserwacją, możesz go przeredagować, aby używał słownika i wzorca poleceń, i usunąć całą logikę przełączania z istniejącego kodu do modułu ładującego, który wypełnia tę mapę, tj .:
źródło
Myślę, że najłatwiejszym sposobem śledzenia historii źródła podczas dzielenia pliku byłoby coś takiego:
źródło
Myślę, że to, co zrobiłbym w tej sytuacji, to ugryzienie i:
Śledzenie starych zmian w pliku jest po prostu rozwiązywane przez pierwszy komentarz do odprawy, mówiąc coś w stylu „split from mainmodule.cpp”. Jeśli musisz wrócić do czegoś nowego, większość ludzi pamięta zmianę, jeśli minie 2 lata, komentarz powie im, gdzie szukać. Oczywiście, jak cenne będzie cofnięcie się o ponad 2 lata i sprawdzenie, kto zmienił kod i dlaczego?
źródło