10 złych nawyków programistycznych, które potajemnie kochamy

Wszyscy to zrobiliśmy: złapaliśmy ciastko, gdy mama nie patrzyła, wypiliśmy trochę za dużo wina na obiad, zostawiliśmy samochód na parkingu po wygaśnięciu licznika. Nawet trochę za szybko pokonaliśmy Deadman's Curve. I tak, wszyscy naruszyliśmy wiele podstawowych zasad programowania, te, które wszyscy zgadzają się, są złe. I potajemnie nam się podobało.

Podpięliśmy sobie zasady dobrego programowania, wypisaliśmy kod, który jest całkowicie zły - i przeżyliśmy. Nie było błyskawic od bogów programowania. Nasze komputery stacjonarne nie eksplodowały. W rzeczywistości nasz kod został skompilowany i wysłany, a klienci wydawali się wystarczająco zadowoleni.

Dzieje się tak dlatego, że złe programowanie nie jest w tej samej lidze, co, powiedzmy, lizanie ogrodzenia elektrycznego lub ciągnięcie za ogon tygrysa. W większości przypadków to działa. Reguły są częściej wskazówkami lub sugestiami stylistycznymi, a nie sztywnymi dyrektywami, których należy przestrzegać, w przeciwnym razie nastąpi śmierć kodu. Jasne, twój kod może zostać wyśmiany, być może nawet publicznie. Ale fakt, że łamiesz konwencje, dodaje odrobinę emocji do podważania, nawet nieumyślnie, tego, co sprowadza się (najczęściej) do społecznych obyczajów przyjemnego kodu.

Aby sprawy były bardziej skomplikowane, czasami lepiej jest złamać zasady. (Ćśśś!) Kod jest bardziej przejrzysty. Może być nawet szybsze i prostsze. Reguły są zwykle nieco za szerokie i pomysłowy programista może ulepszyć kod, łamiąc je. Nie mów szefowi, ale czasami warto zaprogramować swój własny sposób.

Poniżej znajduje się lista dziewięciu zasad, które niektórzy mogą uważać za nieskazitelne, ale wielu z nas często łamie, z powodzeniem i przyjemnością.

Zły nawyk programowania nr 1: Kopiowanie 

Źle to robić w szkole. W pracy zasady nie są tak jasne. Z pewnością istnieje kilka bloków kodu, których nie należy kraść. Jeśli pochodzi z zastrzeżonego kodu, nie składaj go do swojego stosu, zwłaszcza jeśli jest oznaczony informacją o prawach autorskich. Napisz własną wersję. Za to ci płacą.

Trudniejsze pytanie pojawia się, gdy oryginalny twórca chce się udostępnić. Być może jest na jednym z internetowych forów programistycznych. Być może jest to kod typu open source z licencją (BSD, MIT), która pozwala na przechwycenie jednej lub trzech funkcji. Nie ma żadnego prawnego powodu, aby cię powstrzymać. Płacą ci za rozwiązywanie problemów, a nie odkrywanie koła na nowo.

W większości przypadków zalety kopiowania są nieodparte, a wady można ograniczyć przy odrobinie ostrożności. Do kodu, który otrzymujesz z renomowanego źródła, zastosowano już co najmniej jedną rundę przemyśleń. Oryginalny autor szukał rozwiązania i coś znalazł. Opracowano niezmienniki pętli i przepływ danych.

Podchwytliwe pytania dotyczą tego, czy istnieją jakieś bezpodstawne błędy lub inne założenia dotyczące roli lub danych, które są jej podstawą. Być może twój kod miesza się w zerowe wskaźniki, podczas gdy oryginalny kod nigdy ich nie sprawdzał. Jeśli możesz rozwiązać problemy, to tak, jakby twój szef pobierał informacje od dwóch programistów. To programowanie w parach bez wymyślnych pulpitów.

Zły nawyk programowania nr 2: niefunkcjonalny kod

Przez ostatnie dziesięć lat paradygmat funkcjonalny wzrastał. Akolici budujący program z zagnieżdżonych wywołań funkcji uwielbiają cytować badania pokazujące, że kod jest bezpieczniejszy i bardziej wolny od błędów niż starszy styl zmiennych i pętli, a wszystko to połączone w jakikolwiek sposób, który uszczęśliwia programistę. Wielbiciele mówią z zapałem prawdziwie wierzących, karcąc niefunkcjonalne podejścia w przeglądach kodu i żądaniach ściągnięcia. Mogą nawet mieć rację co do zalet.

Ale czasami wystarczy wyjąć rolkę taśmy klejącej. Cudownie zaprojektowany i wdzięcznie zaplanowany kod wymaga czasu, nie tylko do wyobrażenia, ale także do konstruowania, a później do nawigacji. Wszystkie te warstwy zwiększają złożoność, a złożoność jest kosztowna. Twórcy pięknego kodu funkcjonalnego muszą planować z wyprzedzeniem i upewnić się, że wszystkie dane są przekazywane odpowiednimi ścieżkami. Czasami po prostu łatwiej jest sięgnąć i zmienić zmienną. Może zamieść komentarz, aby to wyjaśnić. Nawet dodanie w komentarzu długich, płaczących przeprosin dla przyszłych pokoleń jest szybsze niż przeprojektowanie całego systemu, aby zrobić to we właściwy sposób.

Zły nawyk programowania nr 3: Niestandardowe odstępy

Większość miejsc w oprogramowaniu nie ma wpływu na działanie programu. Z wyjątkiem kilku języków, takich jak Python, które używają odstępów do oznaczania bloków kodu, większość spacji nie ma żadnego wpływu na zachowanie programu. Mimo to istnieją obsesyjni programiści, którzy je liczą i twierdzą, że mają znaczenie. Jeden z nich powiedział kiedyś mojemu szefowi najpoważniejszym tonem, że piszę „Kod niestandardowy”, a on od razu to zauważył. Mój grzech? Naruszenie zasady ESLint space-infix-ops przez nieumieszczenie spacji po obu stronach znaku równości.

Czasami wystarczy pomyśleć o czymś głębszym niż rozmieszczenie przestrzeni. Może martwisz się, że baza danych zostanie przeciążona. Może martwisz się, że pusty wskaźnik może spowodować awarię twojego kodu. Prawie każda część kodu jest ważniejsza niż spacje, nawet jeśli neb-nosowe, władcze komitety normalizacyjne wypełniły strony z zasadami dotyczącymi umieszczania tych spacji lub tabulatorów.

Niesamowite jest to, że istnieje kilka dobrych narzędzi, które automatycznie przeformatują Twój kod, aby był zgodny z dobrze zdefiniowanymi regułami lintingu. Ludzie nie muszą tracić czasu na myślenie o tym. Jeśli jest to tak ważne, mogą uruchomić to narzędzie, aby usunąć problem.

Zły nawyk programowania nr 4: Używanie goto

Zakaz używania gotodat sięgał czasów sprzed istnienia wielu narzędzi programowania strukturalnego. Gdyby programiści chcieli utworzyć pętlę lub przeskoczyć do innej procedury, musieliby wpisać, GOTOa po nim numer linii. Po kilku latach zespoły kompilatorów pozwalają programistom używać etykiety łańcuchowej zamiast numeru wiersza. Wtedy uważano to za nową, gorącą funkcję.

Niektórzy nazywali wynik „kodem spaghetti”. Nikt nie mógł później przeczytać twojego kodu i podążać ścieżką wykonania. Była to plątanina wiecznie splątanych nici. Edsger Dijkstra zdelegalizował polecenie, wydając manuskrypt żartobliwie zatytułowany „Goto Statement uważane za szkodliwe”.

Ale absolutne rozgałęzienie nie jest problemem. To plątanina, która wynika. Często pomysłowe breaklub returnbardzo przejrzyste oświadczenie o tym, co kod robi w tym miejscu. Czasami dodanie gotodo instrukcji case prowadzi do czegoś, co jest prostsze do zrozumienia niż bardziej uporządkowana lista kaskadowych bloków if-then-else.

Istnieją kontrprzykłady. Dziura w zabezpieczeniach typu „niepowodzenie goto” w stosie SSL firmy Apple jest jednym z najlepszych przykładów. Ale jeśli będziemy uważać, aby uniknąć niektórych trudnych problemów z opisami przypadków i pętlami, możemy wstawić dobre, absolutne skoki, które ułatwią czytelnikowi zrozumienie, co się dzieje. Możemy dodać breaklub, returnktóre jest czystsze i przyjemniejsze dla wszystkich - może z wyjątkiem gotonienawidzących.

Zły nawyk programowania nr 5: Brak deklarowania typów

Ludzie, którzy kochają języki maszynowe, mają rację. Piszemy lepszy, bardziej wolny od błędów kod, dodając jasne deklaracje typu danych każdej zmiennej. Zatrzymanie się na chwilę w celu przeliterowania typu pomaga kompilatorowi oznaczyć głupie błędy, zanim kod zacznie działać. Może to być bolesne, ale pomaga. To podejście do programowania oparte na paskach i szelkach, które eliminuje błędy.

Czasy się zmieniły. Wiele nowszych kompilatorów jest na tyle sprytnych, aby wywnioskować typ na podstawie kodu. Mogą pracować wstecz i do przodu w kodzie, dopóki nie będą pewni, że zmienna musi być a, stringan intlub czymś innym. A jeśli te wywnioskowane typy nie są zgodne, kompilatory mogą podnieść flagę błędu. Nie potrzebują już, abyśmy wpisywali zmienne.

Oznacza to, że teraz łatwiej jest zaoszczędzić kilka bitów, pomijając niektóre z najprostszych deklaracji. Kod staje się nieco bardziej przejrzysty, a czytelnik jest zazwyczaj w stanie odgadnąć, że zmienna nazwana iw pętli for jest liczbą całkowitą.

Zły nawyk programowania nr 6: kod jo-jo

Programiści lubią nazywać to „kodem jo-jo”. Najpierw wartości są przechowywane jako łańcuchy. Następnie są przetwarzane na liczby całkowite. Następnie są przekształcane z powrotem w struny. To strasznie nieefektywne. Możesz prawie poczuć walkę procesora pod całym dodatkowym obciążeniem. Inteligentni programiści, którzy piszą szybki kod, projektują swoje architektury tak, aby zminimalizować konwersje. Ich kod działa szybciej z powodu ich planowania.

Ale wierz lub nie, czasami ma to sens. Czasami masz bibliotekę typu whiz-bang, która robi miliardy inteligentnych rzeczy w swojej zastrzeżonej czarnej skrzynce. Czasami szef wypisał siedmiocyfrowe czek, aby wydać licencję na wszystkich geniuszy w czarnej skrzynce. Jeśli biblioteka chce danych w łańcuchach, przekazujesz ją do biblioteki w łańcuchach, nawet jeśli niedawno przekonwertowałeś je na liczby całkowite.

Jasne, możesz przepisać cały kod, aby zminimalizować konwersję, ale zajmie to trochę czasu. Czasami wystarczy, że kod będzie działał dodatkową minutę, godzinę, dzień lub nawet tydzień, ponieważ przepisanie kodu zajmie jeszcze więcej czasu. Czasami spłacanie długu technicznego jest tańsze niż jego budowanie w pierwszej kolejności.

Czasami biblioteka nie jest zastrzeżonym kodem, ale kod, który sam napisałeś dawno temu. Czasami szybciej przekonwertować dane jeszcze raz niż przepisać wszystko w tej bibliotece. Więc idziesz dalej i piszesz kod jo-jo. W porządku - wszyscy tam byliśmy.

Zły nawyk programowania nr 7: Pisanie własnych struktur danych

Jedną ze standardowych zasad jest to, że programista nigdy nie powinien pisać kodu do przechowywania danych po ukończeniu kursu struktur danych na drugim roku studiów. Ktoś inny napisał już wszystkie struktury danych, których kiedykolwiek będziemy potrzebować, a jego kod był przez lata testowany i ponownie testowany. Jest dołączony do języka i prawdopodobnie jest bezpłatny. Twój kod może zawierać tylko błędy.

Ale czasami biblioteki struktur danych są nieco powolne. Czasami zmuszają nas do wprowadzenia struktury, która może być standardowa, ale niewłaściwa dla naszego kodu. Czasami biblioteki zmuszają nas do rekonfiguracji naszych danych, zanim użyjemy struktury. Czasami biblioteki zawierają zabezpieczenia na pasku i szelkach z funkcjami takimi jak blokowanie wątków, a nasz kod ich nie potrzebuje.

Kiedy tak się stanie, nadszedł czas, aby napisać własne struktury danych. Czasami jest to dużo, dużo szybsze. Czasami sprawia to, że nasz kod jest znacznie czystszy, ponieważ nie uwzględniamy całego dodatkowego kodu służącego do ponownego formatowania danych.

Zły nawyk programowania nr 8: staromodne pętle

Dawno temu ktoś tworzący język C chciał zawrzeć wszystkie abstrakcyjne możliwości w jednej prostej konstrukcji. Na początku było kilka rzeczy do zrobienia, kilka rzeczy do zrobienia za każdym razem w pętli i jakiś sposób, aby powiedzieć, kiedy wszystko się skończyło. W tamtym czasie wydawało się, że składnia jest idealnie czysta, aby uchwycić nieskończone możliwości.

To było wtedy. Teraz niektóre współczesne łajdy widzą tylko kłopoty. Za dużo się dzieje. Wszystkie te możliwości dobra są równie zdolne do zła. To sprawia, że ​​czytanie i narzekanie jest o wiele trudniejsze. Uwielbiają bardziej funkcjonalny paradygmat, w którym nie ma pętli, tylko funkcje stosowane do list, szablony obliczeniowe mapowane na niektóre dane.

Są chwile, kiedy metoda bez pętli jest czystsza, zwłaszcza gdy jest tylko jedna zgrabna funkcja i tablica. Ale są chwile, kiedy staromodna pętla jest znacznie prostsza, ponieważ może zrobić znacznie więcej. Na przykład wyszukiwanie pierwszego dopasowania jest prostsze, gdy możesz przerwać, gdy tylko zostanie znaleziony.

Ponadto funkcje mapowania zachęcają do niechlujnego kodowania, gdy istnieje wiele rzeczy do zrobienia z danymi. Wyobraź sobie, że chcesz wziąć wartość bezwzględną, a następnie pierwiastek kwadratowy z każdej liczby. Najszybszym rozwiązaniem jest mapowanie pierwszej funkcji, a następnie drugiej, dwukrotne zapętlenie danych. 

Zły nawyk programowania nr 9: wyrwanie się z pętli w środku

Gdzieś po drodze grupa tworząca reguły zadeklarowała, że ​​każda pętla powinna mieć „niezmiennik”, czyli logiczne stwierdzenie, które jest prawdziwe w całej pętli. Gdy niezmiennik nie jest już prawdziwy, pętla się kończy. To dobry sposób na myślenie o złożonych pętlach, ale prowadzi to do szalonych zakazów - takich jak zakaz używania a returnlub a breakw środku pętli. Jest to podzbiór reguły zakazującej gotowypowiedzi.

Ta teoria jest dobra, ale zwykle prowadzi do bardziej złożonego kodu. Rozważmy ten prosty przypadek, który skanuje tablicę w poszukiwaniu jednego wpisu, który przechodzi test:

podczas gdy ja
   
    

   ...

   if (test (a [i]) to zwróć a [i];

   ...

}

Miłośnicy niezmiennych pętli woleliby, abyśmy dodali kolejną zmienną boolowską, wywołali ją notFoundi użyli w ten sposób:

podczas gdy ((notFound) && (i
   
    

...

if (test (a [i])) then notFound = false;

...

}

Jeśli ta wartość logiczna jest dobrze nazwana, jest to świetny fragment samodokumentującego się kodu. Może to ułatwić wszystkim zrozumienie. Ale to także dodatkowa złożoność. A to oznacza przydzielenie innej zmiennej lokalnej i zapchanie rejestru, co kompilator może, ale nie musi, być wystarczająco inteligentny, aby naprawić.

Czasami gotoskok lub skok jest czystszy.

Zły nawyk programowania nr 10: Przedefiniowanie operatorów i funkcji

Niektóre z najbardziej zabawnych języków pozwalają ci robić naprawdę przebiegłe rzeczy, takie jak przedefiniowanie wartości elementów, które wyglądają tak, jakby powinny być stałe. Na przykład Python pozwala pisać TRUE=FALSE, przynajmniej w wersji 2.7 i wcześniejszych. To nie powoduje jakiegoś logicznego upadku i końca wszechświata; po prostu zamienia znaczenie TRUEi FALSE. Możesz także grać w niebezpieczne gry, takie jak ta, używając preprocesorów C i kilku innych języków. Jeszcze inne języki pozwalają na przedefiniowanie operatorów, takich jak znak plus.

To jest rozciągnięcie, ale w dużym bloku kodu pojawią się punkty, w których szybciej będzie można przedefiniować jedną lub więcej z tych tak zwanych stałych. Czasami szef chce, aby kod robił coś zupełnie innego. Jasne, możesz przepracować kod i zmienić każde wystąpienie lub przedefiniować rzeczywistość. Może sprawić, że będziesz wyglądać jak geniusz. Zamiast przepisywać ogromną bibliotekę, po prostu trochę przewracasz i robi się odwrotnie.

Być może dobrze jest tutaj wyznaczyć granicę. Nie powinieneś próbować tego w domu, bez względu na to, jak sprytne i zabawne może to być. To zbyt niebezpieczne - naprawdę ... szczerze.