Bądź liberalny w tym, co akceptujesz ... czy nie?

45

[Uwaga: to pytanie jest subiektywne, ale wolałbym uzyskać odpowiedzi poparte faktami i / lub refleksjami]

Myślę, że wszyscy wiedzą o zasadzie solidności , zwykle podsumowanej przez prawo Postela:

Bądź konserwatywny w tym, co wysyłasz; bądźcie liberalni w tym, co akceptujecie.

Zgodziłbym się, że przy projektowaniu powszechnego protokołu komunikacyjnego może to mieć sens (w celu umożliwienia łatwego rozszerzenia), jednak zawsze uważałem, że jego zastosowanie do HTML / CSS było całkowitą porażką, każda przeglądarka wdrożyła własne ciche poprawki wykrywanie / zachowanie, co uniemożliwia uzyskanie spójnego renderowania w wielu przeglądarkach.

Zauważam jednak, że tam RFC protokołu TCP uznaje „cichą awarię” za dopuszczalną, chyba że określono inaczej ... co jest co najmniej ciekawym zachowaniem.

Istnieją inne przykłady zastosowania tej zasady w handlu oprogramowaniem, które regularnie pojawiają się, ponieważ ugryzły programistów, od samego początku:

  • Wstawianie średników w JavaScript
  • C (ciche) wbudowane konwersje (co nie byłoby takie złe, gdyby nie zostało obcięte ...)

i istnieją narzędzia, które pomogą wprowadzić „inteligentne” zachowanie:

Uważam jednak, że takie podejście, chociaż może być pomocne w kontaktach z użytkownikami nietechnicznymi lub pomagać użytkownikom w procesie odzyskiwania błędów, ma pewne wady, gdy stosuje się je do projektowania interfejsu biblioteki / klas:

  • jest nieco subiektywne, czy algorytm zgaduje „dobrze”, a zatem może być sprzeczne z zasadą najmniejszego zdziwienia
  • utrudnia to wdrożenie, a tym samym zwiększa szanse na wprowadzenie błędów (naruszenie YAGNI ?)
  • sprawia, że ​​zachowanie jest bardziej podatne na zmianę, ponieważ każda modyfikacja procedury „zgadywania” może uszkodzić stare programy, prawie wykluczając możliwości refaktoryzacji ... od samego początku!

I to doprowadziło mnie do następującego pytania:

Czy projektując interfejs (bibliotekę, klasę, komunikat) kierujesz się zasadą niezawodności, czy nie?

Ja sam bywam dość rygorystyczny, korzystam z szerokiej weryfikacji danych wejściowych na moich interfejsach i zastanawiałem się, czy nie byłem może zbyt rygorystyczny.

Matthieu M.
źródło
Zastanawiam się, jaka jest różnica między HTML a danymi ogólnymi? Zasada niezawodności dotyczy komunikacji. Jeden pisze - czyta. Dlaczego komunikacja sieciowa różni się od wizualnej lub API? Mam przykład interfejsu API, w którym zasada bycia liberalnym w zakresie tego, co akceptujemy, upraszcza życie użytkownikom, którzy są programistami , zmniejsza rozmiar kodu, a zatem poprawia wydajność + eliminuje błędy. Spójrz stackoverflow.com/questions/18576849
Val
@ Val: w rzeczywistości twój przykład nie pasuje. „bycie liberalnym w tym, co akceptujesz” nie jest tylko kwestią podstawową / pochodną, ​​ale wykracza poza to i akceptuje (i interpretuje) nieco błędne dane wejściowe.
Matthieu M.,
Jak pokazanie niektórych przypadków nie pokazuje tego przypadku?
Val

Odpowiedzi:

34

Powiedziałbym solidność, gdy nie wprowadza ona dwuznaczności .

Na przykład: Podczas analizowania listy rozdzielanej przecinkami, to czy przed lub po przecinku jest spacja, nie zmienia znaczenia semantycznego.

Podczas parsowania łańcucha id powinien przyjmować dowolną liczbę popularnych formatów (z myślnikami lub bez myślników, z otaczającymi nawiasami klamrowymi lub bez nich).

Większość języków programowania jest odporna na użycie białych znaków. Szczególnie wszędzie tam, gdzie nie wpływa to na znaczenie kodu. Nawet w Pythonie, gdzie spacja jest istotna, nadal jest elastyczna, gdy znajdujesz się w deklaracji listy lub słownika.

Zdecydowanie zgadzam się, że jeśli coś można zinterpretować na wiele sposobów lub jeśli nie jest w 100% jasne, co miało na myśli, to zbyt duża wytrzymałość może skończyć się bólem, ale jest dużo miejsca na solidność bez dwuznaczności.

Davy8
źródło
1
Zgadzam się, że solidność, gdy nie kosztuje dużo, jest warta zachodu.
Matthieu M.,
2
Nawet lista rozdzielana przecinkami powoduje problemy: silnik javascript chrome i firefox wydaje się akceptować {"key": "value",}jako prawidłowy, IE nie. Często napotykałem ten problem, dopóki nie poprawiłem procesu kompilacji za pomocą JSlint.
keppla
2
@keppla to problemy z implementacją, a nie z projektowaniem. Nie jestem pewien, czy jest to zgodne ze specyfikacją javascript, a IE jej nie przestrzega, czy też jest to „fajna funkcja” dodana przez FF i Chrome, ale w Pythonie określono, że jest poprawna i zaimplementowana jako taka. Jeśli zostanie określony jako prawidłowy i nie działa, oznacza to wadliwą implementację. Jeśli nie jest określony, to nie powinien tak naprawdę polegać na nim (chociaż ze względów praktycznych, jeśli nie działa w głównej przeglądarce, równie dobrze można go uznać za nieokreślony w specyfikacji, jeśli nie kontrolujesz użytkownika)
Davy8
7
@ Davy8: Przecinek końcowy wydaje się nielegalny ( stackoverflow.com/questions/5139205/… ). Nie chcę na tym polegać, chodzi mi o to, że gdy wystarczająca liczba osób zaakceptuje dane wejściowe, staje się de facto standardem (ponieważ nie zauważamy, że jest to błąd), co prowadzi do czegoś, co napotkaliśmy w HTML: że błędy stają się tak powszechne, że nie można ich już ignorować jako złych danych wejściowych.
keppla
1
@keppla, które powinny zawieść w Moz i Chrome. Jako twórca JS przede wszystkim wkurza mnie, że tak nie jest (przynajmniej w Mozu). To sprawia, że ​​debugowanie jest trudniejsze, a nie łatwiejsze. IE robi to, co powinno. Błąd @ zły kod. HTML to jedno. Nie potrzebujemy tego badziewia w języku skryptowym. Jest to przykład okropnego zastosowania zasady Robust, którą wyróżniają się producenci przeglądarek.
Erik Reppen
15

Absolutnie nie. Techniki takie jak programowanie obronne zaciemniają błędy, przez co ich wygląd jest mniej prawdopodobny i bardziej losowy, co utrudnia ich wykrywanie, co utrudnia ich izolację.

Zdecydowanie niedoceniany Pisanie Solidnego Kodu był ogromny, ponieważ wielokrotnie podkreślał potrzebę i techniki uczynienia błędów tak trudnymi do wprowadzenia lub ukrycia. Poprzez zastosowanie jego zasad, takich jak: „Wyeliminuj przypadkowe zachowanie. Wymuś powtarzalność błędów”. oraz „Zawsze szukaj i eliminuj wady swoich interfejsów”. programiści znacznie poprawią jakość swojego oprogramowania, eliminując niejasności i niekontrolowane skutki uboczne, które są odpowiedzialne za dużą liczbę błędów.

Huperniketes
źródło
9

Nadmierne zastosowanie niezawodności prowadzi do zgadywania, czego chciał użytkownik, co jest w porządku, dopóki się nie pomylisz. Wymaga to również całkowicie błędnej wiary, że Twoi klienci nie będą nadużywać Twojego zaufania i nie będą tworzyć przypadkowych bełkotów, które po prostu działają, ale których nie będziesz w stanie wspierać w wersji 2.

Nadmierne zastosowanie poprawności powoduje, że odmawiasz klientom prawa do popełniania drobnych błędów, co jest w porządku, dopóki nie narzekają, że ich produkty działają dobrze na produkcie konkurencji, i mówią ci, co możesz zrobić dzięki standardowi 5000 stron, który zawiera słowo „PROJEKT” nadal jest napisany kredką na okładce, a co najmniej 3 ekspertów twierdzi, że jest zasadniczo wadliwa, a 200 innych uczciwych ekspertów twierdzi, że nie do końca rozumie.

Moje osobiste rozwiązanie zawsze było zaniedbaniem. Wspierasz ich, ale mówisz im, że robią źle i (jeśli to możliwe) najłatwiejszą drogę do poprawności. W ten sposób, gdy wyłączysz tę funkcję błędu na 10 lat, będziesz miał przynajmniej ślad papierowy, aby stwierdzić, że „ostrzegaliśmy cię, że może się to zdarzyć”.

deworde
źródło
+1 za wycofanie się , to naprawdę ważna koncepcja i jestem zaskoczony, że do tej pory było to pomijane.
Matthieu M.,
9

Niestety, tak zwana „zasada solidności” nie prowadzi do solidności. Weź HTML jako przykład. Można by uniknąć wielu kłopotów, łez, marnowania czasu i energii, gdyby przeglądarki od samego początku ściśle analizowały HTML, zamiast próbować odgadnąć znaczenie źle sformułowanej zawartości.

Przeglądarka powinna po prostu wyświetlić komunikat o błędzie zamiast próbować naprawić go pod przykryciem. Zmusiłoby to wszystkich bandytów do naprawienia bałaganu.

ThomasX
źródło
Cytując siebie (musi się starzeć): „zawsze jednak uważałem, że jego zastosowanie do HTML / CSS było całkowitą porażką”
Matthieu M.
3
To prawda, ale ta sama odporność na awarie pomogła również zwiększyć popularność sieci.
MAR
Producenci przeglądarek zawiedli w tym przypadku. W przypadku typów dokumentów mieliśmy możliwość dokonania własnego wyboru w tym względzie, ale pod koniec dnia wszystko prawie zachowywało się tak samo, o ile zadeklarowałeś jakikolwiek typ dokumentu. Teraz uważają, że rozwiązaniem jest bardzo złożony zestaw rygorystycznych zasad dotyczących radzenia sobie z porażką? Myślę, że nie potrafią zidentyfikować problemu.
Erik Reppen
Mówisz, że szybkie działanie, które jest przeciwieństwem „solidnego”, jest bardziej wydajne.
Val
@MaR: Czy to prawda? Bardzo sporne, o wiele bardziej prawdopodobne, że cechy były ważne.
Deduplicator
6

Interfejsy dzielę na kilka grup (dodaj więcej, jeśli chcesz):

  1. te, które są pod twoją kontrolą, powinny być surowe (zazwyczaj klasy)
  2. biblioteki API, które również powinny być ściśle określone, ale zaleca się dodatkową walidację
  3. publiczne interfejsy, które muszą radzić sobie z każdym rodzącym się nadużyciem (zazwyczaj protokoły, dane wejściowe użytkownika itp.). Tutaj solidność na wejściu naprawdę się opłaca, nie można oczekiwać, że wszyscy naprawią swoje rzeczy. I pamiętaj dla użytkownika, że ​​to Twoja wina, jeśli aplikacja nie będzie działać, a nie strona, która wysłała źle sformatowane badziewie.

Wynik zawsze musi być ścisły.

Zniszczyć
źródło
5

Myślę, że HTML i World Wide Web dostarczyły na szeroką skalę rzeczywistego testu zasady solidności i pokazały, że jest to ogromna awaria. Jest bezpośrednio odpowiedzialny za mylący bałagan konkurujących ze sobą prawie standardów HTML, który czyni życie nieszczęśliwym dla twórców stron internetowych (i ich użytkowników) i pogarsza się z każdą nową wersją Internet Explorera.

Od lat 50. wiemy, jak poprawnie sprawdzać poprawność kodu. Uruchom go przez ścisły parser, a jeśli coś nie jest poprawne pod względem składniowym, wyrzuć błąd i przerwij. Nie przechodź, nie zbieraj 200 $, a na miłość binarną nie pozwól, aby jakiś program komputerowy próbował odczytać myśli programisty, jeśli popełni błąd.

HTML i JavaScript pokazały nam dokładnie, co się dzieje, gdy zasady te są ignorowane. Najlepiej jest uczyć się na błędach i nie powtarzać ich.

Mason Wheeler
źródło
4
@ChaosPandion: myślę, że problem nie dotyczy samego Internet Explorera, ale wszystkich niestandardowych stron internetowych, które zostały zaakceptowane przez poprzednie wersje i z którymi każdy musi teraz żyć ... i starać się bardziej lub mniej pomyślnie.
Matthieu M.,
5
Myślę, że zespół IE znajduje się w najgorszej możliwej pozycji ze wszystkich programistów przeglądarki. Joel całkiem ładnie podsumowuje moje myśli .
Dean Harding,
3
Być może utrudniłoby to życie programistom i projektantom, ale wtedy utknęlibyśmy w bardzo powoli ewoluującym i statycznym standardzie - wątpię, żeby sieć wyglądała tak, jak teraz. Prawdziwymi zwycięzcami byli ludzie przeglądający internet, a na koniec ludzie, którzy się liczą.
FinnNk
4
Ponadto, chociaż zasada Solidności może utrudnić życie programistom, trudno nazwać Internet (obecnie integralną częścią prawie każdej większej instytucji na świecie) ogromną AWARIĄ .
deworde
3
„Od lat 50. wiemy, jak poprawnie sprawdzać poprawność kodu. Przeprowadź go przez ścisły analizator składni, a jeśli coś nie jest poprawne pod względem składniowym, wyrzuć błąd i przerwij”. Aby zastosować to do scenariusza z prawdziwego świata: Jeśli zepsułem jedną ikonę z prawej strony mojej strony tuż pod cięciem, rezygnacja z całej strony jest naprawdę dobrym sposobem na wysłanie kogoś, kto szuka gdzie indziej, z powodu problem, którego nawet nie zauważyliby. Tak, możesz argumentować, że nie powinienem był popełnić błędu. Ale to raczej zakłada, że ​​nie poszedłem już do twojego mocniejszego konkurenta i nie odbieram twoich połączeń.
deworde
3

Jako kontrapunkt dla przykładu Masona, moje doświadczenie z protokołem inicjującym sesję było takie, że podczas gdy różne stosy interpretowałyby odpowiednie RFC inaczej (i podejrzewam, że dzieje się tak z każdym standardem, jaki kiedykolwiek napisano), bycie (umiarkowanie) liberalnym w tym, co akceptujesz, oznacza, że ​​ty może faktycznie nawiązywać połączenia między dwoma urządzeniami. Ponieważ te urządzenia są zwykle rzeczami fizycznymi, w przeciwieństwie do oprogramowania na pulpicie, po prostu musisz być liberalny w tym, co akceptujesz, w przeciwnym razie telefon nie będzie mógł zadzwonić na inny telefon określonej marki. To nie sprawia, że ​​Twój telefon wygląda dobrze!

Ale jeśli piszesz bibliotekę, prawdopodobnie nie masz problemu z interpretowaniem przez wiele stron wspólnego standardu na wzajemnie niezgodne sposoby. W takim przypadku powiedziałbym, że zachowuj się surowo, ponieważ akceptuje to dwuznaczności.

Plik żargonu zawiera również horror o „odgadywaniu” intencji użytkownika.

Frank Shearar
źródło
Bardzo zabawna historia :) Zdaję sobie sprawę, że możesz potrzebować więcej swobody podczas próby interakcji z istniejącymi systemami, ponieważ jeśli to nie zadziała , będziesz obwiniony.
Matthieu M.,
W rzeczywistości, jeśli Twój telefon nie działa z większością innych telefonów, jest zły .
SamB
1
@SamB: Wymień złe z uszkodzony .
deworde
3

Masz rację, reguła dotyczy protokołów, a nie programowania. Jeśli popełnisz literówkę podczas programowania, po kompilacji pojawi się błąd (lub uruchom, jeśli jesteś jednym z tych typów dynamicznych). Nie można nic zyskać, pozwalając komputerowi zgadywać. W przeciwieństwie do zwykłych ludzi, jesteśmy inżynierami i jesteśmy w stanie powiedzieć dokładnie, co mam na myśli. ;)

Tak więc, projektując API, powiedziałbym, że nie postępuj zgodnie z zasadą niezawodności. Jeśli programista popełni błąd, powinien natychmiast się o tym dowiedzieć. Oczywiście, jeśli Twój interfejs API wykorzystuje dane ze źródła zewnętrznego, takiego jak plik, powinieneś być łagodny. Użytkownik twojej biblioteki powinien dowiedzieć się o swoich błędach, ale nie o nikim innym.

Nawiasem mówiąc, zgaduję, że „cicha awaria” jest dozwolona w protokole TCP, ponieważ inaczej, gdyby ludzie rzucali w ciebie źle sformułowane pakiety, byłbyś bombardowany komunikatami o błędach. To prosta ochrona DoS.

Uwaga do samodzielnego wymyślenia imienia
źródło
1
„Jeśli popełnisz literówkę podczas programowania, zaraz po kompilacji pojawi się błąd” Przedstawiam ci miliony kompilatorów OSTRZEŻENIA, że standardowy kompilator wypluje, wciąż wykonując doskonale wykonalne zadania.
deworde
1

IMO, solidność jest jedną ze stron kompromisu projektowego, a nie zasadą „preferuj”. Jak wielu zauważyło, nic nie śmierdzi niczym dmuchanie na cztery godziny, próbując dowiedzieć się, gdzie twój JS popełnił błąd, tylko po to, aby odkryć prawdziwy problem, ponieważ tylko jedna przeglądarka działała poprawnie z XHTML Strict. Dzięki temu strona rozpadła się na kawałki, gdy pewna część obsługiwanego HTML była kompletną katastrofą.

Z drugiej strony, kto chce poszukać dokumentacji dla metody, która bierze 20 argumentów i nalega, aby były one w tej samej kolejności z pustymi lub zerowymi symbolami zastępczymi dla tych, które chcesz pominąć? Równie okropnym, solidnym sposobem radzenia sobie z tą metodą byłoby sprawdzenie każdego argumentu i próba zgadnięcia, który z nich był oparty na względnych pozycjach i typach, a następnie zawiodła po cichu lub próba „zrekompensowania” bezsensownych argumentów.

Możesz też uelastycznić proces, przekazując listę literału obiektu / słownika / pary klucz-wartość i obsłużyć istnienie każdego argumentu, gdy do niego dojdziesz. Dla bardzo drobnego kompromisu jest to ciasto i zjadaj to też scenariusz.

Przeciążenie argumentów w inteligentny i spójny z interfejsem sposób to sprytny sposób na solidne podejście do rzeczy. Podobnie pieczenie nadmiarowości w systemie, w którym zakłada się, że dostarczanie pakietów nie będzie regularnie dostarczane w ogromnie skomplikowanej sieci będącej własnością i obsługiwanej przez wszystkich w rozwijającej się dziedzinie technologii z szeroką gamą potencjalnych środków transmisji.

Tolerowanie nadużycia, szczególnie w systemie, który kontrolujesz, nigdy nie jest dobrym kompromisem. Na przykład musiałem odetchnąć, aby uniknąć rzucenia syczącego napadu w innym pytaniu dotyczącym umieszczenia JS na górze lub na dole strony. Kilka osób nalegało, aby lepiej umieścić JS na górze, ponieważ wtedy, jeśli strona nie załaduje się całkowicie, nadal potencjalnie będziesz mieć pewną funkcjonalność. Połowy strony pracy są gorsze niż pełne popiersia. W najlepszym wypadku powodują, że więcej odwiedzających witrynę słusznie zakłada, że ​​jesteś niekompetentny, zanim się o tym dowiesz, niż jeśli strona, której dotyczy błąd, jest po prostu odsyłana do strony z błędem po niepowodzeniu własnej weryfikacji, a następnie automatycznej wiadomości e-mail na adres ktoś, kto może coś z tym zrobić.

Próba dostarczenia funkcji 2010 w przeglądarce 1999, kiedy można po prostu dostarczyć stronę o niższej technologii, jest kolejnym przykładem kompromitacji w projektowaniu. Wykorzystane możliwości i pieniądze, które widziałem zmarnowane na czas programisty spędzony na obejściach związanych z błędami, aby na przykład zaokrąglić rogi elementu unoszącego się na przykład nad gradientowym tłem! @ # $ Ing, całkowicie mnie zaskoczyły. I po co? Dostarczanie stron o wyższej technologii, które działają słabo do sprawdzonych technofobów, ograniczając jednocześnie możliwości wyboru w wyższych przeglądarkach.

Aby był to właściwy wybór, solidna obsługa danych wejściowych powinna zawsze ułatwiać życie po obu stronach problemu, zarówno w krótkim, jak i długim okresie IMO.

Erik Reppen
źródło
4
„dla metody, która pobiera 20 argumentów”> nie trzeba szukać dalej, po 5/6 metoda jest zła . Dziękuję za odpowiedź :)
Matthieu M.
1

Nigdy nie zawiedź cicho . Poza tym próba odgadnięcia, czego chciał użytkownik interfejsu API / biblioteki, nie wydaje się złym pomysłem. Nie poszedłbym za tym; mając ścisłe wymagania, może ujawniać błędy w kodzie wywołującym i / lub błędne interpretacje dotyczące twojego API / biblioteki.

Ponadto, jak już wspomniano, zależy to od tego, jak trudno odgadnąć, czego oczekuje użytkownik. Jeśli jest to bardzo łatwe, masz dwa przypadki:

  1. Twoja biblioteka powinna być zaprojektowana nieco inaczej (zmień nazwę funkcji lub podziel ją na dwie części), aby użytkownik mógł oczekiwać tego, co faktycznie udostępniasz.
  2. Jeśli uważasz, że biblioteka jest poprawnie zaprojektowana, co oznacza jasne / proste nazewnictwo, możesz spróbować wywnioskować, co zamierzał użytkownik.

W każdym razie, że nie jest to w 100% oczywiste i deterministyczne, że jedno wejście należy przekonwertować na inne, nie należy wykonywać konwersji z wielu już wspomnianych powodów (łamanie zgodności przy refaktoryzacji, najmniejsze zdziwienie użytkowników).

W kontaktach z użytkownikiem końcowym próba naprawienia jego danych wejściowych / zgadywania jest bardzo mile widziana. Jest on oczekiwać , aby wprowadzić nieprawidłowe informacje; ten przypadek jest całkowicie wyjątkowy. Jednak inny programista nie jest prostym nietechnicznym użytkownikiem. Ma specjalistyczną wiedzę, aby zrozumieć błąd, a błąd może mieć znaczenie / być dla niego korzystny. Dlatego zgadzam się z tobą w sprawie projektowania ścisłych interfejsów API, podczas gdy - oczywiście - ścisłości towarzyszy jasność i prostota.

Poleciłbym przeczytać to moje pytanie dotyczące podobnej sprawy.

K. Gkinis
źródło