Odbyłem niezwykłą, krótką rozmowę z bardzo starszym architektem na temat języków dynamicznych i statycznych. Powiedział, że dane firmy pokazują, że istnieją dowody na wyższą produktywność, gdy używane są języki statyczne. Uwaga, to duża firma z długą historią. Ku mojemu (i innym) zaskoczeniu metryką, której użył, były dodane wiersze kodu.
Szybko odrzucił obiekcje dotyczące metryki, mówiąc, że w tej samej firmie, z podobną kulturą, branżą i wystarczającą ilością danych, różnice (dotyczące unikalnych sytuacji i możliwości osób fizycznych) mieszają się na tyle, że metryka SLOC jest przydatna do porównania wydajności narzędzia i języki.
Chociaż nie sądzę, aby twierdzenie to było poparte rygorystyczną analizą statystyczną, czy istnieją jakieś dowody w branży, które popierałyby ten sposób myślenia?
źródło
Odpowiedzi:
Argument starszego architekta może oznaczać dwie rzeczy.
Może to oznaczać, że przeciętny programista w firmie produkuje więcej wierszy kodu , używając języków statycznych niż dynamicznych. Na przykład, jeśli piętnastu programistów pracuje z Javą przez sześć miesięcy, napiszą 100 KLOC, a jeśli tych samych piętnastu programistów będzie pracować z Pythonem przez sześć miesięcy, napiszą tylko 50 KLOC.
Nie ma tu korelacji między LOC a produktywnością. Co się stanie, jeśli potrzeba czterech razy więcej wierszy kodu w Javie, aby uzyskać tę samą funkcję niż w Pythonie? Jeśli to prawda, użycie Pythona spowodowałoby dwukrotność wydajności, w oparciu o powyższe wskaźniki KLOC.
Może również oznaczać, że przeciętny programista w firmie produkuje mniej wierszy kodu , używając języków statycznych niż dynamicznych: piętnaście programistów napisałoby w ciągu sześciu miesięcy 100 KLOC w Javie lub 200 KLOC w Pythonie.
Chociaż zwykle mniej wierszy kodu jest lepszych (mniej kodu do pisania, czytania i utrzymywania), nadal nie jest jasne, ile funkcji opracowali programiści Java w porównaniu do Pythona. Może napisali pół wiersza kodu w porównaniu do programistów Pythona, ale stworzyli także połowę liczby funkcji?
W obu przypadkach LOC nie jest cenną miarą, ponieważ ta sama funkcja nie tłumaczyłaby tyle samo wierszy kodu w różnych językach . Niektóre języki są bardziej szczegółowe; inne - bardziej kompaktowe. Chociaż w niektórych przypadkach zwartość jest cenna, nie ma na to ogólnej zasady. Skrajnym przykładem byłby język Brainfuck, który ma ekstremalną zwartość, ale który nie jest popularny ze względu na czytelność. Porównywanie nawet podobnych języków może być trudne: na przykład, jeśli chodzi o nawiasy klamrowe, Java podąża za stylem K&R, podczas gdy w języku C # otwierający nawias klamrowy jest w swoim własnym stylu, w większości przypadków podążając za oficjalnym stylem, co prowadzi do sztucznego wzrost LOC dla C #. A co się stanie, gdy porówna się język proceduralny z językiem obiektowym lub językiem funkcjonalnym?
Zamiast korzystać ze wskaźników podatnych na błędy, starszy architekt może polegać na grupie wskaźników, które mierzą wydajność, gdy są używane razem: liczba funkcji opracowywanych miesięcznie, liczba błędów wprowadzonych do bazy kodu i czas spędzony na ich rozwiązywaniu , ewolucja długu technicznego itp. Porównanie to może być trudne na początku, ponieważ należy wziąć pod uwagę nieznajomość nowego języka przez zespół. Gdy zespół się z nim zaznajomi, wybór powinien opierać się na stabilnych wskaźnikach, a także w większości na preferencjach samych członków zespołu.
LOC ma wartość w niektórych wąskich sytuacjach. Na przykład może podpowiedzieć na temat wielkości projektu i jego części (i średnio koreluje z punktami funkcyjnymi, choć często jest łatwiejszy do zmierzenia) lub wskazać metody i klasy, które mogą wymagać dalszej uwagi, ponieważ ich dużego rozmiaru. LOC należy jednak stosować ostrożnie, ponieważ jest zbyt często wykorzystywany przez osoby, które wyobrażają sobie korelację między niepowiązanymi rzeczami. Najbardziej katastrofalnym wykorzystaniem LOC-ów była w przeszłości próba zmierzenia wydajności pojedynczego programisty na podstawie LOC-ów pisanych miesięcznie.
źródło
O wydajności i SLOC
Problem z SLOC
Problem z metryką SLOC polega na tym, że mierzy ona przybliżoną ilość napisanego kodu, nie biorąc pod uwagę:
W przeciwnym razie produkcja podatnego na błędy, niemożliwego do utrzymania kodu spaghetti z dużą ilością skopiowanych części zostanie uznana za bardziej produktywną niż starannie zaprojektowany kod wielokrotnego użytku.
Dlatego SLOC zdecydowanie nie jest najlepszym sposobem pomiaru wydajności.
Jaką produktywność rozważamy?
Wydajność jest mierzona dla procesu. Tak więc SLOC może być doskonale ważnym wskaźnikiem dla samego procesu kodowania.
Jeśli na przykład źle zrozumiesz słabe wymagania, poświęcisz pięć miesięcy na wyprodukowanie oprogramowania, pokażesz go użytkownikowi, odkryjesz, że jest po prostu źle i poświęcisz kolejne 5 miesięcy na przepisanie go na dobre od zera, będziesz mieć taką samą produktywność w SLOC / miesiąc, że zespół pisze kod po raz pierwszy, na przykład dlatego, że zastosował zwinny proces, który zmniejsza nieporozumienia poprzez częste informacje zwrotne. Ta pozorna równa wydajność kryje ogromne problemy.
Tak więc pomiar produktywności oprogramowania musi uwzględniać cały proces, w tym analizę wymagań, projektowanie kodu, kodowanie, testowanie, debugowanie i sprawdzanie, czy spełnione są oczekiwania użytkowników. Ponieważ wszystkie te czynności są bardzo różne, najlepszą rzeczą jest zmierzenie jedynej ważnej myśli: działającego oprogramowania, czyli tego, co wyprodukowane oprogramowanie oznacza dla użytkownika .
Jak mierzyć elementy oprogramowania?
Istnieje kilka podejść:
O wydajności pisania statycznego vs. dynamicznego
Muszę wyznać, że osobiście jestem fanem statycznie pisanych języków, ponieważ w moim wnętrzu wiem, że jest bardziej niezawodny (lata kodowania mi to udowodniły).
Tak więc jedną rzeczą, którą z całą pewnością biorę pod uwagę, jest to, że język o typie statycznym jest w stanie zapobiec znacznie większej liczbie błędów / błędów w czasie kompilacji (np. Literówki, niedopasowanie w oczekiwanych typach itp.) Niż języki o typie niestatycznym. Ale z całą obiektywnością nie odważyłbym się obelżywie uogólnić to jako wyższą wydajność.
Czy twój architekt ma rację?
Może, może nie.
Ale jego argumenty nie wydają się słuszne: wzrost produktywności języka o typie statycznym wynika ze znacznej liczby błędów wykrytych z góry przez kompilator.
W związku z tym nie można dowiedzieć się o tym „wyższym” wzroście produktywności, patrząc tylko na SLOC, bez analizowania poprawek wymaganych dla dynamicznie typowanych języków. Więc jego porównanie nie może być uczciwe.
Argument porównywalnych okoliczności również nie ma miejsca. Niektóre języki o typie dynamicznym pozwalają na konstrukcje wyższego poziomu, które wymagają mniej kodu niż robienie tego samego w jednym z klasycznych języków o typie statycznym. Możesz więc potrzebować mniej czasu, pisać mniej kodu, ale dodać te same koszty analizy, testowania i weryfikacji. Zatem pomiar wydajności przez SLOC osłabiłby potencjalny wzrost wydajności, tworząc w ten sposób uprzedzenie względem dynamicznie typowanego języka.
Jakieś badania na poparcie tego twierdzenia?
Istnieje kilka najnowszych badań akademickich na ten temat. Chociaż niektóre z nich widzą zaletę pisania statycznego, generalnie są ograniczone do określonego celu (dokumentacja, ponowne użycie źle udokumentowanego kodu lub API itp.). Rozsądne sformułowanie jest również stosowane, ponieważ współczesne IDE znacznie zmniejszyło ryzyko związane z dynamicznym pisaniem:
źródło
Oto kontrprzykład dla twojego starszego architekta: Załóżmy, że chcę napisać hierarchię trzech klas, z których dwie wywodzą się z trzeciej, implementując niektóre funkcje wirtualne zdefiniowane przez klasę podstawową.
Jeśli napiszę te trzy klasy w C ++, to całkiem proste. Deklaruję zajęcia, używam wirtualnych w odpowiednich miejscach i gotowe.
Jeśli napiszę te trzy klasy w C, muszę dodać sporo kodu: muszę zdefiniować
struct
s dla tabel v, muszę dodać wskaźnik tabeli v do klasy podstawowej, muszę dodać kod do konstruktorów, aby faktycznie ustawić wskaźniki tabeli v, muszę dodać kod konstruktorów, aby faktycznie wywołać konstruktor klasy podstawowej, muszę dodać kod, aby jawnie wykonać alokację pamięci przed wywołaniem konstruktora (co C ++new
robi w jednym kroku ), podobnie, muszę oddzielić zniszczenie od kolejnegofree()
wezwania, i tak dalej, i tak dalej.Chodzi o to, że wszystkie te dodatkowe rzeczy są dość bezmyślne. Mogę zrobić to bardzo szybko. Więc napisanie wersji C nie zajmie mi więcej czasu niż napisanie wersji C ++. Niemniej jednak stworzyłem o wiele więcej wierszy kodu C niż kod C ++. Tak bardzo, że wydaje się, że byłem bardziej produktywny w C pod względem SLOC.
Każdy język, który wymaga pewnej ilości kodu wzorcowego, będzie wydawał się bardziej produktywny pod względem SLOC niż język, który nie wymaga takiej samej ilości kodu wzorcowego.
Widzisz, argument SLOC jest tak zasadniczo wadliwy, że faktycznie widziałbym to na odwrót: przyjąłbym stwierdzenie „programiści zwykle produkują więcej SLOC w językach statycznych”, co oznacza: „języki statyczne wydają się wymagać więcej kod płyty grzewczej, a tym samym zmniejszyć wydajność ”.
źródło
Będę contrarian.
Śledzimy SLoC w naszej pracy (chociaż nie używamy go bezpośrednio w decyzjach dotyczących personelu) i kazałem ludziom spierać się na to, co większość ludzi mówi w swoich odpowiedziach. W efekcie „LoC nie ma znaczenia, ponieważ technologia X pozwala nam robić więcej przy mniejszym kodzie” lub „Lepsi programiści piszą lepszy, krótszy kod, więc nie piszą więcej niż ktokolwiek inny”. Z mojego doświadczenia (chociaż nie mam twardych liczb, które mogłyby poprzeć te rzeczy), te obiekcje są po prostu nieprawidłowe. W swoim czasie widziałem wyraźną korelację zarówno szybkości, jak i jakości produkcji kodu dla naszych programistów, w porównaniu z wszystkimi innymi znaczącymi pomiarami ich ogólnej „kompetencji” jako inżyniera. Aby podać kilka kontrprzykładów do argumentów przedstawionych powyżej:
Ta ostatnia część to moje ogólne podsumowanie, BTW. Odkryłem, że niezależnie od stosu technologicznego lub rodzaju projektu, większość programistów ma swoje własne tempo, w którym działają. Jeśli język ma wiele funkcji, które zwiększają efektywność kodu programistów, jest to duży zysk dla firmy, ale to nie znaczy, że w rezultacie będą pisać mniej kodu. Zamiast tego szybciej wykonują funkcje i szybko przechodzą do nowego kodu. Ponownie, końcowym rezultatem jest to, że szybkość, z jaką kodują, zależy przede wszystkim od ich umiejętności, a mniej od ich stosu technologii. W rzeczywistości z tego powodu ogólnie spodziewałbym się, że stos technologii będzie miał większy wpływ na szybkość opracowywania biletów i funkcji niż na szybkość kodowania przez ludzi.
To powiedziawszy, ani szybkość pisania kodu, ani szybkość zamykania biletów nie jest doskonałą miarą wydajności, dlatego nie podejmujemy bezpośrednio decyzji personalnych na podstawie SLoC. Zamiast tego jest to część procesu, a oceny pracowników są przeprowadzane przy użyciu jak największej liczby punktów danych. Powiedziałbym jednak, że twój architekt z pewnością nie jest szalony.
Jeden wyjątek
Jedynym wyjątkiem, z którym się zgadzam, jest możliwość podania kodu kotła. Jeśli wiele klas kopiowania i wklejania dzieje się z jednej klasy (lub czegokolwiek) do drugiej, aby ją uruchomić, to oczywiście zniekształci wskaźniki. Dotyczy to również narzędzi, które mogą automatycznie generować duże ilości kodu. Myślę jednak, że często będą to raczej wyjątek niż reguła. Jeśli programiści poświęcają trochę czasu na kopiowanie kodu płyty kotła, aby rozpocząć, to używasz niewłaściwego zestawu technologii. Jeśli faktycznie piszą kod, nawet jeśli jest dość powtarzalny, spodziewam się, że to wypaczy wszelkie pomiary tylko w niewielkim stopniu: podczas pisania kodu, przez większość czasu jesteśmy ograniczeni przez to, jak szybko potrafimy przemyśleć problem niż jak szybko możemy pisać. Nawet podczas pisania stosunkowo powtarzalnego kodu,
Oczywiście wszystko powyżej opiera się na moich osobistych doświadczeniach. Twój przebieg może się różnić i oczywiście jestem w mniejszości. Możesz się nie zgodzić. Podsumowując:
Uważam, że szybkość kodowania zależy bardziej od tego, jak szybko możesz przemyśleć swoje problemy, niż cokolwiek innego. W rezultacie odkryłem, że szybkość kodowania jest przyzwoitą miarą wydajności, nawet w zestawach technicznych, z kilkoma możliwymi wyjątkami.
źródło
Chociaż wskakuję na modę. Myślę, że należy podkreślić wpływ na zachowanie programistów.
Używanie SLOC jako miernika produktywności ma toksyczny wpływ na morale programistów. W momencie, gdy jakikolwiek inżynier w twoim zespole / firmie zda sobie sprawę, że są mierzone na SLOC, dzieje się kilka rzeczy:
Nie mogę wystarczająco mocno podkreślić, jak korozyjnie jest wpływać na morale, ponieważ widziałem to dwa razy w 2 różnych firmach. Niezależnie od tego, jak pozornie ważne są przypadki użycia, twierdzę, że nie jest to warte wpływu na Twój zespół / firmę, nawet jeśli istnieje tylko niewielka szansa, że zostanie ono odkryte. Chociaż w niektórych przypadkach może istnieć korelacja między liczbą zapisanych wierszy a ilością przydatnych funkcji, zachęca to wszystkich niewłaściwych zachowań u programistów i wysyła komunikat, że jakość nie jest ważna.
źródło
Na ogół nie jest uważany za prawidłowy sposób pomiaru wydajności. Mniejszy kod jest zwykle lepszy niż większy, więc bardziej produktywny programista zwykle wytwarza mniej kodu. Wydajność ma największy hit w debugowaniu; wydajni programiści poświęcają niewiele czasu na debugowanie.
Języki o typie statycznym są bardziej wydajne (jeśli kontrolujesz wszystkie inne różnice między językami), ponieważ gdy są używane mądrze, skracają czas debugowania, wychwytując błędy w fazie kompilacji, gdzie są szybsze do naprawienia.
źródło
Jedyną miarą, której można użyć do porównania wydajności dla programistów między językami, jest metryka, która nie porównuje kodu między językami. Niektóre języki są notorycznie gadatliwe (COBOL za starsze zwycięstwo), a inne wymagają kilku kroków, aby zrobić coś, co możesz zrobić w jednym wierszu kodu (asemblowanie vs. wszystko inne). Nawet jeśli porównasz tylko aktywne linie kodu (tj. Nie liczą deklaracji i liczą tylko kod, który wiąże się z pewną akcją), nadal możesz wypaczać swoje wyniki.
Być może będziesz w stanie argumentować za szybkościami zmian. Tzn. Dodano linie kodu, porównując nachylenie wydajności w tym samym okresie. Nie uwzględnia to jednak negatywnych zmian w wierszach kodu. Na przykład dziedziczysz projekt, który wszędzie kopiuje i wkleja kod. Wykonujesz szybkie i łatwe refaktoryzacje, aby zmniejszyć liczbę powtarzających się bloków kodu - z definicji masz ujemne nachylenie.
Pracowałem nad projektem, w którym infrastruktura była bardzo delikatna, a narzędzia były przestarzałe. Projekt został zbudowany na Javie z dołączoną do niego aplikacją Single Page, ale był hostowany w kontenerze portletu bez widocznych korzyści. Czas potrzebny nawet na proste zmiany był absurdalnie długi. Jeśli oprzesz wszystkie swoje wnioski na tym konkretnym projekcie, możesz dojść do wniosku, że Java była zła lub aplikacje Single Page były złe. Żadne nie są prawdziwe. System, który brzydki projekt miał zastąpić, został zbudowany na C # i WebForms. Kiedy przedstawiliśmy uzasadnienie biznesowe rozszerzenia istniejącej aplikacji w celu zaspokojenia potrzeb klientów, nasza produktywność gwałtownie wzrosła. Czy to oznacza, że ściśle powiązana aplikacja WebForms jest lepsza? Można wyciągnąć taki wniosek tylko w tym konkretnym przypadkui nie obejmuje całego świata. Ma to sens tylko dlatego, że istniała aplikacja o wystarczającej dojrzałości do rozszerzenia.
Nawet porównywanie wskaźników rozwiązywania problemów w systemie śledzenia problemów jest wadliwe w tym sensie, że porównujesz między sobą kompletną infrastrukturę projektu. Użyte biblioteki i frameworki mogą przyspieszyć lub spowolnić postęp. Możesz być w fazie rozruchu z bardzo małą bezwładnością do pokonania, gdzie projekt, w którym jesteś „lepszy niż”, znajduje się w fazie konserwacji, w której liczba nowych biletów jest stosunkowo niska. Nigdy nie chodzi o porównywanie podobnych rzeczy.
źródło