W jaki sposób wzrost złożoności systemów wpłynął na kolejne generacje programistów?

127

Jako „nowy” programista (po raz pierwszy napisałem wiersz kodu w 2009 r.) Zauważyłem, że stosunkowo łatwo jest stworzyć program, który wykazuje dziś dość złożone elementy, na przykład z platformą .NET. Tworzenie interfejsu wizualnego lub sortowanie listy można teraz wykonać za pomocą bardzo niewielu poleceń.

Kiedy uczyłem się programowania, uczyłem się równolegle teorii obliczeń. Rzeczy takie jak algorytmy sortowania, zasady współdziałania sprzętu, algebra boolowska i maszyny o stanie skończonym. Zauważyłem jednak, że jeśli kiedykolwiek chciałbym przetestować jakąś bardzo podstawową zasadę, której nauczyłem się w teorii, zawsze było o wiele trudniej zacząć, ponieważ tak wiele technologii jest zasłoniętych przez takie rzeczy jak biblioteki, frameworki i system operacyjny.

Stworzenie programu wydajnego pod względem pamięci było wymagane 40/50 lat temu, ponieważ nie było wystarczającej ilości pamięci i było drogie, dlatego większość programistów zwróciła szczególną uwagę na typy danych i sposób obsługi instrukcji przez procesor. W dzisiejszych czasach niektórzy mogą argumentować, że ze względu na zwiększoną moc przetwarzania i dostępną pamięć te obawy nie są priorytetem.

Moje pytanie brzmi, czy starsi programiści postrzegają takie innowacje jako dar niebios lub dodatkową warstwę do abstrakcji i dlaczego mogą tak myśleć? I czy młodsi programiści czerpią więcej korzyści z nauki programowania niskopoziomowego PRZED eksploracją królestw rozbudowanych bibliotek? Jeśli tak, to dlaczego?

Adam
źródło
24
Istotny jest artykuł Joela Spolsky'ego z 2002 roku: joelonsoftware.com/articles/LeakyAbstractions.html Jednak w formie wyrażenia / sformułowania to pytanie może zostać uznane za oparte głównie na opiniach.
BrianH
Ubolewam nad brakiem prostszych opcji badania bardzo podstawowych technik programowania.
GrandmasterB
1
To jest istotne. Raczej. (Mam na myśli, że mam nadzieję, że ten obraz jest żartem, ale z tym, co dostaje się do StackOverflow ...)
Izkata
1
Ma swoje zalety i wady. Otwiera świat programowania dla wielu nowych programistów, którzy nie potrzebują tak wysokich umiejętności, aby odnieść sukces - wbrew temu, co niektórzy mogą powiedzieć, to dobrze. Nadal piszemy wydajne programy, które nigdy się nie zmieniły - po prostu zmieniły się cele. Zapisywanie bajtu w danym roku nie jest już dobrą rzeczą - różnica w pamięci zwykle nie robi żadnej różnicy, a przy użyciu np. dwa bajty są bardziej elastyczne i przyszłościowe. Istotnym czynnikiem są również koszty programistów w porównaniu z kosztami SW i HW. Zapotrzebowanie na nowe oprogramowanie jest ogromne. Koderów jest niewiele.
Luaan
1
Skala czasu 40/50 lat jest nieprawidłowa. Programowanie wydajne pod względem pamięci było nadal niezwykle ważne w 16-bitowym systemie Windows (mniej niż 20 lat temu) i (niestety) w większości dzisiejszych systemów wbudowanych / robotyki.
david.pfx

Odpowiedzi:

174

nie jest to konieczne ze względu na zwiększoną moc obliczeniową i dostępną pamięć.

Posiadanie taniej pamięci, ogromnych dysków i szybkich procesorów nie jest jedyną rzeczą, która uwolniła ludzi od potrzeby obsesji na punkcie każdego bajtu i cyklu. Kompilatory są teraz znacznie, znacznie lepsze niż ludzie, w tworzeniu wysoce zoptymalizowanego kodu, gdy ma to znaczenie.

Co więcej, nie zapominajmy, do czego tak naprawdę staramy się zoptymalizować, czyli wytworzonej wartości przy danym koszcie. Programiści są znacznie drożsi niż maszyny. Wszystko, co robimy, sprawia, że ​​programiści produkują działające, poprawne, niezawodne, w pełni wyposażone programy szybciej i taniej, co prowadzi do tworzenia większej wartości na świecie.

Moje pytanie dotyczy jednak tego, co ludzie myślą o tym „ukrywaniu” elementów niższego poziomu. Czy wy, starsi programiści, widzicie to jako dar niebios czy niepotrzebną warstwę, aby się przedostać?

Wykonanie jakiejkolwiek pracy jest absolutnie konieczne. Piszę analizatory kodów dla życia; gdybym musiał się martwić o przydział rejestrów lub planowanie procesora lub którykolwiek z tych milionów innych szczegółów, nie spędzałbym czasu na naprawianiu błędów, przeglądaniu raportów wydajności, dodawaniu funkcji i tak dalej.

Całe programowanie polega na wyodrębnieniu warstwy pod tobą, aby uczynić z niej bardziej wartościową warstwę. Jeśli wykonasz „diagram ciasta warstwowego” pokazujący wszystkie podsystemy i sposób ich wzajemnej budowy, przekonasz się, że istnieją dosłownie dziesiątki warstw między sprzętem a doświadczeniem użytkownika. Wydaje mi się, że na diagramie ciastek warstwy Windows jest około 60 poziomów niezbędnych podsystemów między surowym sprzętem a zdolnością do wykonywania „hello world” w C #.

Czy uważasz, że młodsi programiści skorzystaliby na nauce programowania niskiego poziomu PRZED eksploracją królestw rozbudowanych bibliotek?

Kładziesz nacisk na PRZED, więc muszę udzielić odpowiedzi przeczącej. Pomagam 12-letniemu przyjacielowi w nauce programowania w tej chwili. Lepiej uwierz, że uruchamiam je w Processing.js, a nie w asemblerze x86. Jeśli zaczniesz młodego programistę w czymś takim Processing.js, będą pisać własne gry typu shoot-em-up za około osiem godzin. Jeśli uruchomisz je w asemblerze, pomnoży trzy liczby razem za około osiem godzin. Jak myślisz, które bardziej zainteresuje młodszego programistę?

Teraz, jeśli pytanie brzmi: „czy programiści, którzy rozumieją warstwę n ciasta, czerpią korzyści ze zrozumienia warstwy n - 1?” odpowiedź brzmi tak, ale jest to niezależne od wieku i doświadczenia; zawsze jest tak, że możesz poprawić swoje programowanie na wyższym poziomie, lepiej rozumiejąc abstrakcyjne abstrakcje.

Eric Lippert
źródło
12
+1 zabawne cytowanie: Liczba Dunbarsa jest dobrym przykładem (istnieją inne) badanych ilorazów zdolności poznawczych, które można zobaczyć u wielu osób, pokazując, że mamy stałą przestrzeń mentalną. Abstrakcja wielu rzeczy na pojedyncze uogólnienia to jedyny sposób, w jaki możemy w spójny sposób zwiększyć liczbę rzeczy w naszej przestrzeni mentalnej i właśnie z tego korzysta programowanie wyższego poziomu.
Jimmy Hoffa
18
@Euphoric: Twoje pytanie ma sens, ale gdzie się zatrzymujesz? Załóżmy, że mówię „cóż, zamiast uczyć się Processing.js, nauczmy się pisać gry wideo w C ++ przy użyciu DirectX”. Ok dobrze. Co powstrzymuje cię od powiedzenia „czy nie spowoduje to problemów, jeśli będą chcieli zrobić coś mniej abstrakcyjnego?” i może chcemy zacząć od napisania sterownika karty graficznej. Ale potem ponownie zadajesz pytanie i zanim się zorientujesz, wprowadzamy kod maszynowy do Altair za pomocą przełączników. Musisz gdzieś wybrać poziom abstrakcji !
Eric Lippert
35
@Euphoric: Czy podałbyś ten sam argument do, powiedzmy, księgowości? Nie potrzebujemy więcej osób, które mogłyby przechowywać proste książki dla nowej małej firmy; potrzebujemy WIELKICH, światowej klasy księgowych. Jeśli wstępne kursy rachunkowości są tak trudne, że odstraszają ludzi, którzy jedynie aspirują do bycia produktywnymi, księgowymi podobnymi do pracy, DOBRE. Nie potrzebujemy tych ludzi w branży księgowej! Co powiesz na pianistów? Jeśli wstępne lekcje gry na pianinie odstraszą ludzi, którzy nie będą WIELKIM pianistą, to dobrze; chcemy tylko wielkich pianistów na świecie. Czy coś wydaje się nie tak z tym argumentem?
Eric Lippert
25
@Euphoric: Krótka odpowiedź brzmi: DOBRE NIEBA TAK, potrzebujemy więcej porządnych programistów! Potrzebujemy więcej programistów na każdym poziomie umiejętności i doświadczenia. Świat działa na oprogramowaniu . Im więcej ludzi, którzy mają jakąkolwiek wiedzę na temat, jak zrobić swoją pracę świat, tym lepiej.
Eric Lippert
6
@Euphoric (i inni) - w pewnym sensie przekraczamy granicę konstruktywności komentarzy. Dołącz do nas na czacie inżynierii oprogramowania, jeśli chcesz kontynuować tę rozmowę.
50

Miałem pomysły na ten temat i umieściłem je w książce 20 lat temu . Już dawno się skończył, ale nadal możesz używać używanych kopii na Amazon .

Jedna prosta odpowiedź na twoje pytanie jest tak stara jak Arystoteles: Natura nie znosi próżni . Ponieważ maszyny stały się szybsze i większe, oprogramowanie stało się wolniejsze i większe.

Aby być bardziej konstruktywnym, zaproponowałem, aby teoria informacji i jej bezpośrednie znaczenie dla oprogramowania były częścią edukacji informatycznej. Naucza się go teraz, jeśli w ogóle, w bardzo styczny sposób.

Na przykład zachowanie algorytmów typu Big-O może być bardzo starannie i intuicyjnie zrozumiane, jeśli myślisz o programie jako kanale informacyjnym typu Shannon z symbolami wejściowymi, symbolami wyjściowymi, szumem, redundancją i przepustowością.

Z drugiej strony produktywność programisty można zrozumieć w podobny sposób przy użyciu teorii informacji Kołmogorowa. Dane wejściowe są symboliczną strukturą pojęciową w twojej głowie, a dane wyjściowe to tekst programu, który pojawia się na wyciągnięcie ręki. Proces programowania jest kanałem między nimi. Gdy hałas wchodzi w proces, tworzy niespójne programy (błędy). Jeśli tekst programu wyjściowego ma wystarczającą redundancję, może pozwolić na wychwycenie i poprawienie błędów (wykrywanie i korekcja błędów). Jednak, jeśli jest zbyt zbędny jest zbyt duża, a jej bardzo wielkości, w połączeniu z poziomem błędu, powoduje wprowadzanie błędów. W wyniku tego rozumowania spędziłem dużą część książki, pokazując, jak traktować programowanie jako proces projektowania języka, w celu zdefiniowania języków specyficznych dla domeny odpowiednich do potrzeb. Świadczymy usługi dla języków specyficznych dla domeny w edukacji CS, ale znowu jest to styczne.

Budowanie języków jest łatwe. Za każdym razem, gdy definiujesz funkcję, klasę lub zmienną, dodajesz słownictwo do języka, w którym zacząłeś, tworząc nowy język do pracy. Ogólnie rzecz biorąc nie docenia się tego, że celem powinno być dopasowanie nowego języka do struktury pojęciowej problemu. Jeśli tak się stanie, spowoduje to skrócenie kodu i sprawi, że będzie mniej buggowy, po prostu dlatego, że idealnie jest odwzorowanie 1-1 między koncepcjami a kodem. Jeśli mapowanie to 1-1, możesz popełnić błąd i niepoprawnie zakodować koncepcję jako inną koncepcję, ale program nigdy się nie zawiesi, co dzieje się, gdy nie koduje żadnego spójnego wymagania .

Nie dostaniemy tego. W całej naszej odważnej dyskusji na temat projektowania systemu oprogramowania stosunek kodu do wymagań jest coraz większy.

To prawda, mamy bardzo przydatne biblioteki. Myślę jednak, że powinniśmy być bardzo ostrożni w kwestii abstrakcji. Nie należy zakładać, że B opiera się na A i to dobrze, że jeśli C opiera się na B, jest jeszcze lepiej. Nazywam to fenomenem „księżniczki i groszku”. Układanie warstw na czymś kłopotliwym niekoniecznie to rozwiązuje.

Aby zakończyć długi post, opracowałem styl programowania (który czasem wpędza mnie w kłopoty)

  • Wynalazek nie jest złą rzeczą. To dobrze, podobnie jak w innych gałęziach inżynierii. Jasne, że może to stworzyć krzywą uczenia się dla innych, ale jeśli ogólnym rezultatem jest lepsza wydajność, warto.
  • Minimalistyczny kod w stylu haiku. Dotyczy to zwłaszcza projektowania struktury danych. Z mojego doświadczenia wynika, że ​​największym problemem w oprogramowaniu jest obecnie rozdęta struktura danych.
Mike Dunlavey
źródło
9
Brzmi bardzo podobnie, jak zawsze opowiadał się Chuck Moore (wynalazca Forth ). Na przykład z komentarzy Chucka Moore'a na temat Fortha : „Nie sądzę, że jest to nieodłączne w naturze oprogramowania, że ​​musi ono być duże, nieporęczne, buggy. Jest w naturze naszego społeczeństwa. ... Jest tak wiele ekonomiczna motywacja do wyprodukowania tego ... wzdęcia, które czuję się nieodpowiedzialne w wstawaniu i mówieniu, że cesarz nie ma ubrania.
Peter Mortensen
3
@PeterMortensen: Uzgodniony. Jest samotny Jest na to słowo - kompleks Cassandra .
Mike Dunlavey
2
Podczas gdy pisanie artefaktów kodu w celu „rozszerzenia” języków nie jest trudnym przedsięwzięciem, stworzenie dobrego interfejsu API dokładnie i intuicyjnie odzwierciedla domenę problemową .
Robert Harvey
3
@MikeDunlavey: BTW: Jesteś także facetem „bez profilu” (ma to na myśli pozytywnie). Kilka miesięcy temu ponownie użyłem twojej techniki w świecie rzeczywistym, aby szybko skrócić czas ładowania pliku dokumentu z zazwyczaj 25 sekund do 1 sekundy (czas ładowania, którego użytkownik bezpośrednio doświadcza). Zajęło to kilka iteracji, a 10-20 próbek we wszystkich iteracjach było więcej niż wystarczające. Problemy z wydajnością występowały oczywiście w nieoczekiwanych miejscach.
Peter Mortensen
2
@Izkata and Peter: Tak, jestem tym dziwakiem. FWIW, zamieściłem kilka (bardzo amatorskich) filmów, mając nadzieję, że łatwiej to zrozumieć. Losowe wstrzymywanie. Wykonanie różnicowe. Twoje zdrowie.
Mike Dunlavey
35

Abstrakcja na wysokim poziomie jest niezbędna do osiągnięcia ciągłego postępu w informatyce.

Dlaczego? Ponieważ ludzie mogą mieć tylko tyle wiedzy w swoich głowach w danym momencie. Nowoczesne, wielkoskalowe systemy są możliwe tylko dzisiaj, ponieważ można wykorzystać takie abstrakcje. Bez tych abstrakcji systemy oprogramowania po prostu upadłyby pod własnym ciężarem.

Za każdym razem, gdy piszesz metodę, tworzysz abstrakcję. Tworzysz trochę funkcjonalności ukrytej za wywołaniem metody. Dlaczego je piszesz? Ponieważ możesz przetestować metodę, udowodnić, że działa, a następnie wywołać tę funkcję w dowolnym momencie, po prostu wywołując metodę, i nie musisz już myśleć o kodzie zawartym w tej metodzie.

We wczesnych dniach komputerów korzystaliśmy z języka maszynowego. Napisaliśmy bardzo małe, gołe programy z dogłębną znajomością sprzętu, dla którego je pisaliśmy. To był żmudny proces. Nie było debuggerów; twój program zwykle albo działał, albo się zawieszał. Nie było GUI; wszystko było procesem wiersza poleceń lub procesem wsadowym. Napisany kod będzie działał tylko na tym konkretnym komputerze; nie działałoby na komputerze z innym procesorem lub systemem operacyjnym.

Napisaliśmy więc języki wysokiego poziomu, aby oddzielić wszystkie te szczegóły. Stworzyliśmy maszyny wirtualne, aby nasze programy mogły być przenośne na inne maszyny. Stworzyliśmy odśmiecanie pamięci, aby programiści nie musieli tak pilnie zarządzać pamięcią, co wyeliminowało całą klasę trudnych błędów. Dodaliśmy sprawdzanie granic do naszych języków, aby hakerzy nie mogli ich wykorzystać przy przepełnieniu bufora. Wynaleźliśmy Programowanie Funkcjonalne, abyśmy mogli inaczej rozumować nasze programy, i niedawno go odkryliśmy, aby lepiej wykorzystać współbieżność.

Czy cała ta abstrakcja izoluje Cię od sprzętu? Jasne, że tak. Czy mieszkanie w domu zamiast rozbicia namiotu izoluje Cię od natury? Absolutnie. Ale wszyscy wiedzą, dlaczego mieszkają w domu zamiast w namiocie, a budowanie domu to zupełnie inna gra w piłkę niż rozbicie namiotu.

Jednak nadal możesz rozbić namiot, gdy jest to konieczne, a przy programowaniu możesz (jeśli jesteś tak skłonny) zejść do poziomu bliższego sprzętowi, aby uzyskać korzyści w zakresie wydajności lub pamięci, których możesz nie mieć w przeciwnym razie osiągnij w swoim języku wysokiego poziomu.


Czy potrafisz za dużo abstrakcji? „Wyprzedź hydraulikę”, jak powiedziałby Scotty ? Oczywiście, że możesz. Pisanie dobrych API jest trudne. Pisanie dobrych API, które poprawnie i kompleksowo ucieleśniają domenę problemu, w sposób intuicyjny i możliwy do wykrycia, jest jeszcze trudniejsze. Piling na nowych warstwach oprogramowania nie zawsze jest najlepszym rozwiązaniem. Wzorce projektowania oprogramowania do pewnego stopnia pogorszyły tę sytuację, ponieważ niedoświadczeni programiści czasami sięgają po nie, gdy bardziej odpowiednie jest ostrzejsze i szczuplejsze narzędzie.

Robert Harvey
źródło
1
+1, chociaż myślę, że masz historię programowania funkcjonalnego wstecz (rachunek lambda poprzedza komputery elektroniczne, a wiele języków funkcjonalnych poprzedza programowanie równoległe).
amon
1
Dodałem małe wyjaśnienie.
Robert Harvey
2
„We wczesnych dniach komputerów korzystaliśmy z języka maszynowego. Pisaliśmy bardzo małe programy typu„ bare metal ”z dogłębną znajomością sprzętu, dla którego je pisaliśmy”. Niektórzy z nas w programowaniu wbudowanym od czasu do czasu nadal to robią, na 8-mikrokontrolerach, które mają mniej niż 1K pamięci programu, 64 bajty RAM i kosztują około jednej czwartej. Nie ma tam kompilatora C. (Ale zwykle pracuję z 32-bitowymi mikrokontrolerami z typowo 1/2 MB miejsca na program.)
tcrosley,
9

Naprawdę dobry trening obejmuje zarówno skrajności, jak i pomost między nimi.

Po stronie niskiego poziomu: jak komputer wykonuje kod od podstaw *, w tym znajomość języka asemblera i działania kompilatora.

Po stronie wysokiego poziomu: ogólne koncepcje, np. Stosowanie tablic asocjacyjnych, zamknięć itp. Bez marnowania czasu na martwienie się o to, jak to działa pod maską.

IMHO każdy powinien mieć doświadczenie z obu, w tym z ich wadami, i zasmakować, jak przejść od koncepcji niskiego poziomu do koncepcji wysokiego poziomu. Uwielbiasz tablice asocjacyjne? Świetnie, spróbuj teraz użyć ich na 50-centymetrowym wbudowanym procesorze z 1kB pamięci RAM. Jak pisanie szybkiego kodu za pomocą C? Świetnie, teraz masz trzy tygodnie na napisanie aplikacji internetowej; możesz poświęcić czas na zajmowanie się strukturami danych i zarządzaniem pamięcią za pomocą C, lub możesz poświęcić czas na naukę nowego frameworka internetowego, a następnie wdrożenie aplikacji internetowej w ciągu kilku dni.

Jeśli chodzi o aspekt złożoności: wydaje mi się, że w dzisiejszych czasach tworzenie skomplikowanych systemów jest zbyt łatwe bez jasnego zrozumienia kosztów . W rezultacie jako społeczeństwo narosliśmy ogromną kwotę długu technicznego, który od czasu do czasu nas gryzie. To jak trzęsienia ziemi (tylko koszty życia w pobliżu uskoku geologicznego, prawda?), Tylko że stopniowo się pogarsza. I nie wiem co z tym zrobić. Idealnie byłoby uczyć się i radzić sobie lepiej w zarządzaniu złożonością, ale nie sądzę, że tak się stanie. Odpowiedzialne wykształcenie inżynierskie musi obejmować znacznie więcej dyskusji na temat konsekwencji złożoności niż większość naszych uniwersytetów obecnie zapewnia.


* a poza tym, gdzie jest „podstawa” w sposobie wykonywania kodu przez komputer? Czy to język asemblera? A może architektura komputerowa? Czy logika cyfrowa? A może tranzystory? Czy fizyka urządzeń?

Jason S.
źródło
7

Uważam, że programowanie na wysokim poziomie ma wiele zalet i jest istotną częścią języka programowania. Jednym z powodów sukcesu Javy było posiadanie obszernej biblioteki. Osiągasz więcej przy mniejszym kodzie - wystarczy wywołać wstępnie zdefiniowaną funkcję.

Możemy teraz odróżnić użytkowników języka programowania od programistów (programistów). Optymalizacje pozostawiamy autorom kompilatorów. Koncentrujemy się bardziej na konserwacji, ponownym użyciu itp

Ali
źródło
7

Wzrost złożoności systemów jest nieustępliwy, uciążliwy i ostatecznie okaleczający. Dla mnie, jako programisty starszej generacji, jest to również bardzo rozczarowujące.

Programuję od ponad 40 lat, pisząc kod w 50-100 różnych językach lub dialektach, i staję się ekspertem w 5-10. Powodem, dla którego mogę twierdzić tak wielu, jest to, że przeważnie są one w tym samym języku, z drobnymi poprawkami. Ulepszenia zwiększają złożoność, dzięki czemu każdy język jest nieco inny.

Wdrożyłem te same algorytmy niezliczoną ilość razy: kolekcje, konwersje, sortowanie i wyszukiwanie, kodowanie / dekodowanie, format / parsowanie, bufory i łańcuchy, arytmetyka, pamięć, operacje we / wy. Każda nowa implementacja dodaje złożoności, ponieważ każda jest tylko trochę inna.

Zastanawiam się nad magią stworzoną przez wysoko latających artystów trapezowych z ram internetowych i aplikacji mobilnych, z tego, jak potrafią wyprodukować coś tak pięknego w tak krótkim czasie. Wtedy zdaję sobie sprawę, jak bardzo nie wiedzą, ile będą musieli dowiedzieć się o danych, komunikacji, testowaniu, wątkach lub czymkolwiek, zanim to, co zrobią, stanie się przydatne.

Nauczyłem się swojego rzemiosła w erze języków czwartej generacji, gdzie szczerze wierzyliśmy, że stworzymy serię języków na wyższym i wyższym poziomie, aby stopniowo przechwytywać coraz więcej powtarzających się części oprogramowania do pisania. Jak to się dokładnie skończyło?

Microsoft i IBM zabili ten pomysł, wracając do C w celu pisania aplikacji dla systemu Windows i OS / 2, podczas gdy dBase / Foxpro, a nawet Delphi, marnowali. Następnie sieć zrobiła to ponownie, wykorzystując swoje najlepsze trio języków asemblacyjnych: HTML, CSS i JavaScript / DOM. Stamtąd wszystko było z górki. Zawsze więcej języków i więcej bibliotek oraz więcej frameworków i większa złożoność.

Wiemy, że powinniśmy robić to inaczej. Wiemy o CoffeeScript i Dart, o Less i Sass, o szablonie, aby uniknąć konieczności pisania HTML. Wiemy i tak robimy. Mamy nasze frameworki, pełne nieszczelnych abstrakcji i widzimy, jakie cuda mogą zrobić ci wybrani, którzy uczą się tajemnych zaklęć, ale my i nasze programy jesteśmy uwięzieni w decyzjach podjętych w przeszłości. Zmiana lub rozpoczęcie od nowa jest zbyt skomplikowane.

W rezultacie rzeczy, które powinny być łatwe, nie są łatwe, a rzeczy, które powinny być możliwe, są prawie niemożliwe z powodu złożoności. Potrafię oszacować koszt wprowadzenia zmian w celu wprowadzenia nowej funkcji w ustalonej bazie kodu i mieć pewność, że będę miał rację. Potrafię oszacować, ale nie mogę tego uzasadnić ani wyjaśnić. To zbyt skomplikowane.

W odpowiedzi na twoje ostatnie pytanie zdecydowanie zalecam młodszym programistom, aby zaczynali tak wysoko od ciasta warstwowego, jak to tylko możliwe, i nurkowali tylko do niższych warstw, ponieważ potrzeba i pragnienie zapewniają impuls. Preferuję języki bez pętli, z małym rozgałęzieniem lub bez rozgałęzień i jawnym stanem. Przychodzą mi na myśl Lisp i Haskell. W praktyce zawsze kończę na C # / Java, Ruby, JavaScript, Python i SQL, ponieważ tam są społeczności.

Ostatnie słowa: złożoność jest największym wrogiem! Pokonaj to, a życie stanie się proste.

david.pfx
źródło
Moje ponad 30 lat programowania nauczyło mnie używania dostępnego języka najwyższego poziomu, który zrobi to, co trzeba, i nadal pozwoli na używanie języków niższego poziomu, gdy będzie to wymagane. Najłatwiejszym środowiskiem jest wykonywanie skryptów powłoki, które mogą wywoływać moduły napisane w dowolnym języku. Trudność polega na przełamaniu dominującego sposobu myślenia, że ​​wszystkie funkcje projektu muszą być zaimplementowane w tym samym języku.
DocSalvager
@dicsalvage: Zgadzam się, a moim wielkim rozczarowaniem jest brak coraz wyższych poziomów. Co awk mógł zrobić w 10 liniach w latach 80., teraz zajmuje 15 linii w Ruby lub Python, ale szukam czegoś do zrobienia w 3. A zablokowane środowiska na telefonach oznaczają, że to samo zajmuje 50 w Javie lub celu C. Nie tam skrypty powłoki!
david.pfx
Google „bash dla Androida” pokazuje wielu ludzi pracujących na portach. Istnieją również wersje Pythona, takie jak Kivy
DocSalvager,
@DocSalvage: Wcześniej czy później środowisko telefoniczne przyłączy się do cywilizacji (tak jak ją znamy). Moja skarga to czas potrzebny na zrobienie w kółko rzeczy, które wydają się być ukończone. Musimy wracać do układania fundamentów, murów, drenażu i szałasów, kiedy chcę budować drapacze chmur.
david.pfx
4

Moje pytanie dotyczy jednak tego, co ludzie myślą o tym „ukrywaniu” elementów niższego poziomu. Czy wy, starsi programiści, widzicie to jako dar niebios czy niepotrzebną warstwę, aby się przedostać?

Tak naprawdę nie.

Nakładanie warstw jest konieczne, ponieważ bez niego osiągniesz punkt, w którym twój system stanie się nieusuwalnym spaghetti. Jest to również jedna z zasad wielokrotnego użytku: jeśli twórca biblioteki wykonał dobrą robotę, osoby korzystające z niej nie powinny dbać o szczegóły implementacji. Ilość kodu w puszkach, którego używamy w naszych systemach, wzrosła o porządek w porównaniu z tym, co napisałem swój pierwszy program 35 lat temu. Ten wzrost oznacza, że ​​jesteśmy w stanie robić mocniejsze rzeczy w miarę upływu czasu. To jest dobre.

Miejsce, w którym był to dla mnie problem, jest całkowicie kulturalne. Moja pragmatyczna połowa rozumie, że nie można już skupiać się na każdym szczególe i być w stanie dokończyć to, co chcę zrobić. (Starzenie się też nie pomaga.) Moja cholerna połowa siwobrodego miała trudności z odpuszczeniem wielu lat tak dokładnego zrozumienia wszystkiego, nad czym pracowałem.

Czy uważasz, że młodsi programiści skorzystaliby na nauce programowania niskiego poziomu PRZED eksploracją królestw rozbudowanych bibliotek?

Jak wskazano w innych odpowiedziach, należy znaleźć równowagę między przyciąganiem i utrzymywaniem uwagi neofitów a zapewnianiem im idealnej edukacji od podstaw. Jeśli nie możesz zrobić pierwszego, drugie nie może się zdarzyć.

Widzę w naszej branży rzeczy, które są podobne do reszty społeczeństwa. Kiedyś prawie wszyscy uprawiali własne jedzenie i spędzali na tym dużo czasu. Od tego czasu rozwinęliśmy specjalistów zwanych rolnikami, którzy wykonują tę pracę, uwalniając innych do robienia innych rzeczy, które przyczyniają się do społeczeństwa. Kupuję jedzenie w sklepie spożywczym i nie byłbym w stanie samodzielnie wyprodukować większości z nich, gdybym musiał. Działamy podobnie, aczkolwiek w znacznie bardziej skompresowanej skali czasowej. Programiści specjalizują się w niektórych zestawach warstw, a nie w innych. Przeciętny facet piszący GUI może wiedzieć, że istnieje coś takiego jak przestrzeń wymiany, ale prawdopodobnie nie wie ani nie dba o to, jak system operacyjny nią zarządza.

Konsekwencją tego wszystkiego jest to, że nie chodzi już tylko o rozwój. Kontynuacja specjalizacji oznacza, że ​​programiści będą musieli doskonalić umiejętności komunikacyjne i integracyjne.

Blrfl
źródło
3

Jak ze wszystkim, trochę ci się przydaje, ale za dużo boli. Problem polega na tym, że wiele systemów nie wie, kiedy przestać - tylko 1 abstrakcja, aby pomóc Ci programować szybciej ... ale potem kodujesz w prawdziwym świecie, w którym rzeczy nigdy nie są tak proste, jak chcesz, a ty spędzaj więcej czasu na pracy wokół krawędzi, niż byś spędził z mniej polecaną abstrakcją.

Jest to dobrze opisane tutaj

lub tutaj - „za pomocą jednego wiersza kodu możesz dodać 500 użytkowników do domeny” ...

Twoje abstrakcje starają się ukryć przed tobą złożoność, ale tak naprawdę wszystko, co robią, to ukrywają tę złożoność. Złożoność wciąż istnieje, po prostu masz nad nią znacznie mniej kontroli - i właśnie dlatego kończysz w takiej sytuacji.

gbjbaanb
źródło
2

Czy młodsi programiści czerpią więcej korzyści z nauki programowania niskopoziomowego PRZED eksploracją królestw rozbudowanych bibliotek? Jeśli tak, to dlaczego?

Nie wydaje mi się Nadal jest wiele sytuacji, w których warto zdawać sobie sprawę z niskiego poziomu działania „warstwy poniżej”, np

  • Podczas debugowania problemu na warstwie nczęsto można to wyjaśnić, biorąc pod uwagę to, co dzieje się na warstwie n-1(tj. Warstwę poniżej). Domyślam się, że warstwa 0 byłaby „tranzystorami”, ale jeśli chcesz wyjaśnić problem z tranzystorami, prawdopodobnie mówiłbyś o fizyce (np. Cieple), więc może fizyka jest naprawdę na poziomie 0.

  • Podczas optymalizacji kodu (niestety) czasami pomaga obniżyć poziom abstrakcji, tj. Implementuje algorytm w zakresie warstwy niższego poziomu. Jednak kompilatory stały się naprawdę dobre w wykonywaniu tego za Ciebie, jeśli faktycznie widziały cały zaangażowany kod. Powód ten stał się ostatnio bardziej popularny z powodu boomu urządzeń mobilnych i wbudowanych, które zwykle mają słabsze procesory i gdzie „wydajność na wat” jest znacznie bardziej istotna niż, powiedzmy, systemy stacjonarne.

Zasadniczo jednak znacznie łatwiej było zmusić komputery do robienia rzeczy (nawet w nieco nieefektywny sposób), co oznacza, że ​​jest o wiele więcej programistów niż kiedyś. To z kolei sprawiło, że czynnik „ludzki” stał się znacznie ważniejszy: w odpowiedzi Roberta Harveya wspomniano już, że „ludzie mogą w każdej chwili mieć tak dużo wiedzy w głowie”, i myślę, że jest to obecnie bardzo istotny aspekt.

Główną motywacją w projektowaniu języka programowania i bibliotek (tj. API) jest ułatwienie pracy ludzkiego mózgu. Do dziś wszystko sprowadza się do kodu maszynowego. Jest to jednak nie tylko podatne na błędy, ale także niezwykle trudne do zrozumienia. Więc jest to bardzo pożądane

  • Poproś komputer o pomoc w znalezieniu błędów logicznych w pisanych programach. Pomagają w tym systemy typu statycznego lub analizatory kodu źródłowego (słyszę, że Eric Lippert pracuje obecnie na dość popularnym).

  • Posługuj się językiem, który może być skutecznie przetwarzany przez kompilator i który przekazuje intencje programisty innym programistom, aby ułatwić pracę z programem. Jako absurdalne ekstremum wyobraź sobie, że pisanie programów zwykłym angielskim było możliwe. Inni programiści mogliby łatwiej sobie wyobrazić, co się dzieje, ale opis ten byłby bardzo trudny do wkompilowania w instruktorów maszyn, co jest niezwykle dwuznaczne. Potrzebujesz więc języka, który kompilator może zrozumieć, ale który jest również zrozumiały.

Biorąc pod uwagę, że wiele (większość?) Kompilatorów jest nadal bardzo ogólnych, posiadają one bardzo ogólny zestaw instrukcji. Nie ma instrukcji „narysuj przycisk” ani „odtwarzaj ten film”. Stąd, przejście w dół hierarchii abstrakcji powoduje, że powstają programy, które są bardzo trudne do zrozumienia i utrzymania (choć banalne do skompilowania). Jedyną alternatywą jest zwiększenie hierarchii, co prowadzi do coraz większej liczby abstrakcyjnych języków i bibliotek.

Frerich Raabe
źródło
1

„jeśli starsi programiści postrzegają takie innowacje jako dar niebios lub dodatkową warstwę do wyodrębnienia, i dlaczego mogą tak myśleć?”

Programuję, odkąd byłem w szkole średniej, około 34 lat, zaczynając od Basic i Z80 Assembler, przechodząc do C, różnych języków 4GL, schematu, SQL, a teraz różnych języków internetowych. Zakres, skala i głębokość problemów, którymi zajmował się ten zawód, podlegały w tym czasie okresowi inflacji, szczególnie w latach 90. Konstrukcje, takie jak biblioteki, frameworki i usługi systemu operacyjnego, są elementami mającymi na celu rozwiązanie złożoności związanej z rozszerzoną przestrzenią problemów. Nie są one darem niebios ani ciężarem samym w sobie - są to ciągłe eksploracje ogromnej przestrzeni rozwiązań.

Ale, IMHO, „innowacja” jest lepiej rozumiana w kategoriach nowatorskich form, a nie mylona z ruchem bocznym - przywracaniem form, które już widzieliśmy. Pod pewnymi względami płodność ekosystemu cierpi, gdy prymitywne formy nie komponują się, gdy ustalają decyzje podjęte na wczesnym etapie ewolucji lub nie mogą przetworzyć własnego śladu. Niektóre, jeśli nie większość, konstruktów, na których się koncentrujemy, nie traktują priorytetowo długoterminowego utrzymania wartości jako problemu. Zaczęło się to zmieniać - podejścia takie jak orientacja na usługi i projektowanie oparte na domenie, nie wspominając o modelach hipertekstowych i graficznych, na przykład zmieniają krajobraz. Jak każdy ekosystem, ostatecznie dominujące formy ustąpią miejsca nowym formom; najlepiej służy nam różnorodność,

„A czy młodsi programiści czerpią więcej korzyści z nauki programowania niskopoziomowego PRZED eksploracją domen bibliotek ekspansywnych? Jeśli tak, to dlaczego?”

Twierdziłbym, że większość ludzkiego języka opiera się na metaforach, o których dawno zapomniano, więc chociaż poparłbym naukę programowania na niskim poziomie z naukowego / liczbowego punktu widzenia, ważniejsze jest, aby szukać prymitywów, które będą wspierać skalę i zakres problemy, które rozwiązujemy w taki sposób, że możemy bezpiecznie zignorować niższy poziom szczegółowości. Framework nie jest prymitywny, ani system operacyjny, ani biblioteka - są dość słabi w robieniu tego rodzaju abstrakcji, której naprawdę potrzebujemy. Rzeczywisty postęp zajmie ludzi, którzy (a) wiedzą, co się działo wcześniej i (b) mogą myśleć wystarczająco powieściowo, aby wymyślić coś wystarczająco odmiennego, aby zbadać przestrzeń rozwiązania, która nie była wcześniej badana lub była badana i zapomniana.

OTOH, nawet jeśli Twoim celem jest praca na poziomie technicznym / mechanicznym, pewien poziom narażenia na programowanie na niskim poziomie nadal będzie pomocny w rozwijaniu umiejętności rozwiązywania problemów.

jerseyboy
źródło
1

Mój pierwszy program (jako 15-letni nastolatek) miałem w 1974 r. W PL / 1 na kartach perforowanych do komputera mainframe IBM 370/168. Mój ojciec pracował w IBM i miałem szczęście, że mogłem chodzić do centrum danych w niedziele.

W tym czasie program składający się z kilku tysięcy instrukcji (tj. Kart dziurkowanych) był dużym programem (i również ciężkim, ponieważ wiele tysięcy kart dziurkowanych ważyło wiele kilogramów). Interfejsy wizualne nie istniały (typowy program czytany ze „standardowego wejścia” za pomocą komendy z kartą dziurkowaną rozpoczynającą się od //GO.SYSIN DD *IIRC, ale nie opanowałem JCL ). Algorytmika była ważna, a IIRC standardowa biblioteka była dość mała jak na dzisiejszy standard.

Obecnie programy składające się z kilku tysięcy linii są ogólnie uważane za małe. Na przykład kompilator GCC ma ponad dziesięć milionów wierszy kodu źródłowego i nikt nie rozumie ich w pełni.

Mam wrażenie, że dzisiejsze programowanie różni się znacznie od lat 70., ponieważ trzeba zużywać znacznie więcej zasobów (w szczególności istniejące biblioteki i ramy oprogramowania). Wydaje mi się jednak, że osoby opracowujące oprogramowanie do centrów danych (np. Wyszukiwarki w Google) lub niektóre oprogramowanie wbudowane dbają o algorytmy i wydajność tak samo, jak przeciętny programista z lat 70.

Nadal uważam, że zrozumienie programowania niskiego poziomu jest ważne nawet dzisiaj (nawet jeśli większość programistów nie koduje podstawowych algorytmów kontenera, takich jak zrównoważone drzewa, sortowane tablice sortowane dychotomicznie itp.), Ponieważ zrozumienie całego obrazu jest nadal ważne.

Główną różnicą między latami siedemdziesiątymi a dzisiejszymi jest stosunek kosztów między wysiłkiem dewelopera (człowieka) a mocą komputera.

Basile Starynkevitch
źródło