🐕 O psie, który jeździł koleją. 🚂 Jak zapłacić za koszyk w serwisie PKP? Odrobaczanie systemu.

Powyżej zostało użyte zdjęcie psa ze strony Pies.pl

O psie, który jeździł koleją, albo bardziej o naszym polskim PKPowym isPies czy “kłopotliwych zniżkach” słyszał w branży IT już prawie każdy. Chociaż w międzyczasie doszła “szybka szpachla ifem” w postaci fragmentu if(ZakupBiletuUtil().isCOVID()), to dzisiaj także nie o tym. Dzisiaj historia z życia na kodach wzięta. Co stanie się, kiedy trzech programistów podejmie się karkołomnego wyzwania zapłacenia za jedyny bilet w koszyku? Czy zwyciężą “Cannot read properties of null”? W jaki sposób wyruszą w swoją podróż? Czy przeprowadzą odrobaczanie? A może się poddadzą? Przeczytaj, jak 3 programistów bilet na PKP kupowało i pokonali siejącego czerwone logi buga, który mieszkał w przypadku brzegowym.

  • Ten wpis pomoże Ci rozwiązać problem w serwisie PKP, jeśli masz w koszyku bilet/y na jedno połączenie kolejowe i nie możesz dokonać płatności.
  • Jako programista przeczytasz trochę o Debuggerze w Google Chrome i Ubiquitous Language z Domain-Driven Design. Dowiesz się, kiedy Polskie wyrazy w kodzie mają uzasadnienie!
  • Zabłądziłeś/aś w odmęty internetu? W najgorszym przypadku jedynie umilę Ci trochę czas, jeśli nie kupujesz żadnego biletu i znasz się na frontendzie oraz DDD.

Enjoy!

Z życia na kodach

Bywają tacy podróżni, którzy mogą “nie dbać o bagaż, nie dbać o bilet” tak jak bohater książki “O psie, który jeździł koleją”. Jednak w naszym przypadku taka zasada nie miała zastosowania. Zaopatrzenie się w bilet powinno być jedną z najprostszych czynności związanych z podróżą, ale nie tym razem!

I może nic by się nie udało, gdyby nie Ci wścibscy programiści. A właściwie ja i dwóch kolegów frontendowców — pozdrawiam Marcina i Konrada :) Historia niestety jest niezmyślona i dużo w niej prawdy.

Weszliśmy na stronę PKP i wykonaliśmy rutynowe kroki zakupu biletów.

  1. Wprowadziliśmy kryteria wyszukiwania — stację początkową i docelową oraz datę wyjazdu.
  2. Wybraliśmy jedno z proponowanych połączeń połączenie.
  3. Określiliśmy ile biletów, jakiego typu chcemy kupić.
  4. Nie zapłaciliśmy za wybrane bilety, ale wybraliśmy opcję “Dodaj do koszyka”.
Zakup biletów w serwisie Intercity PKP. Kroki dodania do koszyka.

Zakup biletów w serwisie Intercity PKP. Kroki dodania do koszyka.
(Kliknij, aby powiększyć)

Plan był taki, aby wyszukać też bilety na kolejne połączenia. Jednak po chwili, zdaliśmy sobie sprawę, że nie wiemy jeszcze, kiedy chcemy wracać. Dlatego porzuciliśmy na razie ten pomysł i postanowiliśmy od razu zapłacić za bilety w koszyku.

Bug zamieszkały w przypadku brzegowym

Bilety w koszyku figurowały jako jedna pozycja, ponieważ były wybrane dla tego samego połączenia kolejowego. Jak się okazało, było to bardzo zgubną decyzją i tu leży isPies pogrzebany. Niestety, zaatakował nas znienacka bug siejący czerwonymi logami w konsoli. Jakie było nasze zdziwienie (no dobra, tak naprawdę to nic nas już na tej stronie nie zaskoczy), kiedy kilkukrotne klikanie w button “Kupuję i płacę” nie powodowało żadnej reakcji systemu (stan aktualny na 24.11.2021). Słyszałem kiedyś historię, o tym, że przez kilka dni od startu nowej firmy, nikt nie kupował produktu przez internet. Cały sztab ludzi analizował sytuację, marketingowcy zastanawiali się, co jest nie tak z reklamą itp. A okazało się, że przyczyna leży zupełnie gdzie indziej. Po prostu przycisk “kup teraz” nie działał…

W naszym przypadku konsola w przeglądarce zabarwiła się na czerwono, a wszystko jakby krzyczało: “zawróćcie z tej drogi”, niczym potwory w Scooby-Doo. Ale my postanowiliśmy się nie poddawać i przeprowadzić odrobaczanie (luźne tłumaczenie dla ang. debugging) tego kodu z błędów. Zbadaliśmy pacjenta za pomocą trybu Inspect w Google Chrome oraz debuggera kodu JavaScript. Błąd w konsoli wyglądał następująco:

Uncaught TypeError: Cannot read properties of null (reading 'value')
    at platnosc (platnosc.js?ver=883646852:63)
    at HTMLInputElement.<anonymous> (main.js?ver=883646852:1449)
    at HTMLDocument.dispatch (jquery-3.2.1.min.js:3)
    at HTMLDocument.q.handle (jquery-3.2.1.min.js:3)

Jako programiści postanowiliśmy pobawić się w Devtektywów i osiągnąć nasz cel. Nadszedł czas na tripple programming.

Szybkie otworzenie narzędzi deweloperskich i… znaleźliśmy kłopotliwą linijkę (nie zniżkę) w pliku platnosci.js. Problem nie leżał w isPies, tylko zupełnie gdzie indziej…

document.getElementById('faktura_nazwa_firmy').value = convertString(document.getElementById('faktura_nazwa_firmy').value);

Przeprowadzając łączenie kropek między błędem a linijką, widać, że null został zwrócony przez document.getElementById('faktura_nazwa_firmy'), bo to z tego obiektu chcemy odczytać właściwość value. Oznacza to, że na stronie jest brak pola faktura_nazwa_firmy, którego wartość jest konieczna do wykonania kodu po wciśnięciu przycisku. Dlaczego kod wymaga czegoś takiego, skoro ja wcale nie kupuję biletu na firmę? To już pozostanie tajemnicą. Poradziliśmy sobie z tym bardzo łatwo. Najprostszym rozwiązaniem było dodanie jednej linijki kodu w kodzie HTML strony. <input id="faktura_nazwa_firmy" value="NaKodachPL" /> Dzięki temu pojawiło się dodatkowe pole, kod wykonał się bez rzucenia błędem i mogliśmy przejść do płatności. Dalej już wszystko działało. Po zapłaceniu bilety pojawiły się w systemie i podczas kontroli przez konduktora, też nie było problemu :) Bug został przechytrzony!

Przycisk nie reaguje. Po otwarciu konsoli widoczny błąd.

Przycisk nie reaguje. Po otwarciu konsoli widoczny błąd.
(Kliknij, aby powiększyć)

Co to za hackowanie!?

Jeśli dopiero zaczynasz przygodę z programowaniem, to z pewnością zastanawiasz się: “Można tak sobie zmieniać kod strony”? Czy to nie jest jakieś hackowanie? Otóż nie… Pamiętam, jak w czasach gimbazy roiło się w internecie od poradników, w stylu “Jak zarobić 99999 surowców w grze Plemiona”. Autorzy tych filmików po prostu zmieniali wartość w kodzie strony. Oczywiście taka zmiana w praktyce nie dawała zupełnie nic. Dlaczego? Tak samo, jak w przypadku dodania pola przy zakupie biletu. Twoja przeglądarka działa w taki sposób, że pobiera kod strony i wykonuje go u Ciebie na komputerze. Dlatego też, skoro kod, znajduje się u Ciebie, to możesz też wprowadzać dowolne zmiany. Jednak są one widoczne tylko dla Ciebie i nie mają wpływu na innych graczy, czy klientów PKP. Przy ponownym otworzeniu strony nawet na Twoim komputerze, pliki serwisu zostaną ponownie pobrane z serwera, a wcześniej wprowadzone zmiany znikną.

Zreprodukowanie buga

Znacie ten dowcip, jak wchodzi tester do baru i zamawia 1 piwo, potem 999999 piw, oczywiście też 0 i -1 piwo? Sytuacja podobna jak na poniższym filmiku HRejterów.

Tak samo tutaj, prawdopodobnie trafiliśmy na pewien przypadek brzegowy (ang. edge case). Postanowiłem zreprodukować ten błąd i występuje on jedynie, kiedy w koszyku mamy jedną pozycję (bilety na jedno połączenie kolejowe). Dlatego pewnie w większości przypadków funkcjonalność działa poprawnie (kto używa koszyka dla jednego biletu!? ja :D). Ty też możesz go powtórzyć, wykonując kroki, które opisałem. To bardzo istotne, jeśli pracujesz jako tester, żeby dokładnie opisywać jakie Twoje działania spowodowały błąd. Niezastąpiony może być też filmik obrazujący problem!

Dlaczego dla większej ilości biletów koszyk działa? Nie mam pojęcia. Jeśli chcesz, podejmij wyzwanie, przeanalizuj błąd i daj znać w komentarzu! A może pracujesz w PKP i zaraz zabierasz się za to zgłoszenie? Pamiętajcie na początek o odpowiednich testach, aby pokryć ten przypadek!

Ucz się na błędach

Błędy są po to, żeby się na nich uczyć! Najlepiej wiedzą o tym osoby odpowiadające za nasze bezpieczeństwo podczas podróży samolotem. Z katastrof lotniczych zawsze wyciąga się wnioski i wprowadza nowe procedury, aby coś podobnego się nie powtórzyło. Szczęście, że to tylko sprzedaż biletów i nie spowoduje to żadnego zderzenia pociągów (co innego, kiedy od softu zależy ludzkie życie) :)

Jednak, aby nasze systemy nie miały takich defektów, konieczne jest ich odpowiednie przetestowanie. Chcesz wiedzieć jak pisać aplikacje, aby były testowalne? Zastanawiasz się, w jaki sposób pisać utrzymywalne testy? Zdradzę Ci teraz, od czego zacząć naukę. Zrób w tym kierunku pierwszy krok i już dzisiaj zaopatrz się w książkę Unit Testing Principles, Practices, and Patterns: Effective Testing Styles, Patterns, and Reliable Automation for Unit Testing, Mocking, and Integration Testing with Examples in C#. Tytuł niebywale długi jak nazwy klas w Javie, ale temat jest jednak obszerny :D

Wstań z kanapy! A raczej siadaj do komputera… Masz trochę czasu, zanim kurier zapuka do Twoich drzwi. Już teraz warto zajrzeć na blog autora książki Enterprise Craftsmanship - Vladimir Khorikov. Znajdziesz tam wiele o testach, ale też o Domain-Driven Design.

Nie wiem, czy wybrano psa, ale wiem, że Ponglish czasem nawet na propsie

NaKodach.pl | Lampo, czyli prawdziwy pies, który jeździł koleją. Żył we Włoszech w latach 50. XX wieku.

Lampo, czyli prawdziwy "Pies, który jeździł koleją" żył we Włoszech w latach 50. XX wieku.

Skoro jesteśmy już w temacie PKP, to warto przypomnieć, że swego czasu kod na stronie przewoźnika był pośmiewiskiem internetu i spadła na niego fala hejtu. Ważne jest, aby krytykować sam kod, a nie programistów, którzy go napisali. Na pewno każdemu zdarzyło się napisać w karierze podobnego potworka :) Ale co z tymi Polskimi nazwami w kodzie? Czy ktoś w końcu wie czyWybranoPsa i dlaczego? Czy mieszanie Polskiego z Angielskim, tzw. “Ponglish” jest tutaj uzasadniony? Tego nie wiem, ale odejdźmy na chwile na bezpieczną odległość od torów i zadajmy sobie pytanie kiedy warto rozpatrzyć taki sposób zapisu.

Jak wiemy w IT zawsze “to zależy”. Super przykład podaje Łukasz Szydło w rozmowie z Mariuszem Gilem w podcaście Better Software Design O różnych odmianach Ubiquitous Language. Szczególne branżowe słownictwo może być ciężko przetłumaczalne, a jedną z najważniejszych zasad Domain-Driven Design jest utrzymanie języka biznesowego w kodzie. To podstawa do tego, aby nasz kod mógł ewoluować wraz z biznesem. Jeśli jesteśmy w stanie przetłumaczyć słownictwo domenowego 1 do 1, a biznes funkcjonuje w innym języku niż angielski, to warto utrzymywać słowniczek, żebyśmy nie mieli kilku wyrazów na to samo. Byłem już w projekcie, gdzie np. na paliwo raz było słowo Gas, a raz Fuel. Polski język ma sens, kiedy nie potrafimy przetłumaczyć dokładnie, albo w naszym języku mamy więcej określeń na to samo niż angielski. Łukasz daje przykład Glebogryzarki w branży rolniczej, a także instytucji finansowych. Po więcej odsyłam Cię do podcastu i na mój mailing. Jaki mailing? Wszystko masz poniżej :)

✉️ isCovid - jak to zrobić lepiej?

Kod PKP, chociaż jest już legacy, to nie można o nim zapomnieć. Na przepisanie zapewne nie ma czasu, a dochodzą nowe wymagania. Ostatnio jak mógł zauważyć uważny obserwator, doszła “szybka szpachla ifem” w postaci fragmentu if(ZakupBiletuUtil().isCOVID()). Taką implementację możnaby nazwać anty-wzorcem. Dostrzegam tutaj pewną przypadłość, którą można nazwać “schizofrenię kontekstów”. Jest to problem, o którym piszę więcej na mailingu Domain-Driven Design. Kod nie wie, czy funkcjonuje w kontekście epidemii, czy nie i musi co chwile pytać samego siebie czy isCovid. To rodzi potrzebę ciągłej ifologi i zagnieżdżania, tam, gdzie kod tak naprawdę powinen zostać rozdzielony na konteksty i uproszczony w myśl zasady “dziel i zwyciężaj”. Takie rozwiązanie może być dobrym sposobem na “Deadline-Driven Development”, ale z pewnością nie jest to inwestycja w przyszłość.

Chcesz uczynić swoje programowanie bardziej pragmatyczne? Marzysz, żeby modelować procesy biznesowe zamiast tabelek w bazie danych? Już pierwszą dawkę wiedzy otrzymasz po zapisaniu się na mailing Domain-Driven Design. Będziesz miał ze mną bezpośredni kontakt i dostaniesz praktyczne zadania prosto na swoją skrzynkę! ZAPISZ SIĘ TUTAJ

Inni też tym żyją

  • Sławomir Sobótka - DDD Q&A - wersja rozszerzona https://www.youtube.com/watch?v=do-xqIbKZ_8

  • DevUpgrade - Sławek Sobótka & Jakub Pilimon | Tutaj zobaczysz jak pisać bez ifologi https://devupgrade.online/

  • Radek Maziarka | DLACZEGO BOUNDED CONTEXTY SĄ WAŻNE – JĘZYK https://radekmaziarka.pl/2018/08/20/dlaczego-bounded-contexty-sa-wazne-jezyk/

  • Wykop | Kod PKP jest złotem https://www.wykop.pl/wpis/30442309/kod-pkp-jest-zlotem-function-czywybranopsa-var-isp/

  • Kod na stronie ZUS KOD 1 KOD 2

{/* Programista wiedzie życie na kodach. Gdzie te kody mogą się przydać? Na pewno nie obejdzie się, aby zakupić koszyk na stronie PKP. Co umożliwa bycie programistą? Piszę tutaj o wielu rzeczach. Ślub, dobrze płatna praca bez ryzyka. Jednak czasem mogą się przydać w tak trywailnej wydawałoby się operacji jak zakup biletu na połączenie PKP. 42 minuty podcast

Jak Ci się podobało? Chciałbyś może przeczytać analizę tego, dlaczego dodawanie nowych funkcjanolności za ifem jest?

Tak samo tutaj potrzeba nam pamiętać, że testy piszemy na przypadki. Może jeden bilet z koszykiem nie ma sensu? Ale co wtedy zrobić?

Warto poznać debugger w Chrome, umożliwia nie tylko takie “hacki”, ale też jest bardzo przydatny przy developmencie.

Czytając “O Psie, który jeździł koleją” nie sposób doszukać się zakupu biletu przez psa. Jednak jeśli już programista chce się wybrać w podróż PKP z pewnością trzeba zaopatrzyć się w bilet.

Nie o psie, który jeździł koleją, choć wątek psa oczywiście się pojawia. Ale o programistach, którzy chcieli przejechać się polskim PKP, ale system postanowił.

“Wsiąść do pociągu byle jakiego, Nie dbać o bagaż, nie dbać o bilet”. Takie możliwości ma niestety tylko Maryla Rodowicz. Wybierając się na podróż pociągiem PKP, jednak trzeba zaopatrzyć się w bilet. Jednak zakup biletu przez system internetowy przewoźnika, może być nie lada wyzwaniem.

W Polskiej branży IT opowieść o Psie, który jeździł koleją może kojarzyć się jednoznacznie ze słynnymi fragmentami z kodu PKP jak

🕵️ Devtektywi na tropie |

Tatooine - planeta o dwóch słońcach.

Zakup biletów w serwisie Intercity PKP. Kroki dodania do koszyka.

Tatooine - planeta o dwóch słońcach.

Zakup biletów w serwisie Intercity PKP. Kroki dodania do koszyka.

Tatooine - planeta o dwóch słońcach.

Zakup biletów w serwisie Intercity PKP. Kroki dodania do koszyka.

*/}
Podziel się wpisem:

✉️ Lista Mailingowa

Otrzymasz materiały o Event Sourcingu, Event Modelingu, Domain-Driven Design, programowaniu obiektowym i funkcyjnym oraz innych powiązanych tematach. A także zaproszę Cię na wspólne sesje modelowania.

🫱🏻‍🫲🏽 Mentoring Online

Nauka Domain-Driven Design? Podział projektu na moduły? Zaplanowanie architektury? Konsultacja CV? A może rozwój w kierunku Seniora? Spotkajmy się! Umów się na mentoring lub konsultacje. Wspólnie opracujemy plan dla Ciebie 🫵

📖 SOLIDne CV?

🤔 Szukasz teraz nowej pracy? Masz wymagane kompetencje, ale nikt nie daje Ci szansy ich zaprezentować? Zmień to!

CV na kodach

💪 Przeprowadziłem ponad 100 rozmów rekrutacyjnych i widziałem tysiące CV. Nieraz miałem okazję porównać CV odrzucane, z tymi które robiły największe wrażenie. Na tej podstawie opracowałem porady, które pomogą Ci się pozytywnie wyróżnić i przejść ten etap rekrutacji.

Więcej o życiu na kodach

💔 Hexagonal Architecture Ci w tym nie pomoże (ale Vertical Slices już tak)

Porty i Adaptery zrobiliśmy wzorcowo. "Możemy podmienić Postgresa na Mongo z dnia na dzień" - pochwaliliśmy się na demo. I rzeczywiście - mogliśmy. Tylko nikt tego nie potrzebował. A co było tak naprawdę potrzebne? To co realnie zabiło projekt - coupling między funkcjonalnościami, blokowanie się programistów - zostało nietknięte. Jak zaadresować ten problem?

🍕 Dynamic Consistency Boundary: SourcingCriteria != AppendCriteria

Nie każde zdarzenie, które wpływa na Twoją decyzję, może złamać Twój niezmiennik. Rozdzielenie SourcingCriteria i AppendCriteria w DCB pozwala odblokować współbieżność bez poświęcania spójności. DCB w końcu pozwala osiągnąć to co obiecywały Agregaty w Domain-Driven Design: granice spójności jak najmniejsze, ale wystarczająco duże.

⚔️ Moc i magia Domain-Driven Design w świecie Heroes III: Mapa Kontekstów i Hydra - co mają wspólnego i jak kąsają Twój biznes?

Zobrazujemy zależności między modułami i zespołami — kto będzie musiał się do kogo dostosować, a gdzie potrzebne są wspólne ustalenia i partnerstwo. Context Mapping jest w stanie uwidocznić Ci problemy niedostrzegalne na pierwszy rzut oka, ale niestety odczuwalne przez wszystkich. Możesz wziąć niebieską pigułkę i dalej żyć w błogiej nieświadomości, aż brutalne realia projektu nie wyrwą Cię z tego marazmu. Albo przeczytać ten wpis...

Mailing Domain-Driven Design

Wciąż za mało życiowych cheatów?

Zostaw swój adres e-mail i zobacz moje spojrzenie na codzienność programisty.

Na sam początek opowiem Ci o zetknięciu z Domain-Driven Design, zmianie myślenia i nowej erze mojego programistycznego ja.

Możesz liczyć na materiały o Event Sourcingu, Event Modelingu, DDD, programowaniu obiektowym i funkcyjnym oraz innych powiązanych tematach.

Na pewno poświęcę trochę maili umiejętnością miękkim.

Będziesz też informowany o nowościach Życia na kodach prosto na Twoją skrzynkę!

SzkolenieWarsztaty dla zespołów
Event Modeling
  • Proces, który buduje zaufanie
  • Od modelu wprost do kodu — 1:1
  • Idealny kontekst dla ludzi i AI
  • Spec-Driven Development zrobione dobrze
DDDEvent SourcingAIVertical Slices
Sprawdź, czy to dla Ciebie