Projekt na dużą skalę w Haskell? [Zamknięte]


565

Jaki jest dobry sposób projektowania / struktury dużych programów funkcjonalnych, zwłaszcza w Haskell?

Przeszedłem kilka samouczków (Napisz do mnie jako ulubiony program, a Real World Haskell jest na drugim miejscu) - ale większość programów jest stosunkowo niewielka i ma jeden cel. Ponadto nie uważam niektórych z nich za szczególnie eleganckie (na przykład obszerne tabele odnośników w WYAS).

Chcę teraz pisać większe programy z większą ilością ruchomych części - pozyskiwanie danych z różnych źródeł, czyszczenie, przetwarzanie na różne sposoby, wyświetlanie ich w interfejsach użytkownika, utrwalanie, komunikowanie się w sieci itp. W jaki sposób jedna najlepsza struktura takiego kodu, aby była czytelna, łatwa w utrzymaniu i dostosowywana do zmieniających się wymagań?

Istnieje dość duża literatura zajmująca się tymi pytaniami dla dużych programów imperatywnych zorientowanych obiektowo. Pomysły takie jak MVC, wzorce projektowe itp. Są przyzwoitymi receptami na realizację ogólnych celów, takich jak rozdzielenie obaw i ponowne wykorzystanie w stylu OO. Ponadto nowsze języki rozkazujące nadają się do refaktoryzacji w stylu „projektu w miarę dorastania”, do którego moim zdaniem nowicjusz Haskell wydaje się mniej odpowiedni.

Czy istnieje odpowiednik literatury dla Haskell? W jaki sposób najlepiej wykorzystać do tego celu zoo egzotycznych struktur kontrolnych w programowaniu funkcjonalnym (monady, strzały, aplikacje itp.)? Jakie najlepsze praktyki możesz polecić?

Dzięki!

EDYCJA (jest to kontynuacja odpowiedzi Dona Stewarta):

@dons wspomniał: „Monady przechwytują kluczowe projekty architektoniczne rodzajów”.

Myślę, że moje pytanie brzmi: jak myśleć o kluczowych projektach architektonicznych w czysto funkcjonalnym języku?

Rozważ przykład kilku strumieni danych i kilku etapów przetwarzania. Potrafię pisać modułowe parsery dla strumieni danych do zestawu struktur danych, a każdy etap przetwarzania mogę zaimplementować jako czystą funkcję. Kroki przetwarzania wymagane dla jednego elementu danych będą zależeć od jego wartości i innych ”. Po niektórych krokach powinny pojawić się skutki uboczne, takie jak aktualizacje GUI lub zapytania do bazy danych.

Jaki jest „właściwy” sposób, aby ładnie powiązać dane i kroki analizy? Można napisać dużą funkcję, która robi właściwe dla różnych typów danych. Lub można użyć monady, aby śledzić, co zostało przetworzone do tej pory i aby każdy krok przetwarzania uzyskał to, czego potrzebuje dalej od stanu monady. Lub można napisać w dużej mierze osobne programy i wysyłać wiadomości (nie podoba mi się ta opcja).

Slajdy, które podłączył, mają punkt Rzeczy, których potrzebujemy: „Idiomy do mapowania projektu na typy / funkcje / klasy / monady”. Jakie są idiomy? :)


9
Myślę, że podstawową ideą podczas pisania dużych programów w języku funkcjonalnym są małe, wyspecjalizowane i bezpaństwowe moduły komunikujące się poprzez przekazywanie wiadomości . Oczywiście musisz trochę udawać, ponieważ prawdziwy program potrzebuje stanu. Myślę, że tutaj F # świeci nad Haskellem.
ChaosPandion,

18
@Chaos, ale tylko Haskell domyślnie wymusza bezpaństwowość. Nie masz wyboru i musisz ciężko pracować, aby wprowadzić stan (aby przełamać kompozycję) w Haskell :-)
Don Stewart

7
@ChaosPandion: Teoretycznie nie zgadzam się. Z pewnością w imperatywnym języku (lub funkcjonalnym zaprojektowanym z myślą o przekazywaniu wiadomości) może to być właśnie to, co bym zrobił. Ale Haskell ma inne sposoby radzenia sobie z państwem i być może pozwalają mi zachować więcej „czystych” korzyści.
Dan

1
Trochę o tym napisałem pod „Wytycznymi projektowymi” w tym dokumencie: community.haskell.org/~ndm/downloads/…
Neil Mitchell,

5
@JonHarrop nie zapominajmy, że chociaż MLOC jest dobrą miarą przy porównywaniu projektów w podobnych językach, nie ma sensu porównywanie między językami, szczególnie w językach takich jak Haskell, gdzie ponowne użycie kodu i modułowość jest znacznie łatwiejsza i bezpieczniejsza w porównaniu do niektórych języków.
Tair

Odpowiedzi:


519

Mówię o tym trochę w Inżynierii dużych projektów w Haskell oraz w projektowaniu i wdrażaniu XMonad. Inżynieria w dużej mierze polega na zarządzaniu złożonością. Podstawowymi mechanizmami strukturyzacji kodu w Haskell do zarządzania złożonością są:

System typów

  • Użyj systemu typów, aby wymusić abstrakcje, upraszczając interakcje.
  • Wymuszaj kluczowe niezmienniki poprzez typy
    • (np. że niektóre wartości nie mogą uciec przed pewnym zakresem)
    • Ten określony kod nie ma IO, nie dotyka dysku
  • Egzekwuj bezpieczeństwo: sprawdzone wyjątki (Może / Albo), unikaj mieszania pojęć (Słowo, Int, Adres)
  • Dobre struktury danych (takie jak zamki błyskawiczne) mogą sprawić, że niektóre klasy testowania nie będą potrzebne, ponieważ wykluczają np. Błędy wykraczające poza granice.

Profiler

  • Podaj obiektywne dowody na stertę programu i profile czasowe.
  • W szczególności profilowanie sterty jest najlepszym sposobem na zapewnienie niepotrzebnego użycia pamięci.

Czystość

  • Znacząco zmniejsz złożoność, usuwając stan. Czysto funkcjonalny kod jest skalowany, ponieważ jest kompozycyjny. Wszystko czego potrzebujesz to typ, aby określić, jak użyć jakiegoś kodu - nie zmieni się on tajemniczo, gdy zmienisz inną część programu.
  • Używaj wielu programów w stylu „model / widok / kontroler”: parsuj dane zewnętrzne tak szybko, jak to możliwe, w czysto funkcjonalne struktury danych, działaj na tych strukturach, a następnie, gdy cała praca zostanie wykonana, renderuj / opróżnij / serializuj. Utrzymuje większość kodu w czystości

Testowanie

  • QuickCheck + Pokrycie kodu Haskell, aby upewnić się, że testujesz rzeczy, których nie możesz sprawdzić typami.
  • GHC + RTS jest świetny do sprawdzania, czy spędzasz zbyt dużo czasu na GC.
  • QuickCheck może również pomóc w identyfikacji czystych, ortogonalnych interfejsów API dla modułów. Jeśli właściwości kodu są trudne do określenia, prawdopodobnie są zbyt skomplikowane. Kontynuuj refaktoryzację, aż uzyskasz czysty zestaw właściwości, które mogą przetestować Twój kod, które dobrze komponują. Zatem kod jest prawdopodobnie również dobrze zaprojektowany.

Monady dla strukturyzacji

  • Monady przechwytują kluczowe projekty architektoniczne w różnych typach (ten kod zapewnia dostęp do sprzętu, ten kod jest sesją dla jednego użytkownika itp.)
  • Np. Monada X w Xmonadzie dokładnie uchwyca projekt tego, który stan jest widoczny dla jakich elementów systemu.

Klasy typów i typy egzystencjalne

  • Użyj klas typów, aby zapewnić abstrakcję: ukryj implementacje za interfejsami polimorficznymi.

Współbieżność i równoległość

  • Wkradnij się pardo swojego programu, aby pokonać konkurencję za pomocą łatwego, składanego równoległości.

Refaktor

  • W Haskell można dużo refaktoryzować . Typy zapewniają, że Twoje zmiany na dużą skalę będą bezpieczne, jeśli używasz typów mądrze. Pomoże to skalować bazę kodu. Upewnij się, że refaktoryzacja spowoduje błędy typu aż do zakończenia.

Używaj FFI mądrze

  • FFI ułatwia grę z obcym kodem, ale ten obcy kod może być niebezpieczny.
  • Zachowaj ostrożność w założeniach dotyczących kształtu zwracanych danych.

Programowanie meta

  • Trochę szablonu Haskell lub ogólnych może usunąć płytkę kotłową.

Pakowanie i dystrybucja

  • Użyj Cabal. Nie rzucaj własnym systemem kompilacji. (EDYCJA: Właściwie prawdopodobnie chcesz użyć Stack teraz, aby rozpocząć.).
  • Użyj Haddock dla dobrych dokumentów API
  • Narzędzia takie jak graphmod mogą wyświetlać struktury modułów.
  • Jeśli to możliwe, polegaj na wersjach bibliotek i narzędzi platformy Haskell. Jest to stabilna podstawa. (EDYCJA: Ponownie, w dzisiejszych czasach prawdopodobnie będziesz chciał użyć Stack do uruchomienia stabilnej bazy.)

Ostrzeżenia

  • Służy -Walldo utrzymywania kodu w czystości od zapachów. Możesz również spojrzeć na Agdę, Isabelle lub Catch, aby uzyskać więcej pewności. Aby sprawdzić, jak strzępią się, zobacz świetną wskazówkę , która zasugeruje ulepszenia.

Dzięki tym wszystkim narzędziom możesz kontrolować złożoność, usuwając możliwie jak najwięcej interakcji między komponentami. Idealnie, masz bardzo dużą bazę czystego kodu, który jest naprawdę łatwy w utrzymaniu, ponieważ jest kompozycyjny. Nie zawsze jest to możliwe, ale warto dążyć.

Ogólnie: rozłóż jednostki logiczne systemu na możliwie najmniejsze komponenty referencyjne przezroczyste, a następnie zaimplementuj je w modułach. Globalne lub lokalne środowiska dla zestawów komponentów (lub komponentów wewnętrznych) mogą być mapowane na monady. Użyj algebraicznych typów danych, aby opisać podstawowe struktury danych. Podziel się tymi definicjami szeroko.


8
Dzięki Don, twoja odpowiedź jest doskonała - wszystkie są cennymi wskazówkami i będę się do nich regularnie odnosił. Wydaje mi się jednak, że moje pytanie pojawia się o krok, zanim trzeba będzie tego wszystkiego. To, co naprawdę chciałbym wiedzieć, to „Idiomy mapowania projektu na typy / funkcje / klasy / monady” ... Mógłbym spróbować wymyślić własne, ale miałem nadzieję, że gdzieś może być destylowany zestaw najlepszych praktyk - a jeśli nie, zalecenia dotyczące dobrze ustrukturyzowanego kodu do odczytu systemu o dużym formacie (w przeciwieństwie do, powiedzmy, skoncentrowanej biblioteki). Zredagowałem swój post, aby zadać to samo pytanie bardziej bezpośrednio.
Dan

6
Dodałem trochę tekstu o rozkładzie projektu do modułów. Twoim celem jest identyfikacja logicznie powiązanych funkcji w moduły, które mają referencyjnie przezroczyste interfejsy z innymi częściami systemu, i jak najszybsze wykorzystanie, w miarę możliwości, czysto funkcjonalnych typów danych do bezpiecznego modelowania świata zewnętrznego. Dokument projektowy xmonad obejmuje wiele z tego: xmonad.wordpress.com/2009/09/09/...
Don Stewart

3
Próbowałem pobrać slajdy z wykładu Inżynieria dużych projektów w Haskell , ale link wyglądał na zepsuty. Oto działający: galois.com/~dons/talks/dons-londonhug-decade.pdf
mik01aj

3
Udało mi się znaleźć nowy link do pobrania: pau-za.cz/data/2/sprava.pdf
Riccardo T.,

3
@Heather Mimo że link do pobrania na stronie, o której wspomniałem wcześniej w komentarzu, nie działa, wygląda na to, że slajdy można nadal oglądać na scribd
Riccardo T.

118

Don przedstawił ci większość powyższych szczegółów, ale oto moje dwa centy za zrobienie naprawdę drobiazgowych programów stanowych, takich jak demony systemowe w Haskell.

  1. W końcu żyjesz w stosie transformatorów monadowych. Na dole jest IO. Co więcej, każdy główny moduł (w sensie abstrakcyjnym, nie w sensie moduł w pliku) mapuje swój niezbędny stan na warstwę w tym stosie. Jeśli więc masz kod połączenia z bazą danych ukryty w module, piszesz go, aby był nad typem MonadReader Connection m => ... -> m ... a wtedy funkcje bazy danych zawsze mogą uzyskać połączenie bez funkcji innych moduły muszą być świadome jego istnienia. Możesz skończyć z jedną warstwą przenoszącą połączenie z bazą danych, inną konfiguracją, trzecią różnymi semaforami i mvarami do rozwiązywania równoległości i synchronizacji, inną obsługą plików dziennika itp.

  2. Najpierw sprawdź, jak radzisz sobie z błędami . W chwili obecnej największą słabością dla Haskell w większych systemach jest mnóstwo metod obsługi błędów, w tym kiepskich, takich jak Może (co jest złe, ponieważ nie można zwrócić żadnych informacji o tym, co poszło źle; zawsze używaj albo Zamiast, może, chyba że naprawdę oznacza tylko brakujące wartości). Dowiedz się, jak to zrobić w pierwszej kolejności, i skonfiguruj adaptery z różnych mechanizmów obsługi błędów używanych przez biblioteki i inny kod w ostatecznym. Oszczędzi ci to później świata smutku.

Dodatek (wyodrębniony z komentarzy; dzięki Lii i liminalisht ) -
więcej dyskusji na temat różnych sposobów krojenia dużego programu w monady na stosie:

Ben Kolera daje świetne praktyczne wprowadzenie do tego tematu, a Brian Hurt omawia rozwiązania problemu wprowadzania liftakcji monadycznych w niestandardową monadę. George Wilson pokazuje, jak mtlpisać kod, który działa z dowolną monadą, która implementuje wymagane typy czcionek, a nie z niestandardowym typem monady. Carlo Hamalainen napisał kilka przydatnych notatek podsumowujących przemówienie George'a.


5
Dwie dobre strony! Ta odpowiedź ma tę zaletę, że jest dość konkretna, czymś, czego inne nie są. Byłoby interesujące przeczytać więcej dyskusji na temat różnych sposobów krojenia dużego programu w monady w stosie. Proszę zamieścić linki do takich artykułów, jeśli je masz!
Lii,

6
@Lii Ben Kolera daje świetne praktyczne wprowadzenie do tego tematu, a Brian Hurt omawia rozwiązania problemu wprowadzania liftakcji monadycznych w niestandardową monadę. George Wilson pokazuje, jak mtlpisać kod, który działa z dowolną monadą, która implementuje wymagane typy czcionek, a nie z niestandardowym typem monady. Carlo Hamalainen napisał kilka przydatnych notatek podsumowujących przemówienie George'a.
liminalisht

Zgadzam się, że stosy transformatorów monadowych są zwykle kluczowymi fundamentami architektonicznymi, ale bardzo staram się nie dopuszczać do nich wejścia-wyjścia. Nie zawsze jest to możliwe, ale jeśli pomyślisz o tym, co oznacza „a potem” w swojej monadzie, możesz odkryć, że naprawdę masz kontynuację lub automat gdzieś na dole, który można następnie zinterpretować w IO za pomocą funkcji „run”.
Paul Johnson

Jak już zauważył @PaulJohnson, to podejście Monad Transformer Stack wydaje się być w konflikcie z wzorcem projektowym ReaderT
McBear Holden

43

Projektowanie dużych programów w Haskell nie różni się tak bardzo od robienia tego w innych językach. Programowanie w dużej mierze polega na rozbiciu problemu na możliwe do opanowania części i na tym, jak je połączyć; język implementacji jest mniej ważny.

To powiedziawszy, w dużym projekcie miło jest wypróbować system typów, aby upewnić się, że możesz dopasować swoje elementy tylko w prawidłowy sposób. Może to obejmować typy nowego typu lub fantomowe, aby sprawiać wrażenie, że rzeczy tego samego typu są różne.

Jeśli chodzi o refaktoryzację kodu w miarę postępów, czystość jest wielkim dobrodziejstwem, więc staraj się zachować jak najwięcej kodu w czystości. Czysty kod jest łatwy do refaktoryzacji, ponieważ nie ma ukrytej interakcji z innymi częściami twojego programu.


14
W rzeczywistości stwierdziłem, że refaktoryzacja jest dość frustrująca, jeśli typy danych wymagają zmiany. Wymaga to żmudnej modyfikacji arsenału wielu konstruktorów i dopasowywania wzorców. (Zgadzam się, że przekształcanie czystych funkcji w inne czyste funkcje tego samego typu jest łatwe - pod warunkiem, że nie dotkniesz typów danych)
Dan

2
@ Dan Możesz uciec całkowicie za darmo z mniejszymi zmianami (np. Dodaniem pola) podczas korzystania z rekordów. Niektórzy mogą chcieć, aby zapisy stały się nawykiem (jestem jednym z nich ^^ ").
MasterMastic

5
@Czy mam na myśli, że jeśli zmienisz typ danych funkcji w dowolnym języku, nie musisz robić tego samego? Nie rozumiem, jak język taki jak Java lub C ++ pomógłby ci w tym zakresie. Jeśli powiesz, że możesz użyć jakiegoś wspólnego interfejsu, którego przestrzegają oba typy, to powinieneś był to zrobić z klasami w Haskell.
średnikiem

4
@semicon różnica dla języków takich jak Java polega na istnieniu dojrzałych, dobrze przetestowanych iw pełni zautomatyzowanych narzędzi do refaktoryzacji. Ogólnie rzecz biorąc, narzędzia te mają fantastyczną integrację z edytorem i zabierają ogrom żmudnej pracy związanej z refaktoryzacją. Haskell daje nam genialny system do wykrywania rzeczy, które należy zmienić w refaktoryzacji, ale narzędzia do faktycznego przeprowadzenia refaktoryzacji są (obecnie) bardzo ograniczone, szczególnie w porównaniu z tym, co było już dostępne w Javie ekosystem od ponad 10 lat.
jsk

16

Dzięki tej książce nauczyłem się strukturalnego programowania funkcjonalnego po raz pierwszy . Być może nie jest to dokładnie to, czego szukasz, ale dla początkujących w programowaniu funkcjonalnym może to być jeden z najlepszych pierwszych kroków do nauki konstruowania programów funkcjonalnych - niezależnie od skali. Na wszystkich poziomach abstrakcji projekt powinien zawsze mieć jasno ułożone struktury.

Rzemiosło programowania funkcjonalnego

Rzemiosło programowania funkcjonalnego

http://www.cs.kent.ac.uk/people/staff/sjt/craft2e/


11
Jakkolwiek Craft of FP jest - nauczyłem się z niego Haskella - jest to tekst wprowadzający dla początkujących programistów , a nie do projektowania dużych systemów w Haskell.
Don Stewart

3
To najlepsza książka, jaką znam na temat projektowania interfejsów API i ukrywania szczegółów implementacji. Dzięki tej książce stałem się lepszym programistą w C ++ - tylko dlatego, że nauczyłem się lepszych sposobów organizacji mojego kodu. Cóż, twoje doświadczenie (i odpowiedź) jest z pewnością lepsze niż ta książka, ale Dan prawdopodobnie nadal będzie początkującym w Haskell. ( where beginner=do write $ tutorials `about` Monads)
comonad

11

Obecnie piszę książkę o tytule „Projektowanie funkcjonalne i architektura”. Zapewnia pełny zestaw technik tworzenia dużej aplikacji przy użyciu czysto funkcjonalnego podejścia. Opisuje wiele funkcjonalnych wzorów i pomysłów podczas budowania podobnej do SCADA aplikacji „Andromeda” do kontrolowania statków kosmicznych od zera. Moim podstawowym językiem jest Haskell. Książka obejmuje:

  • Podejścia do modelowania architektury za pomocą diagramów;
  • Analiza wymagań;
  • Wbudowane modelowanie domen DSL;
  • Zewnętrzny projekt i wdrożenie DSL;
  • Monady jako podsystemy z efektami;
  • Darmowe monady jako interfejsy funkcjonalne;
  • Arrowised eDSL;
  • Odwrócenie kontroli przy użyciu wolnych monadycznych eDSL;
  • Pamięć transakcyjna oprogramowania;
  • Soczewki;
  • Monady State, Reader, Writer, RWS, ST;
  • Zanieczyszczony stan: IORef, MVar, STM;
  • Wielowątkowość i jednoczesne modelowanie domen;
  • GUI;
  • Możliwość zastosowania głównych technik i podejść, takich jak UML, SOLID, GRASP;
  • Interakcja z nieczystymi podsystemami.

Można zapoznać się z kodem do książki tutaj , a „Andromeda” kod projektu.

Spodziewam się, że ukończę tę książkę pod koniec 2017 roku. Do tego czasu możesz przeczytać mój artykuł „Projektowanie i architektura w programowaniu funkcjonalnym” (Rus) tutaj .

AKTUALIZACJA

Udostępniłem moją książkę online (pierwsze 5 rozdziałów). Zobacz post na Reddit


Alexander, czy możesz uprzejmie zaktualizować tę notatkę po ukończeniu rezerwacji, abyśmy mogli ją śledzić. Twoje zdrowie.
Max

4
Pewnie! Na razie skończyłem połowę tekstu, ale jest to 1/3 całej pracy. Zachowaj więc zainteresowanie, to mnie bardzo inspiruje!
graninas,

2
Cześć! Udostępniłem moją książkę online (tylko pierwsze 5 rozdziałów). Zobacz post na Reddit: reddit.com/r/haskell/comments/6ck72h/…
graninas

dzięki za udostępnienie i pracę!
Maks

Naprawdę nie mogę się doczekać!
patriques

7

Warto wspomnieć o blogu Gabriela Skalowalne architektury programów .

Wzorce projektowe Haskell różnią się od wzorców projektowych głównego nurtu pod jednym ważnym względem:

  • Konwencjonalna architektura : Połącz kilka komponentów razem typu A, aby wygenerować „sieć” lub „topologię” typu B.

  • Architektura Haskell : Połącz ze sobą kilka komponentów typu A, aby wygenerować nowy komponent tego samego typu A, którego charakter nie różni się od jego podstawników

Często uderza mnie, że pozornie elegancka architektura często wypada z bibliotek, które wykazują to miłe poczucie jednorodności, w sposób oddolny. W Haskell jest to szczególnie widoczne - wzorce, które tradycyjnie byłyby uważane za „architekturę odgórną”, są zwykle rejestrowane w bibliotekach takich jak odgórną mvc , Netwire i Cloud Haskell . To znaczy, mam nadzieję, że ta odpowiedź nie będzie interpretowana jako próba zastąpienia innych w tym wątku, tylko że wybory strukturalne mogą i powinny być idealnie oderwane w bibliotekach przez ekspertów z dziedziny. Moim zdaniem prawdziwą trudnością w budowaniu dużych systemów jest ocena tych bibliotek pod kątem ich „dobroci” architektonicznej w porównaniu do wszystkich twoich pragmatycznych obaw.

Jak liminalisht wspomina w komentarzach, Wzorzec projektowania kategorii jest kolejnym postem Gabriela na ten temat, w podobnym tonie.


3
Wspomniałbym o innym poście Gabriela Gonzaleza na temat wzoru projektu kategorii . Jego podstawowym argumentem jest to, że to, co my, programiści funkcjonalni, uważamy za „dobrą architekturę”, jest tak naprawdę „architekturą kompozycyjną” - projektuje programy przy użyciu elementów, które na pewno mają komponować. Ponieważ prawa kategorii gwarantują, że tożsamość i asocjatywność są zachowywane w ramach kompozycji, architektura kompozycyjna jest osiągana poprzez zastosowanie abstrakcji, do których mamy kategorię - np. Czyste funkcje, działania monadyczne, potoki itp.
liminalisht


3

Być może najpierw musisz cofnąć się o krok i pomyśleć, jak przełożyć opis problemu na projekt. Ponieważ Haskell ma tak wysoki poziom, może uchwycić opis problemu w postaci struktur danych, działań jako procedur i czystej transformacji jako funkcji. Masz projekt. Programowanie rozpoczyna się od skompilowania tego kodu i znalezienia w kodzie konkretnych błędów dotyczących brakujących pól, brakujących instancji i brakujących transformatorów monadycznych, ponieważ na przykład wykonujesz dostęp do bazy danych z biblioteki, która potrzebuje określonej monady stanu w ramach procedury IO. I voila, jest program. Kompilator karmi twoje szkice mentalne i zapewnia spójność projektowania i rozwoju.

W ten sposób od samego początku korzystasz z pomocy Haskell, a kodowanie jest naturalne. Nie chciałbym robić czegoś „funkcjonalnego”, „czystego” lub wystarczająco ogólnego, jeśli to, co masz na myśli, jest konkretnym zwykłym problemem. Myślę, że nadmiar inżynierii jest najbardziej niebezpieczną rzeczą w IT. Sytuacja wygląda inaczej, gdy problemem jest stworzenie biblioteki, która wyodrębnia zestaw powiązanych problemów.

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.