Prawo Nieszczelnych Abstrakcji

Oryginalny post: The Law of Leaky Abstractions

Autor: Joel Spolsky

Jest jedna magiczna i zupełnie kluczowa rzecz w działaniu Internetu na której codziennie polegasz. Dzieje się to w protokole TCP, jednym z podstawowych elementów składających się na Internet.

TCP to metoda przesyłania danych, która jest niezawodna. Rozumiem przez to, że kiedy wysyłasz wiadomość przez sieć z wykorzystaniem TCP dotrze ona na miejsce w formie niezmienionej i nieuszkodzonej.

Używamy TCP do wielu rzeczy, takich jak przeglądanie stron internetowych lub wysyłanie emaili. To niezawodność TCP zapewnia nam, że wszystkie ekscytujące emaile ze Wschodniej Afryki docierają w idealnym stanie. O tak.

Dla porównania, jest inna metoda transmitowania danych, która nazywa się IP i jest zawodna. Nikt nie obieca, że Twoje dane dotrą do celu, a mogą gdzieś po drodze ulec zabałaganieniu. Jeśli wysyłasz wiele komunikatów z wykorzystaniem IP, nie bądź zaskoczony, jeśli tylko połowa z nich dotrze, niektóre z nich w innej kolejności niż były wysyłane, a niektóre zamienione na inne komunikaty. Być może te nowe będą zawierać zdjęcia uroczych małych orangutanów lub – co bardziej prawdopodobne – dużo nienadających się do przeczytania śmieci, które wyglądają jak temat emaila z tajwańskim spamem.

Teraz magia: TCP jest skonstruowane na bazie IP. Innymi słowy, TCP musi wysyłać dane w sposób niezawodny, używając zawodnego narzędzia.

Żeby zrozumieć dlaczego to jest magia, rozważmy następujący, dość analogiczny, choć trochę szalony scenariusz osadzony w świecie rzeczywistym.

Wyobraźmy sobie, że mamy sposób na wysyłanie aktorów z Broadway’u do Hollywoodu, który polega na usadzeniu ich w samochodach i przewozie przez kraj. Niektóre z tych samochodów się rozbiły, zabijając nieszczęsnych aktorów. Czasami aktorzy upili się po drodze i zgolili głowy lub sprawili sobie tatuaże na nosach i stali się zbyt brzydcy, aby pracować w Hollywood. Często aktorzy przybywali w innej kolejności niż byli wysyłani, ponieważ wybierali inne trasy. Wyobraźmy sobie teraz nową usługę: Hollywood Express, która polega na dostarczaniu aktorów do Hollywood, zapewniając jednocześnie, że (a) dotrą (b) w kolejności (c) w nienaruszonej formie. Magia polega na tym, że Hollywood Express nie ma żadnej innej metody dostarczania aktorów niż zawodna metoda pakowania ich do samochodów i przewiezienia przez kraj. Hollywood Express sprawdza, czy wszyscy aktorzy dotarli w idealnym stanie i, jeśli nie, powiadamiają biuro odpowiedzialne za wysyłkę, aby przesłali bliźniaka. Jeśli aktorzy przybywają w złej kolejności Hollywood Express przestawia ich. Jeśli wielkie UFO podczas swojej podróży do Strefy 51 rozbije się gdzieś na autostradzie w Newadzie, czyniąc ją tym samym nieprzejezdną, wszyscy aktorzy, którzy mieli jechać tą drogą są kierowani przez Arizonę, a Hollywood Express nawet nie informuje reżyserów w Kalifornii co się stało. Bo dla nich aktorzy po prostu przyjeżdżają trochę wolniej niż zwykle i nie muszą nic słyszeć o rozbiciu UFO.

To jest, w przybliżeniu, magia TCP. Informatycy lubią nazywać to abstrakcją: uproszczenie czegoś znacznie bardziej skomplikowanego, które znajduje się pod spodem. Jak się okazuje, wiele aspektów programowania sprowadza się do tworzenia abstrakcji. Czym jest biblioteka do łańcuchów znaków? Jest metodą na sprawienie wrażenia, że komputer potrafi operować na łańcuchach znaków równie prosto jak na liczbach. Czym jest system plików? Jest metodą na sprawianie wrażenia, że dysk twardy nie jest tylko zbiorem wirujących magnetycznych talerzy, które potrafią przechowywać bity w wybranych lokalizacjach, ale raczej hierarchicznym systemem folderów-w-folderach, zawierających pojedyncze pliki, które ostatecznie zawierają ciągi bajtów.

Wróćmy do TCP. Na początku – ze względu na prostotę – powiedziałem małe kłamstewko i niektórym z Was zapewne wylatuje już para z uszu, bo tak to Was zdenerwowało. Powiedziałem, że TCP gwarantuje, że Twoja wiadomość dotrze. Tak naprawdę, nie gwarantuje. Jeśli Twój domowy wąż przeżuł kabel sieciowy prowadzący do Twojego komputera i żadne pakiety IP nie mogą przepłynąć , wtedy TCP nie może na to nic poradzić i Twoja wiadomość nie dotrze. Jeśli podpadłeś administratorom systemu w swojej firmie i ukarali Cię przyłączając do przeładowanego huba, tylko niektóre z Twoich pakietów przejdą. TCP będzie działać, ale wszystko stanie się naprawdę powolne.

To jest to, co nazywam nieszczelną abstrakcją. TCP próbuje zapewnić kompletną abstrakcję nad zawodną siecią, ale czasami ta sieć wymyka się abstrakcji spod kontroli i zaczynasz odczuwać rzeczy od których nie można się uchronić. To jest przykład na to, co nazwałem Prawem Nieszczelnych Abstrakcji:

Wszystkie nietrywialne abstrakcje, do pewnego stopnia, są nieszczelne.

Abstrakcje zawodzą. Czasami mniej, czasami bardziej. Jest wyciek. Rzeczy idą źle. Tak dzieje się w przypadkach, kiedy mamy do czynienia z abstrakcjami. Oto kilka przykładów.

  • Coś tak prostego jak iterowanie po dwuwymiarowej tablicy może mieć zupełnie różną wydajność, jeśli robisz to horyzontalnie zamiast wertykalnie. W zależności od „struktury drewna” – jeden kierunek może powodować znacznie więcej błędów stronicowania niż inny, a przeładowanie stron jest wolne. Nawet programiści assemblera powinni móc zakładać, że mają dużą i płaską przestrzeń adresową, ale pamięć wirtualna sprawia, że jest to abstrakcja. Zaczyna ona przeciekać, kiedy pojawiają się błędy stronicowania i niektóre odwołania do pamięci trwają znacznie więcej nanosekund niż inne.
  • Język SQL ma na celu poddać abstrakcji kroki procedur potrzebnych do zadawania zapytań bazie danych. Pozwala Ci określić to, co chcesz otrzymać i pozwolić bazie zdefiniować kroki procedury do tego potrzebne. Ale w niektórych przypadkach zapytania SQL są tysiące razy wolniejsze niż inne, równoważne pod względem logicznym. Słynny przykład pokazuje, że niektóre serwery SQL są znacznie szybsze jeśli napiszesz „where a=b and b=c and a=c” zamiast  „where a=b and b=c”, pomimo tego, że wyniki są takie same. Nie zajmujesz się procedurą, określasz tylko specyfikację. Ale czasami abstrakcja przecieka i powoduje potwornie duże pogorszenie wydajności. Musisz wtedy uciec się do analizy zapytań, przestudiować co dzieje się źle i określić, jak sprawić, aby zapytania działały szybciej.
  • Pomimo tego, że sieciowe biblioteki takie jak NFS i SMB pozwalają Ci traktować pliki na odległych maszynach „jak gdyby” były lokalne, czasami połączenie jest bardzo powolne albo urywa się. Plik przestaje wyglądać na lokalny i jako programista musisz napisać jakiś kod, aby sobie z tym poradzić. Abstrakcja „odległy plik jest taki sam jak lokalny” przecieka. Oto konkretny przykład dla administratorów Unixa. Jeśli umieścisz katalogi domowe użytkowników na zamontowanym dysku NFS (pierwsza abstrakcja) i Twoi użytkownicy stworzą pliki .forward, aby przekazywać wszystkie swoje emaile gdzieś indziej (druga abstrakcja) wtedy, kiedy przyjdzie nowy email a serwer NFS przestanie działać, email nie zostanie przeforwardowany, ponieważ nie uda się odnaleźć pliku .foreward. Wyciek w abstrakcji spowodował, że kilka wiadomości przepadło.
  • Klasy łańcuchów znaków w C++ mają na celu sugerować, że łańcuchy znaków są wbudowanymi typami danych. Próbują poddać abstrakcji fakt, że łańcuchy są trudne i pozwolić działać Ci jakby gdyby były równie łatwe jak liczby całkowite. Prawie wszystkie klasy stringów w C++przeciążają operator +, więc możesz pisać s + „bar” żeby skonkatenować. Ale wiesz co? Nie ważne jak bardzo by próbowali, nigdzie na Ziemi nie znajdą klasy w C++, która pozwoliłaby napisać „foo” + „bar”, ponieważ literały łańcuchów znaków w C++ zawsze są typu char*, nigdy string. Abstrakcja ma wyciek, którego język nie pozwoli zatkać. (Co zaskakujące, historia ewolucji C++ może być sprowadzona do historii prób zatkania wycieku w abstrakcji łańcuchów znaków. Dlaczego nie można po prostu dodać natywnej klasy string do języka? – to wykracza poza moje pojmowanie w tym momencie.)
  • I wreszcie, nie możesz jechać zbyt szybko, kiedy leje. Mimo, że Twój samochód ma zamontowane wycieraczki, reflektory i dach i grzejnik, wszystko abyś nie musiał przejmować się faktem, że pada (wszystkie te zamontowane rzeczy sprawiają, że pogoda poddaje się abstrakcji). Ale czekaj, musisz pamiętać o utracie przyczepności i fakcie, że deszcz jest czasami na tyle intensywny, że widoczność jest bardzo ograniczona. Jedziesz więc wolniej, ponieważ pogoda nigdy nie może być całkowicie poddana abstrakcji. Ze względu na prawo nieszczelnych abstrakcji.

Jednym z powodów dla których prawo nieszczelnych abstrakcji jest tak problematyczne jest fakt, że abstrakcje tak naprawdę nie ułatwiają naszego życia w stopniu w jakim byśmy chcieli. Kiedy uczę kogoś jak programować w C++ byłoby wspaniale gdybym nigdy nie musiał mówić mu o char* i arytmetyce wskaźników. Byłoby wspaniale gdybym mógł od razu przejść do stringów w STL. Ale pewnego dnia ten ktoś napisze kod „foo” + „bar” i wtedy staną się naprawdę dziwne rzeczy, a ja będę musiał zatrzymać się i – chcąc nie chcąc - nauczyć go czym jest char*. Albo, pewnego dnia będzie chciał wywołać funkcję Windows API, której dokumentacja mówi, że ma ona argument typu OUT LPTSTR i nie będzie wiadomo jak ją wywołać dopóki nie pozna się char*, wskaźników, Unicode’a, wchar_t i pliku nagłówkowego TCHAR oraz całej masy innych przeciekających rzeczy.

Podczas nauczania kogoś jak programować pod COM, byłoby wspaniale gdybym mógł po prostu nauczyć go korzystania z wizardów Visual Studio i wszystkich opcji generacji kodu, ale kiedy coś pójdzie inaczej niż powinno, kompletnie nie będzie wiadomo co się stało, jak to debugować i jak przywrócić działającą postać. Muszę nauczyć go co to jest IUnknown i CLSID i ProgIDS i... proszę!

Podczas nauczania kogoś jak programować w ASP.NET, byłoby wspaniale gdybym mógł po prostu nauczyć go, że może kliknąć dwa razy na obiekty i pisać kod, który wykona się na serwerze, kiedy użytkownik kliknie na te obiekty. Istotnie, ASP.NET wprowadza abstrakcję zacierającą różnicę między pisaniem kodu HTML do obsługi hiperlinku (<a>) i kodu do obsługi kliknięcia na przycisk. Ale pojawia się problem: projektanci ASP.NET musieli ukryć fakt, że w HTML nie ma możliwości wysłania formularza hiperlinkiem. Obchodzą to generując linie kodu JavaScript i dołączając obsługę zdarzenia onclick do hiperlinku. Abstrakcja przecieka. Jeśli użytkownik ma wyłączony JavaScript, wtedy aplikacja w ASP.NET nie będzie działać prawidłowo i jeśli programista nie rozumie, jak działa ta abstrakcja w ASP.NET, nie będzie miał zielonego pojęcia, co mogło pójść źle.

Prawo przeciekającej abstrakcji oznacza, że kiedykolwiek ktoś proponuje jakieś nowe super narzędzie do generowania kodu, które uczyni nas tak bardzo efektywnymi, pojawiają się ludzie, którzy mówią „najpierw naucz się jak to zrobić manualnie, dopiero później używaj tego super narzędzia oszczędzającego czas”. Narzędzia do generowania kodu, które mają na coś nakładać abstrakcję , podobnie jak wszystkie abstrakcje, przeciekają. Jedynym sposobem na kompetentne radzenie sobie z tymi przeciekami jest zrozumienie jak abstrakcje działają i - względem czego. Więc abstrakcje oszczędzają czas na pracę, ale nie oszczędzają czasu na naukę.

A to wszystko oznacza, paradoksalnie – pomimo, że mamy narzędzia programistyczne coraz to wyższego poziomu z coraz lepszymi abstrakcjami – że stanie się wprawnym programistą staje się jeszcze trudniejsze.

Podczas moich praktyk w Microsoft pisałem bibliotekę do stringów na Macintoshe. Typowe zadanie: napisz strcat, które zwraca wskaźnik do ostatniego znaku nowego stringa. Kilka linii kodu w C. Wszystko co robiłem było zaczerpnięte prosto z K&R – cienkiej książki o programowaniu w języku C.

Dzisiaj, podczas pracy w CityDesk, potrzebuję znać Visual Basic, COM, ATL, C++, InnoSetup, wewnętrzne mechanizmy Internet Explorera, wyrażenia regularne, DOM, HTML, CSS i XML. Wszystko wysokiego poziomu, w porównaniu z zagadnieniami z K&R. Ale, cały czas muszę znać rzeczy z K&R, inaczej byłbym spalony.

Dziesięć lat temu mogliśmy przypuszczać, że nowe paradygmaty programowania sprawią, że programowanie stanie się prostsze. Rzeczywiście, abstrakcje, które stworzyliśmy przez te lata pozwalają nam operować na nowych poziomach, z którymi nie mieliśmy do czynienia dziesięć albo piętnaście lat temu, takimi jak programowanie GUI i programowanie sieciowe. I rzeczywiście, te wspaniałe narzędzia, jak współczesne obiektowe języki z formularzami, pozwalają nam wykonywać naszą pracę znacznie szybciej. Ale nagle, pewnego dnia, będziemy musieli zidentyfikować problem z wyciekającą abstrakcją, a to zabierze 2 tygodnie. Kiedy potrzebujesz zatrudnić programistę głównie do programowania w VB, nie wystarczy zatrudnić programistę VB, ponieważ będzie on kompletnie zablokowany, kiedy abstrakcja VB zacznie przeciekać.

Prawo Nieszczelnych Abstrakcji ściąga nas w dół.

Data publikacji oryginału: 11 listopada, 2002

4 komentarze:

KonradKubiec.com pisze...

"Prawo Nieszczelnych Abstrakcji ściąga nas w dół."

Wielka prawda życiowa - niestety często zapomina, co później skutkuje wielkim bólem głowy :)

Eloar pisze...

Zawodne, to jest UDP.

Anonimowy pisze...

Wszystko jest zawodne, tylko tak naprawdę ilu programistów bierze pod uwagę możliwość wystąpienia błędów np. w pamięci RAM?

Karol Pasiut pisze...

"Prawo Nieszczelnych Abstrakcji ściąga nas w dół." - jednym z ciekawszych dosłownych przypadków był incydent boeinga 747 (nie pamiętam numeru lotu) który niemal skończył się katastrofą.

Otóż padł jeden silnik, ale piloci kontynuowali lot na autopilocie - czymś co powinno wyabstrahować fakt, że należy trzymać kurs i równy lot. Tyle, że autopilot nie przewidywał lotu na trzech silnikach. Efekt? Prawa strona miała mniej ciągu, więc leciała wolniej. Zatem samolot zaczął skręcać w lewo i jednocześnie przechylać się w lewo. Piloci zauważyli to dopiero kiedy samolot leciał praktycznie bokiem. Zanim wyrównali, przeciążenia urwały pół usterzenia. Oczywiście na szkoleniu mieli informację, że w przypadku awarii silnika należy wyłączyć autopilota. Nikt nie powiedział jednak po co, łatwo więc było to przeoczyć.

Prześlij komentarz

Related Posts with Thumbnails