Jak przezwyciężyć programowanie przez przypadek? [Zamknięte]

24

W książce The Pragmatic Programmer pisarze wspominają o programowaniu przez przypadek . Wyjaśnia, co to jest, dlaczego jest spowodowany, jakie niebezpieczeństwa możesz napotkać, i porównuje to z polem minowym podczas wojny.

Czy oglądasz kiedyś czarno-białe filmy wojenne? Zmęczony żołnierz ostrożnie wysuwa się z zarośli. Przed nami polana: czy są jakieś miny, czy można bezpiecznie przejść? Nic nie wskazuje na to, że jest to pole minowe - żadnych znaków, drutu kolczastego ani kraterów. Żołnierz szturcha go bagnetem ziemi i krzywi się, oczekując eksplozji. Nie ma jednego Tak więc przez chwilę ostrożnie przechodzi przez pole, szturchając i szturchając go. W końcu, przekonany, że pole jest bezpieczne, prostuje się i maszeruje dumnie do przodu, by zostać roztrzaskanym na strzępy.

Początkowe sondy żołnierza do wykopalisk nic nie ujawniły, ale było to po prostu szczęście. Doprowadzono go do fałszywego wniosku - z katastrofalnymi skutkami.

Jako programiści pracujemy również na polach minowych. Każdego dnia czekają na nas setki pułapek. Pamiętając opowieść o żołnierzu, powinniśmy uważać na wyciąganie fałszywych wniosków. Powinniśmy unikać programowania przez przypadek - polegając na szczęściu i przypadkowych sukcesach - na rzecz programowania celowo ...

Ale nie jestem bardzo zadowolony ze sposobu, w jaki opisują kwestię „jak to przezwyciężyć”. Tak, musisz pomyśleć przed napisaniem kodu, ale jak to ćwiczyć? Jedyne, co mogę myśleć, to dodanie funkcji do istniejących projektów Open Source, w których musisz mieć wiedzę zarówno na temat „tego, co robię teraz”, jak i „jak działają inne fragmenty kodu”, i nie ma to zastosowania kiedy piszesz własne projekty.

EDYTOWAĆ:

streszczenie z twoich postów:

  • Nie zgaduj następnego ruchu, udowodnij, że jest poprawny
  • W razie potrzeby test jednostkowy i refaktoryzacja w miarę możliwości
  • Często dodawaj funkcje - kompiluj - testuj
  • Jeśli nie potrafisz wyjaśnić kodu noobowi, prawdopodobnie programujesz przez przypadek.

BTW, trudno jest zaakceptować odpowiedź, to naprawdę trudne. Wszystkie odpowiedzi są naprawdę świetne :)

py_script
źródło
6
Pomogłoby to osobom, które mogły nie czytać książki przez długi czas, mieć link taki jak pragprog.com/the-pragmatic-programmer/extracts/coincidence .
btilly,
Jeśli nie masz bardzo krótkiej kariery programistycznej (lub jednoosobowego sklepu), prawdopodobnie natkniesz się na kod, który wygląda dziwnie znajomo, a później trochę chodzenia po kodzie, pens spada: jest twój. To nie tylko kwestia Open Source ...
Robbie Dee,
@Robbie Dee. czy możesz to trochę wyjaśnić? Nie rozumiem cię. Rzeczywiście mam krótką karierę programistyczną i to jest powód tagu młodszego programisty.
py_script
2
@py_script Chciałem tylko podkreślić, że lata później równie łatwo można natknąć się na własny kod (i być nim oszukanym), jak kod kogoś innego. Więc jeśli zaczniesz od dobrych nawyków, to później wypłaci dywidendy.
Robbie Dee,

Odpowiedzi:

26

Nie musisz myśleć z wyprzedzeniem, po prostu bądź bardzo jasny na temat tego, co zostało zrobione, i bądź bardzo jasny na temat tego, co robisz teraz.

Podprogramy powinny mówić, co robią, robić to, co mówią i nie mieć ukrytych zależności. Wtedy ktoś, kto do nich zadzwoni, może łatwiej zrozumieć, co zrobi.

Unikaj stanu globalnego. (Zmienne, singletony itp.) Im więcej musisz mieć w głowie, aby zrozumieć, co się dzieje, tym trudniej jest zrozumieć, co się stanie i znaleźć przypadki skrajne.

Napisz testy jednostkowe. Testy jednostkowe świetnie nadają się do przechwytywania rzeczywistego zachowania właśnie napisanego kodu, a nie do idealnego zachowania, które chcesz znaleźć.

Skróć cykl edycji / kompilacji / testu. Gdy dodasz dużą część kodu i źle przetestujesz, istnieje prawdopodobieństwo, że będzie on zachowywać się inaczej niż myślisz. Następnie „naprawiasz” go losową zmianą i na chwilę masz właściwą odpowiedź, ale nie masz pojęcia, jak to się naprawdę stało. Programujesz teraz przez przypadek. Ale gdy dodasz 5 wierszy, a następnie przetestujesz, szanse na poprawną odpowiedź, ponieważ działa tak, jak myślisz, są lepsze. Mogę powiedzieć z doświadczenia, że ​​5 fragmentów po 10 linii każdy, indywidualnie testowanych, to zupełnie inna bestia niż 50 linii kodu testowanych jednocześnie.

Refaktoryzujcie bezwzględnie. Wiele razy zauważyłem refaktor, który sprawi, że mój kod będzie nieco prostszy, ale zajmie się pracą, której nie chciałem wykonywać. Po tym, jak zacząłem celowo zajmować się tymi refaktorami w pierwszej kolejności, przekonałem się, że zazwyczaj zwraca się ono w ciągu miesiąca. Zwróć jednak uwagę na klucz. Refaktory, na których się skupiam, to takie, które upraszczają codzienne życie, a nie takie, które spełniają dowolną estetykę lepszej lub bardziej ogólnej. Te refaktory, na których nauczyłem się być znacznie ostrożniejszym.

Żadna z tych rzeczy nie wymaga wcześniejszego planowania. Ale wszystkie ułatwiają zrozumienie istniejącego kodu, a tym samym ułatwiają celową implementację następnego małego fragmentu.

btilly
źródło
Dzięki, naprawdę dobra odpowiedź. Myślę, że część cyklu jest naprawdę cenna. Czy możesz podać mi przykłady refaktoryzacji, które powinieneś zrobić, ale ich wdrożenie zajmie naprawdę dużo czasu, co może zniechęcić kogoś?
py_script
1
Mam wiele przykładów. W projekcie C ++ stworzyłem klasy bez metod rygorystycznych. Po ich utworzeniu debugowanie stało się łatwiejsze i przyspieszyłem rozwój. W projekcie Perla mieliśmy skrót konfiguracji, w którym każdy programista miał własną poprawioną kopię każdej nowej konfiguracji. Dodawanie parametrów konfiguracyjnych było uciążliwe, ponieważ trzeba było edytować konfigurację każdego programisty. Napisałem system szablonów dla skrótów i stało się to łatwiejsze. W systemie raportowania dodałem funkcję pokazującą wyniki pośrednie. Mój rozwój przyspieszył, a nawet dostałem raport o błędzie użytkownika ...
btilly,
1
gdzie dział finansowy prześledził moją logikę i znalazł dokładne zapytanie, w którym popełniłem błąd i jaki był mój błąd. (Przypadkowo zgniatałem zduplikowane rzędy UNIONtam, gdzie potrzebowałem UNION ALL.) I tak dalej.
btilly,
1
W ciągu miesiąca? Zazwyczaj uważam, że za każdym razem, gdy dotykam kodu, powinienem go zrefaktoryzować, ale nie dokonałem refaktoryzacji, zajmuje to prawie tyle samo czasu, ile by go refaktoryzował.
Amy Blankenship
1
@AmyBlankenship Tak. W ciągu miesiąca. Czasami śmiesznie w środku, czasem nie. Plik konfiguracyjny, o którym wspomniałem powyżej, jest przykładem „czasami nie”. Kilka dni zajęło mi napisanie, udokumentowanie i przetestowanie nowego silnika szablonów. Następnie przepisz istniejącą konfigurację, aby z niej skorzystać, i będzie znacznie krótsza, ale stworzy tę samą dokładną strukturę danych. (To była najtrudniejsza część projektu.) Więc ... zmarnowane dni, bez absolutnie żadnego widocznego rezultatu. Jednak wysiłek opłacił się w niecały miesiąc, ale od tego czasu zarabia.
btilly,
41

Sprowadza się to prawie do zgadywania . Robią to głównie nowi programiści, ale widziałem też weteranów, ponieważ myślą, że to oszczędza czas na badania. Coś nie działa, więc dodajesz a +1lub a -1, zmieniasz truena a falselub odwrotnie, porządkujesz niektóre instrukcje, dodajesz lub zmieniasz opóźnienia, zmieniasz priorytety wątków i inne małe transformacje, w zasadzie testując losowe permutacje, aż to zadziała.

Dotyczy to głównie zmiany istniejącego kodu, ale jest również czynnikiem w nowym kodzie, ponieważ nikt tak naprawdę nie zaczyna od zera. Zawsze opierasz się na standardowych bibliotekach, systemach operacyjnych lub przynajmniej architekturze procesorów.

Innymi słowy, nie skończysz, dopóki nie dowiesz się, dlaczego poprawka działa. Droga, którą podążasz, aby tam dotrzeć, nie ma tak wielkiego znaczenia. Nawet przypadkowe permutacje mogą czasem być pomocne w zawężeniu błędu, który jest trudny do zdiagnozowania, pod warunkiem, że poświęcisz trochę czasu, by zadać sobie pytanie: „Dobra, zmieniając wartość true na false, naprawiłem to, ale dlaczego?”

Karl Bielefeldt
źródło
1
Doskonały punkt +1. Nigdzie nie dotyczy to więcej niż poprawek kodu ...
Robbie Dee,
Niestety dotyczy to również mnie. Szczerze mówiąc, istnieją obiektywne trudności, takie jak brak dokumentacji. Dzisiaj trochę poprawiłem swój kod i doszedłem do tego, że nie wiedziałem, do czego przydatny jest parametr, ponieważ brakowało dokumentacji. Wiemy tylko, że to liczba.
py_script
Przyznaję. Kiedy stoi pochylony syndrom wykałaczki, może łatwiej będzie układać \ s, dopóki nie działa, niż jest, aby dowiedzieć się, jak wiele warstw ucieczki jesteś twierdząc, ze ...
btilly
16

Najbardziej przerażającym komentarzem, jaki kiedykolwiek spotkałem w programie, był

Nie dotykaj tego. To działa. Nie wiemy jak i dlaczego, ale to działa. 1

i to jest przerażające tylko dlatego, że przyznaje, że fragment kodu był wynikiem programowania przez przypadek .

Aby uniknąć programowania przez przypadek, powinieneś być w stanie wyjaśnić (współpracownikowi, tobie lub gumowej kaczce ) dokładnie, co robi kod i dlaczego działa. Punktory do celowego programowania są głównie pomocą w dążeniu do celu, jakim jest możliwość wyjaśnienia kodu.


1 W przypadku kontekstu ten komentarz pojawił się w kodzie, który obsługuje przełączniki kontekstu w prymitywnym systemie operacyjnym. Kod był już produkowany przez kilka lat, kiedy go spotkałem.

Bart van Ingen Schenau
źródło
2
nazywa się to również kodowaniem kurczaka voodoo c2.com/cgi/wiki?VoodooChickenCoding
minusSeven
1
Nawet jeśli programista uważa, że ​​to prawda, tego rodzaju komentarz jest ogromnie nieprzydatny. Kod mógł być skomplikowany, ale jeśli inaczej przeczytasz kod, możesz pomyśleć, że był prosty. Jedyny komentarz to zwiększenie paranoi!
Robbie Dee,
3
Może się to zdarzyć również przy utrzymywaniu starej bazy kodu, w języku, w którym większość obecnych zespołów programistów nie czuje się dobrze.
pcurry
Gumowa kaczka służy nie tylko do debugowania. Fajnie ... Myślę, że pracujemy nad tą samą firmą, mamy wiele takich komentarzy: P
py_script
zdarzają się sytuacje, w których poprawka działa po prostu z powodu usterki w interfejsie API i nie ma lepszej i logicznej poprawki. Debugowanie niektórych skompilowanych bibliotek stron trzecich może być tak głębokie jak debugowanie jądra. Nawet jeśli znalazłeś problem, po godzinach debugowania niewiele możesz zrobić. Więc podchodzisz do problemu inaczej. Przyjmujesz model „czarnej skrzynki”, który wymusza programowanie przez przypadek. Bawisz się dziwnym zachowaniem czarnej skrzynki i jeśli uda ci się sprawić, by działała tak, jak chcesz, WIELKIE, dodaj komentarz z „magią nie dotykaj” i przejdź dalej.
Radu Simionescu
7

Dla nowych programistów najważniejszą częścią przezwyciężenia tego jest prawdziwe zrozumienie tego, co robią.

W wielu obszarach, gdy nie wiesz, jak coś zrobić, po prostu przechodzisz próbę i błąd. Spróbuj czegoś; jeśli to działa, świetnie, jeśli nie, spróbuj czegoś innego.

W programowaniu, szczególnie przy użyciu języka, który ma pojęcie niezdefiniowanego zachowania (takiego jak C lub C ++), to podejście po prostu nie działa, ponieważ sukces nie jest już decyzją typu boolean. Możesz mieć rzeczy, które „w pewnym sensie” działają, które czasami działają, które działają dla niektórych danych wejściowych, ale nie dla innych.

Czasami szkoliłem nowych programistów, a tendencja do pisania losowych rzeczy, aby sprawdzić, czy to działa, jest powszechna. Wpisują wiersz, a następnie zwracają się do mnie i pytają: „Czy to działałoby w ten sposób?” podczas gdy było jasne, że nie mieli absolutnie pojęcia, czy to możliwe.

Na wynos jest to, że jako nowy programista naprawdę musisz być w stanie wyjaśnić, co robi Twój kod. Musisz nauczyć się czytać kod, a nie tylko pisać.

(Oczywiście dotyczy to również doświadczonych programistów, ale moje doświadczenie tutaj dotyczy głównie nowych kompletnych początkujących).

Sebastian Redl
źródło
<< Musisz nauczyć się czytać kod, a nie tylko go pisać. >> Więc, jeśli chodzi o moje pierwsze pytanie, czy uważasz, że pomoże mi to dodać funkcje do projektów open source?
py_script
2

Zbyt łatwo jest kodować, testować i naprawiać „na linii wyścigowej”. Masz jakąś funkcjonalność, która dała X i Y, tworzy Z. Ale co, jeśli X jest uszkodzony, a Y jest niedostępny? Co jeśli nie możesz wyprowadzić Z? Zawsze pamiętaj o tym, co może pójść nie tak, i zanotuj to w cyklu testowym.

Rób krótkie i opisowe procedury - najlepszy kod wymaga niewielu (jeśli w ogóle) komentarzy.

Znaczące nazwy metod, klas i zmiennych znacznie ułatwiają czytelność.

Jeśli natrafisz na zapach kodu, zatrzymaj się. Ten zapach raczej nie zniknie, a odrobina wysiłku może teraz zaoszczędzić Ci ogromnej ilości żalu.

Uwzględnij testy w swoim procesie rozwoju. Byłbym zwolennikiem używania BDD zamiast TDD, ponieważ zmusza cię to do opisania tego, co chcesz osiągnąć, zamiast ślepo polegać na szeregu testów, które mogłyby dać fałszywe poczucie bezpieczeństwa.

Oprzyj się pokusie dodania dodatkowych fajnych funkcji (chyba że jest to twój własny projekt dla zwierząt). Każdy dodatkowy kod musi być zaprojektowany, napisany, przetestowany i utrzymany. Jeśli nie jest to wymagane przez klienta / firmę - ryzykujesz, że stanie się to ogromnym obciążeniem zasobów.

Robbie Dee
źródło