Czy istnieje korelacja między skalą projektu a surowością języka?

72

Wyjaśniając mojej koledze różnicę między ścisłością języków i paradygmatów, stwierdziłem, że:

  • Języki tolerancyjne, takie jak języki dynamiczne i interpretowane, są najlepiej stosowane w prototypach i małych projektach lub średnich aplikacjach internetowych. Przy wyborze eleganckich dynamicznych języków, takich jak Python lub JavaScript z Node.js, korzyści są następujące:

    1. Szybki rozwój,

    2. Zredukowany kod płyty kotła,

    3. Możliwość przyciągnięcia młodych, kreatywnych programistów, którzy uciekają przed „językami korporacyjnymi”,   takimi jak Java.

  • Języki o typie statycznym / kompilowane najlepiej nadają się do aplikacji wymagających większej rygorystyczności, takich jak aplikacje o znaczeniu krytycznym lub aplikacje do aplikacji średnich i dużych.

    1. Dobrze znane paradygmaty i wzorce opracowane przez dziesięciolecia,

    2. Łatwość kontroli statycznej,

    3. Możliwość znalezienia wielu profesjonalnych programistów z wieloletnim doświadczeniem.

  • Ścisłe języki, takie jak Haskell, Ada lub techniki, takie jak kontrakty kodowe w języku C #, są lepsze dla systemów, które preferują bezpieczeństwo nad elastycznością (nawet jeśli Haskell może być wyjątkowo elastyczny), takich jak systemy i systemy krytyczne dla życia, które powinny być wyjątkowo stabilne. Korzyści to:

    1. Możliwość wykrycia jak największej liczby błędów w czasie kompilacji,

    2. Łatwość kontroli statycznej,

    3. Łatwość formalnych dowodów.

Jednak patrząc na języki i technologie wykorzystywane w dużych projektach przez duże korporacje, wydaje się, że moje twierdzenie jest błędne . Na przykład Python jest z powodzeniem stosowany w dużych systemach, takich jak YouTube lub inne aplikacje Google, które wymagają dużej rygorystyczności.

Czy nadal istnieje korelacja między skalą projektu a surowością języka / paradygmatu, który należy zastosować?

Czy jest jeszcze trzeci czynnik, o którym zapomniałem wziąć pod uwagę?

Gdzie się mylę?

Arseni Mourzenko
źródło
12
Ścisłe sprawdzanie typów i statyczne sprawdzanie typów to nie to samo. Python jest typowany dynamicznie, ale jest bardziej rygorystyczny niż C. Zaletą statycznego sprawdzania typów nie jest sama w sobie ścisłość, ale że typy są sprawdzane podczas kompilacji, a nie w czasie wykonywania. Zajmowałem się wieloma problemami w C / C ++ w mojej karierze z powodu niejawnego castingu.
Steven Burnap,
5
Prawdopodobnie jest coś do powiedzenia na temat cyklu życia: oprogramowanie, które zaczyna się w pierwszej kategorii, może ewoluować w inne, „przeciągając” nim język.
Mat
11
Jedyną elegancką cechą javascript jest to, że działa w większości przeglądarek.
JeffO
1
@StevenBurnap: Nie mogłem się bardziej zgodzić co do różnicy między statycznym a ścisłym. Java to kolejny punkt w spektrum, ponieważ jest statyczny i zbyt surowy. Programiści często krytykują typowanie statyczne na przykładzie Javy, ale większość tej krytyki powinna być skierowana przeciwko zbyt ścisłemu kompilatorowi Javy , a nie typowemu typowi statycznemu. Wystarczy spojrzeć na Scalę na tej samej maszynie JVM, która jest wpisana statycznie, ale ma znacznie mniej szczegółowy kod ze względu na możliwości wnioskowania o typach fantastycznego kompilatora.
Cornel Masson,
2
„Python z powodzeniem stosuje się w dużych systemach” - jaka jest tutaj definicja „sukcesu”? Że głównie działa i daje jakiś wynik? Czy uwzględniono wymaganą ilość testów i siły roboczej? Co z utrzymaniem?
Den

Odpowiedzi:

39

Ciekawe studium przypadku dotyczące skalowania projektów wykorzystujących dynamiczny i zinterpretowany język można znaleźć w Beginning Scala autorstwa Davida Pollaka.

Zacząłem szukać sposobu na wyrażenie kodu w moim mózgu w prostszy, bardziej bezpośredni sposób. Znalazłem Ruby i Rails. Czułam się wyzwolona. Ruby pozwoliła mi wyrazić koncepcje w znacznie mniejszej liczbie wierszy kodu. Railsy były o wiele łatwiejsze w użyciu niż Spring MVC, Hibernate i inne „usprawnione” frameworki Java. Dzięki Ruby and Rails mogłem wyrazić znacznie więcej tego, co było w mojej głowie w krótszym czasie. To było podobne do wyzwolenia, które odczułem, kiedy przeniosłem się z C ++ na Javę ...

Gdy moje projekty Ruby i Rails wykroczyły poza kilka tysięcy wierszy kodu i gdy dodałem członków zespołu do moich projektów, wyzwania związane z dynamicznymi językami stały się oczywiste.

Spędziliśmy ponad połowę czasu na pisanie testów, a znaczna część wydajności, którą zauważyliśmy, została utracona podczas pisania testów . Większość testów byłaby niepotrzebna w Javie, ponieważ większość z nich miała na celu upewnienie się, że zaktualizowaliśmy program wywołujący, gdy zmieniliśmy kod, zmieniając nazwy metod lub liczby parametrów. Odkryłem również, że praca w zespołach, w których dochodziło do połączenia umysłu między dwoma do czterech członków zespołu, w Ruby wszystko poszło dobrze, ale kiedy próbowaliśmy przyciągnąć nowych członków do zespołu, połączenia mentalne były trudne do przekazania nowym członkom zespołu .

Poszukałem nowego języka i środowiska programistycznego. Szukałem języka, który byłby tak wyrazisty jak Ruby, ale równie bezpieczny i wydajny jak Java ...

Jak widać, głównym wyzwaniem dla autora w skalowaniu projektu było opracowanie testów i transfer wiedzy.

W szczególności autor omawia różnice w pisaniu testowym między językami o typie dynamicznym i statycznym w rozdziale 7. W sekcji „Poignently Killing Bunnies: Dwemthy's Stairs” autor omawia port Scali konkretnego przykładu Ruby:

Dlaczego Lucky Stiff ... wprowadza niektóre koncepcje metaprogramowania Ruby w Dwemthy's Array, w której królik walczy z szeregiem stworzeń. N8han14 zaktualizował przykład do pracy w Scali ...

W porównaniu z kodem Ruby części biblioteki Scala były bardziej złożone. Musieliśmy wykonać wiele pracy, aby upewnić się, że nasze typy są prawidłowe. Musieliśmy ręcznie przepisać właściwości Creature w klasach DupMonster i CreatureCons. To więcej pracy niż method_missing. Musieliśmy również wykonać sporo pracy, aby wesprzeć niezmienność naszych stworzeń i broni.

Z drugiej strony wynik był znacznie potężniejszy niż wersja Ruby. Gdybyśmy musieli napisać testy dla naszego kodu Ruby, aby przetestować to, czego zapewnia nas kompilator Scala, potrzebowalibyśmy o wiele więcej linii kodu. Na przykład możemy być pewni, że nasz Królik nie mógł władać siekierą. Aby uzyskać tę pewność w Ruby, musielibyśmy napisać test, który zapewni, że wywołanie |^Królika nie powiedzie się. Nasza wersja Scala zapewnia, że ​​tylko Broń zdefiniowana dla danego Stworzenia może być używana przez to Stworzenie, co wymagałoby dużo refleksji w środowisku uruchomieniowym w Rubim ...


Czytanie powyżej może sprawić, że w miarę powiększania się projektów pisanie testowe może stać się zbyt kłopotliwe. Takie rozumowanie byłoby błędne, o czym świadczą przykłady udanych bardzo dużych projektów wspomnianych w tym samym pytaniu („Python jest z powodzeniem wykorzystywany do ... YouTube”).

Rzecz w tym, że skalowanie projektów nie jest tak naprawdę proste. Bardzo duże, długowieczne projekty mogą „pozwolić sobie” na inny proces opracowywania testów, z zestawami testowymi jakości produkcyjnej, profesjonalnymi zespołami testerów i innymi ciężkimi materiałami.

Zestawy testów YouTube czy Kit Java Compatibility pewien żyć innym życiem niż testy w małym projekcie samouczek jak Array Dwemthy użytkownika .

komar
źródło
24

Twoje twierdzenie nie jest błędne. Musisz tylko kopać trochę głębiej.

Mówiąc wprost, duże systemy używają wielu języków, a nie tylko jednego języka. Mogą istnieć części zbudowane przy użyciu „ścisłych” języków i mogą istnieć części zbudowane przy użyciu języków dynamicznych.

Jeśli chodzi o twój przykład z Google i YouTube, słyszałem, że używają Pythona przede wszystkim jako „kleju” między różnymi systemami. Tylko Google wie, z czego te systemy są zbudowane, ale założę się, że wiele krytycznych systemów Google jest zbudowanych przy użyciu ścisłych i „korporacyjnych” języków, takich jak C ++ lub Java, a może coś, co sami stworzyli, jak Go.

Nie jest tak, że nie można używać tolerancyjnych języków w systemach na dużą skalę. Wiele osób twierdzi, że Facebook używa PHP, ale zapominają wspomnieć, że Facebook musiał stworzyć wyjątkowo surowe wytyczne programowe, aby móc efektywnie z niego korzystać na taką skalę.

Tak, w przypadku dużych projektów wymagany jest pewien stopień ścisłości. Może to wynikać z rygorystyczności języka lub frameworka, albo z wytycznych programowych i konwencji kodu. Nie możesz po prostu złapać kilku absolwentów szkół wyższych, dać im Python / Ruby / JavaScript i oczekiwać, że napiszą oprogramowanie, które skaluje się wśród milionów użytkowników.

Euforyk
źródło
„Nie możesz po prostu złapać kilku absolwentów szkół wyższych” ... ”i oczekiwać, że napiszą oprogramowanie, które skaluje się wśród milionów użytkowników”. prawdopodobnie by wystarczył.
dyesdyes
Warto tutaj zauważyć, że podobnie jak w przypadku Google i Pythona, użycie PHP przez Facebooka jest w dużej mierze klejem ... Rozumiem, że w przypadku większości funkcji PHP jest głównie używany jako stosunkowo prosty klient w bardziej złożonym systemie serwerów, który jest zwykle wdrażany w bardziej tradycyjnym języku „wagi ciężkiej”, takim jak Java, C ++, Haskell, OCaML itp.
Jules
„Tylko Google wie, z czym te systemy są zbudowane”. Mam nawet wątpliwości co do tego :) Z mojego doświadczenia wynika, że ​​żaden pojedynczy podmiot (osoba lub inna osoba) nie może wymienić wszystkich części bardzo dużego systemu. W wielu przypadkach w misach jakiegoś serwera znajduje się dawno zapomniany fragment skryptu Perl, Fortran lub KSH, który wykonuje „Magię”.
mattnz
3

Istnieją dwa rodzaje błędów do sprawdzenia: błędy typu (konkatenacja liczby całkowitej + lista liczb zmiennoprzecinkowych) i błędy logiki biznesowej (przelew pieniędzy na konto bankowe, sprawdź, czy konto źródłowe ma pieniądze).

„Dynamiczna” część dynamicznego języka programowania to tylko miejsce, w którym odbywa się sprawdzanie typu. W „programowanym dynamicznie” języku programowania sprawdzanie typu odbywa się podczas wykonywania każdej instrukcji, natomiast w „statycznym języku” sprawdzanie typu odbywa się w czasie kompilacji. I możesz napisać interpreter dla statycznego języka programowania (jak robi to emscriptem ), a także możesz napisać statyczny kompilator dla dynamicznego języka programowania (takiego jak gcc-python lub shed-skin ).

W dynamicznym języku programowania, takim jak Python i JavaScript, musisz pisać testy jednostkowe nie tylko dla logiki biznesowej programu, ale także sprawdzać, czy Twój program nie zawiera błędów składniowych lub typowych. Na przykład, jeśli dodasz „+” liczbę całkowitą do listy liczb zmiennoprzecinkowych (co nie ma sensu i spowoduje błąd), w języku dynamicznym błąd zostanie zgłoszony w czasie wykonywania podczas próby wykonania instrukcji. W statycznym języku programowania, takim jak C ++, Haskell i Java, tego rodzaju błąd typu zostanie przechwycony przez kompilator.

Mała baza kodu w dynamicznie sprawdzanym języku programowania ułatwia wyszukiwanie błędów pisowni, ponieważ łatwiej jest uzyskać 100% pokrycie kodu źródłowego. To jest to, wykonujesz kod ręcznie kilka razy z różnymi wartościami i gotowe. Posiadanie 100% pokrycia kodu źródłowego daje uczciwą wskazówkę, że twój program może nie zawierać błędów pisowni .

Przy dużej bazie kodu w dynamicznie sprawdzanym języku programowania trudniej jest przetestować każdą instrukcję z każdą możliwą kombinacją typów, szczególnie jeśli jesteś nieostrożny i piszesz funkcję, która może zwrócić łańcuch, listę lub obiekt niestandardowy w zależności od jego argumentów.

W statycznie sprawdzonym języku programowania kompilator przechwytuje większość błędów typu w czasie kompilacji. Mówię najbardziej, ponieważ błąd dzielenia przez zero lub błąd wykreślenia poza zakresem to także błędy typu.

Najczęściej prawdziwa dyskusja nie dotyczy języków programowania, ale ludzi korzystających z tych języków. I to prawda, ponieważ na przykład język asemblera jest tak samo potężny jak każdy inny język programowania, a jednak piszemy kod w JavaScript. Dlaczego? Ponieważ jesteśmy ludźmi. Po pierwsze, wszyscy popełniamy błędy, a korzystanie z dedykowanego narzędzia specjalnego do konkretnego zadania jest łatwiejsze i mniej podatne na błędy. Po drugie, istnieje ograniczenie zasobów. Nasz czas jest ograniczony, a pisanie stron internetowych na temat montażu zajęłoby wieki.

vz0
źródło
3

Moje doświadczenie z dużymi systemami polega na tym, że stoją lub upadają nie na podstawie wyboru języka, ale na podstawie zagadnień związanych z projektowaniem / architekturą lub zasięgiem testów . Wolę mieć utalentowany zespół Pythona przy moim dużym projekcie korporacyjnym niż mierny Java.

To powiedziawszy, każdy język, który pozwala ci pisać znacznie mniej kodu , powinien być wart uwagi (np. Python vs Java). Być może przyszłość będzie w sprytnych, statycznie typowanych językach z zaawansowanym wnioskowaniem typu (np. W formie Scala). Lub hybrydowy, taki jak C # próbuje z dynamickwalifikatorem ...?

I nie zapominajmy o „innych” statycznych zaletach pisania: poprawnym uzupełnianiu kodu / inteligencji, które moim zdaniem jest istotną cechą, a nie przyjemną w użyciu.

Cornel Masson
źródło
1
„uzupełnianie kodu / inteligencja” - automatyczne refaktoryzacja jest również bardzo ważna.
Den
@Den Absolutnie. Czy to możliwe, że dynamiczne języki pomagają bardzo szybko pisać początkowe wersje (łatwiej, mniej kodu do napisania), ale później mogą się ugrzęznąć, ponieważ coraz trudniej jest ocenić wpływ zmiany lub dokonać refaktoryzacji (bez narzędzi do automatycznego refaktoryzacji)?
Cornel Masson,
0

Inną kwestią jest to, kto stoi za pisaniem aplikacji na dużą skalę. Pracowałem w wielu miejscach, które chcą używać Ruby lub Pythona w niektórych dużych projektach w stylu korporacyjnym, ale konsekwentnie są „zestrzelane” przez kierowników IT i zespoły bezpieczeństwa korporacyjnego właśnie z powodu otwartego charakteru projektów.

Powiedziano mi: „Nie możemy używać Ruby on Rails, ponieważ jest to oprogramowanie typu open source i ktoś może umieścić w nim hacki, które kradną krytyczne lub chronione informacje”. Przykro mi, ale kiedy ktoś ma taki sposób myślenia, że ​​open source == zło, prawie niemożliwe jest jego zmiana. Ten sposób myślenia jest chorobą korporacyjną.

C # i Java to zaufane języki z zaufanymi platformami. Ruby i Python nie są zaufanymi językami.

Jarrett Meyer
źródło
2
Nie zgadzam się z ostatnią linią. Java jest jednym z najniższych punktów zaufania w historii. C # jest ostrożnie uważany przez całą społeczność open source. Ruby jest postrzegany jako solidny, ale powolny (nawet jeśli już nie jest), a Python jest zaufanym potomkiem czarujących koni z całej branży (czy ktoś uczy się maszyn i danych?).
CodeBeard
1
Języki dynamiczne są szkodliwe dla bezpieczeństwa, ale „open source” nie jest dobrym powodem. Może oznaczały „łatwo wpływać na jedną część kodu z zupełnie innej części kodu”. Zobacz programmers.stackexchange.com/questions/206558/…
Euphoric
1
Zauważ, że rzeczywiście „otwarte źródło” jest jednym z aspektów wyboru języka. To na przykład jeden z trzech powodów podanych przez Jeffa Atwooda, aby wyjaśnić, dlaczego Dyskurs używa Ruby.
Arseni Mourzenko
C # jest teraz całkowicie open-source, ale wciąż jest wyselekcjonowany, zaplanowany i opracowany przez profesjonalnych deweloperów, co jest chyba miłe. Miejmy nadzieję, że coś takiego „Python 3 vs 2” się tutaj nie wydarzy.
Den
Błędy i dziury w zabezpieczeniach są wprowadzane przez programistów, a nie przez języki. Dla przypomnienia wprowadziłem wiele poprawek bezpieczeństwa do projektów open source. Ile zamkniętych projektów pomogłem ??? zero!
Reactgular