Jak powinniśmy być defensywni?


11

Mamy już działa Pex nad jakimś kodem, i on został pokazano kilka dobrych rzeczy (również złe rzeczy, ale pokazując im, zanim dotrze do produkcji!).

Jednak jedną z fajnych rzeczy w Pex jest to, że niekoniecznie przestaje próbować znaleźć problemy.

Jednym z obszarów, które znaleźliśmy, jest to, że podczas przekazywania ciągu nie sprawdzaliśmy pustych ciągów.

Więc zmieniliśmy:

if (inputString == null)

do

if (string.IsNullOrEmpty(inputString)) // ***

To naprawiło początkowe problemy. Ale kiedy ponownie uruchomiliśmy Pex, zdecydowaliśmy, że:

inputString = "\0";

powodował problemy. I wtedy

inputString = "\u0001";

Zdecydowaliśmy, że możemy użyć wartości domyślnych, jeśli napotkamy, // ***i że cieszymy się z wyjątku spowodowanego przez inne nieparzyste dane wejściowe (i radzimy sobie z tym).

Czy to wystarczy?


Czy sprawdziłeś, czy możesz wyłączyć niektóre ostrzeżenia? Wiem, że JTest też by to zrobił, ale była też opcja wyłączenia niektórych z jego rekomendacji. Dopracowanie ustawień zajęło trochę czasu, aby uzyskać profil testowy kodu, który nam się podobał.
FrustratedWithFormsDesigner

@ Frustrated: Tak, z pewnością istnieją poprawki w profilu, które możemy zrobić i robimy. Nawiasem mówiąc, Pex jest świetnym narzędziem. Wyniki, które pokazał, właśnie skłoniły do ​​pytania.
Peter K.

Nie mam dla ciebie odpowiedzi, ale chciałem podziękować za ten link do tego Pex; wygląda całkiem fajnie, jeśli tak właśnie myślę i automatycznie (?) utworzy testy jednostkowe dla istniejącego kodu. Kod, nad którym pracuję, jest ogromny i nie ma testów oraz bardzo ściśle powiązany kod, więc nie można niczego refaktoryzować.
Wayne Molina

@WayneM: Tak, jest całkiem niezły, chociaż musiałem przejść przez kilka przykładów, aby zrozumieć, co robi. W przypadku starszego kodu jest on świetny. Jeszcze lepiej dla nowego kodu!
Peter K.,

Odpowiedzi:


9

Trzy pytania powinny pomóc ci określić, jak defensywne jest twoje kodowanie.

Po pierwsze - jakie są konsekwencje przedostania się złych danych wejściowych? Jeśli jest to komunikat o błędzie na jednym z komputerów twojego programisty, być może nie jest tak krytyczny, aby być defensywnym. Czy może to spowodować zakłócenia finansowe u klientów, zakłócenia informacji księgowych IE? Czy to system czasu rzeczywistego, w którym zagrożone jest życie? W scenariuszu życia / śmierci prawdopodobnie powinien istnieć więcej kodów sprawdzania poprawności i obsługi błędów niż rzeczywisty kod funkcji.

Po drugie, ile osób będzie ponownie korzystać z tej funkcji lub fragmentu kodu? Tylko ty? Twój dział? Twoja firma? Twoi klienci? Im szersze zastosowanie kodu, tym bardziej defensywny.

Po trzecie - jakie jest źródło danych wejściowych, które zatwierdzam? Gdyby pochodziły od użytkownika lub z publicznej strony internetowej, byłbym bardzo defensywny. Jeśli dane wejściowe zawsze pochodzą z twojego kodu, bądź nieco defensywny, ale nie marnuj czasu na sprawdzanie.

Zawsze będzie można dodać więcej sprawdzania błędów i sprawdzania poprawności w systemie. Chodzi o to, czy koszt napisania i utrzymania tego kodu przewyższy koszt problemów spowodowanych błędami w kodzie.


6

Użytkownicy są źli, a wszystko, co wprowadzą, powinno być sprawdzone z najwyższą dyscypliną.

Cokolwiek wygenerowanego bez korzyści wprowadzanych przez użytkownika lub z wcześniej oczyszczonych danych nie powinno być sprawdzane na tym samym poziomie. Problem polega na tym, że zapominasz i używasz tych metod na złych danych, ponieważ zapomniałeś, że kod nie został zahartowany.

Jedyną rzeczą, którą zawsze powinieneś sprawdzić, jest wszystko, co może spowodować przepełnienie lub awarię. Nie ma znaczenia, jak głęboko ta metoda jest zakopana i jak jesteś pewien, że ten warunek nigdy nie wystąpi. W każdym razie musisz to zaprogramować, aby uspokoić Murphy'ego.


2

Byłbym tak defensywny, jak trzeba. Trochę dwuznacznie, tak sądzę, ale postaram się wyjaśnić.

Kiedy poprawisz metodę, jeśli metoda ta ma parametry wejściowe, musisz podjąć decyzję, czego oczekujesz. W sytuacjach i miejscach w aplikacji będzie się to różnić. Na przykład, jeśli metoda lub fragment kodu akceptuje dane z danych wprowadzonych przez użytkownika, to chciałbyś objąć całą bazę kodu i odpowiednio obsłużyć wszelkie dane wejściowe, czy to za pomocą komunikatu o błędzie, czy też jakiegoś przyjemnego sposobu wyświetlania niedopuszczalnych danych.

Jeśli metoda jest jawnym interfejsem API, to samo. Nie możesz kontrolować tego, co nadchodzi, więc powinieneś spodziewać się, że spróbujesz objąć wszystkie aspekty i program odpowiednio.

W przypadku metod, które są wytwarzane w silniku rdzenia twojego projektu, musisz podjąć decyzję. Czy zakładam, że przybywające dane zostały wstępnie sprawdzone i sprawdzone przed ich dostarczeniem, czy powinienem przeprowadzić niezbędne kontrole. Myślę, że zależy to od poziomu koncepcyjnego metody i tego, czy jest to dopuszczalne miejsce do sprawdzenia. Mogę więc rozważyć:

1) Czy to jedyne miejsce, w którym muszę to sprawdzić? Czy ta zmienna będzie musiała zostać sprawdzona w wielu różnych miejscach dla tego warunku? Jeśli tak, mogę wykonać kontrolę raz wyżej, a następnie przyjąć ważność później

2) Czy inne komponenty systemu powinny działać z metodami i interfejsami, które piszę? Jeśli tak, czy można kontrolować za pomocą instrukcji potwierdzenia debugowania, wyjątków debugowania, komentowania metod i ogólnej architektury systemu wymaganego wyniku, czy dane będą wymagały sprawdzenia.

3) Jakie są skutki awarii w tym punkcie kodu. Czy spowoduje to upadek całej sprawy? Czy jakikolwiek błąd zostanie złapany gdzie indziej i ten błąd przynajmniej zostanie złapany.

4) Czy warto tutaj wystawiać czek? Czasami sprawdzenie fragmentu możliwych uszkodzonych danych, chociaż pomaga w rozwiązaniu problemu w tym momencie, a nie pomyłce, może pomóc to ukryć. W tym momencie możesz spędzać godziny, goniąc za innym problemem, tylko po to, aby znaleźć faktyczny problem z powodu prawidłowej kontroli danych, które z powrotem spadły z łańcucha zdarzeń, które spadły kaskadowo do tego, który zgłosił użytkownik / programista.

Ogólnie jestem programistą obronnym, ale wierzę również, że dzięki dokładnemu TDD i odpowiednim testom jednostkowym możesz wprowadzić kontrole w kodzie na wymaganych poziomach i mieć pewność, że działa tak, jak powinien, gdy dojdzie do sekcji niższych poziomów .


1

Spędziłem tygodnie wcześniej na błędach, które można było wykryć dzięki 5 minutom dodatkowej pracy takiej jak ta z góry. Moim zdaniem ta praca z góry zawsze jest tego warta. W końcu naprawisz błąd. Jedyne pytanie brzmi: ile to zajmie.

Jedną z rzeczy, które tego rodzaju narzędzia analityczne często odkrywają, są rzeczy, które niekoniecznie są błędami, ale złymi nawykami programistycznymi, które zwiększają prawdopodobieństwo wystąpienia błędów. Jednym z takich powszechnych nawyków jest zmienna inicjalizacja. Czasami pusty ciąg jest idealnie poprawną wartością dla zmiennej. W takim przypadku chcesz skonfigurować swoje narzędzie, aby nie uwzględniało błędu w tym konkretnym przypadku. Jednak często pusty ciąg nie jest prawidłową wartością zmiennej, ale ludzie i tak ją ustawiają, ponieważ kompilator narzeka, jeśli czegoś tam nie ma, lub twój język automatycznie inicjuje się na pusty ciąg, niezależnie od tego, czy jest prawidłowy, czy nie.

To frustruje ludzi, ponieważ wydaje się, że jest to haczyk 22 bez poprawnego rozwiązania, ale rozwiązaniem jest refaktoryzacja kodu, aby nie musiał inicjować zmiennej, dopóki nie będzie tam prawidłowej wartości. To wymaga dodatkowej przemyślenia, ale czyni kod o wiele bardziej niezawodnym i jest w rzeczywistości łatwiejsze do napisania i utrzymania na dłuższą metę.

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.