Pisanie solidnego kodu vs. nadinżynieria


33

Skąd wiecie, że piszecie najbardziej niezawodny kod bez nadmiernej inżynierii?

Uważam, że za dużo myślę o każdej możliwej ścieżce, którą może podążać mój kod, i czasami wydaje mi się to stratą czasu. Wydaje mi się, że zależy to od rodzaju pisanego programu, ale nie chcę poświęcać zbyt wiele czasu, biorąc pod uwagę sytuacje, które nigdy się nie zdarzają.


2
Kod w Agda2
SK-logic

konkretny przykład bardzo pomógłby w zrozumieniu twojego zdania. :)
João Portela

Czy mogę po prostu sprawdzić, czy naprawdę pytasz o solidność, tj. „Zdolność systemu do kontynuowania pracy w obecności nieprawidłowych danych wejściowych lub stresujących warunków środowiskowych”, ponieważ niektóre odpowiedzi wydają się sądzić, że mówisz o możliwości rozszerzenia.
DJClayworth

Pracuję w szalonych terminach, a do tego jest wersja demo, więc mogę z radością szybko się wyrywać bez perfekcyjnego paraliżu.
Job

1
Oto artykuł, który mówi na ten temat: code-tag.com/2017/04/02/…
San

Odpowiedzi:


39

Skąd wiecie, że piszecie najbardziej niezawodny kod bez nadmiernej inżynierii?

Co uważasz za solidny kod? Kod, który jest już gotowy na przyszłość i tak potężny, że poradzi sobie w każdej sytuacji? Źle, nikt nie może przewidzieć przyszłości! I znowu źle, ponieważ będzie to skomplikowany, niemożliwy do utrzymania bałagan.

Stosuję różne zasady: przede wszystkim YAGNI (jeszcze) i KISS , więc nie piszę niepotrzebnego kodu. To również skutecznie zapobiega nadmiernej inżynierii. Refaktoryzuję aplikację, gdy potrzebne są rozszerzenia. Nowoczesne narzędzia do refaktoryzacji pozwalają dość łatwo tworzyć interfejsy i wymieniać implementacje, gdy są potrzebne.

Następnie staram się, aby kod, który piszę, był jak najbardziej niezawodny, co obejmuje eliminację tylu ścieżek, które program może obrać (i również stanów), jak to możliwe, i trochę programowania Spartan . Dużą pomocą są funkcje / metody „atomowe”, które nie polegają na stanach zewnętrznych lub przynajmniej nie pozostawiają programu w niespójnym stanie, gdy zawodzą. Jeśli zrobisz to dobrze, jest bardzo mało prawdopodobne, że kiedykolwiek skończysz z kodem spaghetti i jest to błogosławieństwo dla łatwości konserwacji. Ponadto w projektowaniu obiektowym zasady SOLID są doskonałym przewodnikiem po solidnym kodzie.

Naprawdę dowiedziałem się, że często możesz zmniejszyć złożoność, na przykład kombinatoryczne wybuchy ścieżek lub stanów programu, głęboko zastanawiając się, jak zaprojektować to jako najprostszą możliwą ścieżkę. Staraj się ograniczyć możliwe kombinacje do minimum, wybierając najlepszą kolejność podprogramów i projektując je do tego celu.

Solidny kod to zawsze prosty i przejrzysty kod, ale prostota jest cechą, która nie zawsze jest łatwa do osiągnięcia. Jednak powinieneś do tego dążyć. Zawsze po prostu pisz najprostszy możliwy kod i dodawaj złożoności tylko wtedy, gdy nie masz innego wyboru.

Prostota jest solidna, złożoność delikatna.

Złożoność zabija.


2
Ponieważ nie ma wielu klas, fabryk i abstrakcji. To paradoks, ale niektórzy lubią takie rzeczy. Nie mam pojęcia dlaczego.
Koder,

5
To jest Sparta!!!
Tom Squires,

4
Ludzie, którzy nie robili tego od dwudziestu lat, po prostu nie rozumieją, jak złożoność może cię zabić. Myślą, że są tacy mądrzy. Są głupie, a nie mądre. Ta złożoność zabije cię martwego.
PeterAllenWebb

1
Wytrzymałość nie polega na zabezpieczeniu na przyszłość - chodzi o dalsze działanie przy nieprawidłowych danych wejściowych lub stresujących środowiskach - Code Complete p464.
DJClayworth,

5
O ile pytający nie używa słowa „solidny” w innym sensie niż ten, który rozumiem, odpowiadasz na inne pytanie. Nie pyta „czy powinienem kodować, aby uwzględnić przyszłe wymagania”, pyta „jakie nietypowe przypadki wejściowe powinienem obsługiwać”. Żadne z YAGNI, KISS i SOLID nie są istotne. Czy musisz pozwolić milionowi użytkowników próbujących zalogować się jednocześnie? Co się stanie, jeśli nazwa logowania zacznie się od ukośnika odwrotnego? Na żadne z tych pytań YAGNI nie odpowiada.
DJClayworth

8

Staram się zachować równowagę, koncentrując się na

  • obsługa wszystkich możliwych ścieżek wykonania w istniejących przypadkach użycia (jest to część „niezawodności”),
  • włączanie funkcji / wymagań Jestem dość pewien, że nadejdą w dającej się przewidzieć przyszłości, oraz
  • rzeczy, które wiem z doświadczenia, które będą potrzebne do długoterminowej konserwacji bazy kodu (tj. utrzymanie kodu w czystości i możliwości jego testowania).

Jest to niewyraźny obszar graniczny - czasem udaje mi się wykonać trochę niepotrzebnej pracy, czasem nie udaje mi się zrobić czegoś, co później okazuje się konieczne. Jeśli tęsknoty nie są duże, wszystko w porządku. W każdym razie staram się uczyć na własnych błędach.


Świetny byłby tutaj przykład obsługi wszystkich możliwych ścieżek wykonania w istniejących przypadkach użycia
CodeYogi,

5

Różnica między solidnym a zaawansowanym inżynierią polega na tym, że z wdziękiem radzi sobie ze wszystkimi możliwymi przypadkami użycia, nawet dziwacznymi i skrajnymi przypadkami użycia, które NIE POWINNY się zdarzyć. Kiedy mówię z wdziękiem, mam na myśli, że użytkownik wprowadza dziwny przypadek wyjątku lub napotyka sytuację, która wymaga nieobsługiwanej lub nieokreślonej funkcji, która nie została zdefiniowana, a kod z wdziękiem kończy działanie bez awarii lub informuje użytkownika o nieobsługiwanej funkcjonalności.

Z drugiej strony nadinżynieria może wchodzić w zakres pełnej implementacji funkcji, które nie były potrzebne lub o które prosiło (niektóre funkcje, których klient NIE POTRZEBUJE, ale których nigdy nie wymagano!) LUB można je zdefiniować poprzez uzyskanie zbyt złożonego projektu lub zbyt złożonego kod do obsługi stosunkowo prostego problemu.


4

1) Uzyskaj wymagania.

2) Napisz minimalny kod, aby spełnić wymagania. Jeśli coś jest niejednoznaczne, zgadnij. Jeśli jest to bardzo niejednoznaczne, wróć do 1.

3) Wyślij do testowania.

4) Jeśli testerzy stwierdzą, że jest to dobre, udokumentuj zgodę. Jeśli coś jest nie tak, wróć do 1.

Skoncentruj się na zdaniu testów, a nie przewidywaniu testów. Jeśli nie masz testerów ... zdobądź testerów! Są one niezbędne nie tylko do weryfikacji poprawności kodu, ale także do całego procesu programowania.


1
+1 za Skoncentrowanie się na zdaniu testów, a nie na przewidywaniu testów, jednak wielu programistów takich jak ja oczekuje, że zrobią to oba, jednak z powodu braku silnych analityków biznesowych.
wałek klonowy

@maple_shaft - Very true. Chodzi o to, że problemy te powstają z powodu niekompetencji kogoś innego. Stresowanie się nad czyjąś pracą to droga do wypalenia zawodowego. Gdyby moja firma była na tyle głupia, by kazać mi spłacić należność za miesiąc, nie byłbym zbyt rozczarowany sobą, gdyby nie wyszło dobrze. Zdefiniowanie wymagań zazwyczaj wymaga jedynie opisania tego, co robisz na co dzień, aby można je było zautomatyzować. Jeśli żaden pracownik nie może tego zrobić, cóż ... firma może mieć kłopoty.
Morgan Herlocker,

3

Po pierwsze, należy maksymalnie znormalizować dane (nie nadmiarowe). Jeśli dane są w pełni znormalizowane, żadna pojedyncza aktualizacja danych nie może spowodować niespójności.

Nie zawsze możesz znormalizować dane, innymi słowy, możesz nie być w stanie wyeliminować nadmiarowości, w którym to przypadku mogą mieć niespójne stany. Następnie należy tolerować niespójność i okresowo ją naprawiać za pomocą jakiegoś programu, który przesuwa ją i łata.

Istnieje silna tendencja do ścisłego zarządzania redundancją za pomocą powiadomień. Są one nie tylko trudne do upewnienia się, że są poprawne, ale mogą prowadzić do ogromnej nieefektywności. (Część pokusy pisania powiadomień powstaje, ponieważ w OOP są one praktycznie zachęcane).

Ogólnie rzecz biorąc, wszystko, co zależy od sekwencji zdarzeń, komunikatów itp., Będzie podatne na atak i będzie wymagało mnóstwa kodowania obronnego. Zdarzenia i komunikaty są charakterystyczne dla danych z redundancją, ponieważ komunikują zmiany między częściami, próbując zapobiec niespójności.

Jak powiedziałem, jeśli musisz mieć nadmiarowość (a szanse są całkiem spore, musisz), najlepiej być w stanie a) tolerować ib) naprawiać. Jeśli spróbujesz zapobiec niespójności wyłącznie za pomocą wiadomości, powiadomień, wyzwalaczy itp., Bardzo trudno będzie uczynić ją niezawodną.


3
  • napisz do ponownego użycia.
  • pisać testy. trywialne, nietrywialne, niektóre absurdalnie złożone, aby zobaczyć, jak sobie radzi w takich warunkach. testy pomogą ci również określić formę interfejsu.
  • napisz program, który mocno zawiedzie (np. stwierdzenie). mój kod ma mnóstwo ponownego użycia i sprawdzam pod kątem mnóstwa przypadków - jest więcej sprawdzania / obsługi błędów niż rzeczywista implementacja (na podstawie liczby wierszy).
  • ponowne użycie.
  • natychmiast naprawiaj rzeczy, które pójdą źle
  • uczyć się i budować na podstawie doświadczenia.

błędy pojawią się po drodze, ale (na szczęście) zostaną zlokalizowane i (w większości przypadków) pojawią się na bardzo wczesnym etapie testów. drugą zaletą ponownego użycia jest to, że klient / osoba dzwoniąca może zapisać większość sprawdzania błędów / rusztowań przy użyciu tego, co wynika z implementacji.

twoje testy określą następnie możliwości twojego programu i ich wytrzymałość - dodawaj kolejne testy, aż będziesz zadowolony ze wskaźników sukcesu i wkładu; w razie potrzeby ulepszanie, rozszerzanie i wzmacnianie.


2

Rozróżniam to, pisząc kod z dobrze zdefiniowanym, ale niekoniecznie optymalnym zachowaniem dla bardzo mało prawdopodobnych przejść. Na przykład, kiedy jestem całkiem pewien (udowodniony, ale nie przetestowany), że macierz będzie dodatnio określona, ​​wstawiam twierdzenie lub wyjątek do programu, aby przetestować stan, ale nie piszę dla niego własnej ścieżki kodu. W ten sposób zachowanie jest zdefiniowane, ale nieoptymalne.


2

Solidność: stopień, w jakim system nadal działa w obecności nieprawidłowych danych wejściowych lub stresujących warunków środowiskowych. (Code Complete 2, s. 464)

Ważne pytanie dotyczy tego, jak ważna jest dla ciebie solidność. Jeśli jesteś na Facebooku, bardzo ważne jest, aby Twoja strona internetowa nadal działała, gdy ktoś wprowadza znaki specjalne, i aby twój serwer pozostał, gdy 100 milionów użytkowników jest zalogowanych jednocześnie. Jeśli piszesz skrypt do wykonania typowej operacji, którą wykonujesz tylko Ty, nie przejmujesz się tym zbytnio. Pomiędzy nimi jest wiele poziomów. Ocena, jakiej solidności potrzebujesz, jest jedną z ważnych umiejętności, których powinien nauczyć się programista.

Zasada YAGNI dotyczy dodawania funkcji, które mogą być potrzebne programowi. Ale ta zasada nie dotyczy solidności. Programiści zwykle przeceniają prawdopodobieństwo, że dane przyszłe rozszerzenie będzie potrzebne (zwłaszcza jeśli jest fajne), ale nie doceniają prawdopodobieństwa, że ​​coś pójdzie nie tak. Ponadto, jeśli okaże się, że później potrzebna jest pominięta funkcja, programista może napisać ją później. Jeśli okaże się, że mimo wszystko konieczne jest sprawdzenie pominiętego błędu, uszkodzenie może zostać wyrządzone.

Dlatego w rzeczywistości lepiej jest popełnić błąd po stronie, sprawdzając, czy występują nietypowe błędy. Ale jest równowaga. Niektóre rzeczy do rozważenia w tej równowadze:

  • Jak często może występować ten błąd?
  • Jaki jest koszt wystąpienia tego błędu?
  • Czy to jest do użytku wewnętrznego czy zewnętrznego?

Nie zapominaj, że ludzie mogą - i będą - próbować korzystać z twojego programu w nieoczekiwany sposób. Lepiej jest, gdy dzieje się coś przewidywalnego.

Jako ostatnią linię obrony użyj twierdzenia lub zamknięcia. Jeśli wydarzy się coś, czego nie możesz znaleźć, jak sobie z tym poradzić, zamknij program. Zwykle jest to lepsze niż pozwolenie programowi na kontynuowanie i zrobienie czegoś nieprzewidywalnego.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.