Dzisiejszy wpis nie traktuje bezpośrednio o jakimś konkretnym problemie programistycznym, ale jest swojego rodzaju “niezapominajką” po pewnym problemie, na który natrafiłem podczas pracy nad jednym z projektów. Jako programista nigdy nie powinienem zakładać, że cokolwiek zachowa się “tak jak powinno”, a przynajmniej dopóki nie potwierdzą mi tego testy jednostkowe i podobne im narzędzia. Tym razem jednak złamałem tą zasadę, co w ramach bonusu przysporzyło mi kilku godzin szukania błędu… tam, gdzie go nie było.

Wstęp.

Jak zapewne zauważyliście, tytuł dzisiejszego wpisu jest na pierwszy rzut oka średnio zrozumiały, ale taki jest jego zamysł - jest to pół “felieton”, pół wpis problemowy. Zobaczymy, jak ocenicie moją zdolność pisania poza tematyką typowo techniczną i czy w ogóle nadaje się to do czytania. ;] Po prostu w pewnym sensie dotknął mnie fakt, że wykorzystywanie w codziennej pracy zaawansowanych narzędzi i technik programistycznych, zapominamy często o rzeczach trywialnych i sprawdzeniu podstaw, zanim zaczniemy drążyć temat, co dosyć wyraźnie udzieliło mi się “tamtego feralnego dnia”.

Nie wiem, czy ktoś pokusił się już o sklasyfikowanie tego typu problemów, dlatego, mając na uwadze twórczość autorów świetnego blogu The dark side of IT i przewijających się tam tematów proponuję nazwę “problem kabla od drukarki”, polegający na tym, że bardzo często przeklikamy wszystkie możliwe opcje w panelu kontrolnym, sprawdzimy tusze i papier… a zapomnimy podłączyć ją do prądu, dziwiąc się, czemu nie działa, “a przecież powinno”.

Dzisiejszy wpis jest dosyć długi, dlatego podzieliłem go na dwie części - felietonową i problemową, mam nadzieję, że wybór lektury jednej lub obu części będzie jasny w zależności od tego, czego od niego oczekujecie. Zapraszam do lektury.

Część “felietonowa”.

Gdybym prowadził prelekcję na tematy związane z programowaniem, podejrzewam, że zaryzykowanie stwierdzenia:
Programiści uczą się głównie na własnych błędach.
pozwoliłoby na uzyskanie całkiem wyraźnego pomruku zgody wśród ludzi na sali. Jest takie powiedzenie:
Ekspert, to taki człowiek, który pomylił się wystarczająco dużą liczbę razy.
Coś w tym jest, ponieważ nic tak nie uczy, jak życiowe porażki. Jeśli nie dostaniemy “zapłaty” za nasze niedociągnięcia, prawdopodobnie w przyszłości znowu popełnimy ten sam błąd. Nieważne, czy to będzie kara finansowa w postaci obcięcia pensji, kara “czasowa”, polegająca na zmarnowaniu [w najlepszym przypadku] kilku godzin na poszukiwanie przyczyny problemu, czy też kara “osobista”, polegająca na obniżeniu naszej pozycji w grupie - wiadomo, że społeczności programistów są zhierarchizowane podobnie jak środowiska hakerów - im więcej potrafisz, tym większa jest moc Twojego zdania i wpływ na innych, ale każda pomyłka daje okazję do zajęcia Twojego miejsca oraz ciężkie i powoduje czasochłonne odrabianie dawnego poziomu “zaufania” i “respektu”.

Im bardziej dotkliwa będzie ta “kara”, tym większa szansa, że następnym razem zastanowimy się co najmniej kilka razy, zanim klikniemy zły przycisk / napiszemy błędną linię kodu / scommitujemy błędne zmiany / wykonamy nieodpowiednie polecenie w konsoli / i tak dalej, i tak dalej…

Inną ważną sprawą jest to, że jako programiści absolutnie nigdy nie powinniśmy zakładać, że coś zadziała “tak jak powinno”, “no bo przecież gdzie się może wysypać głupi printf()…”. Jedynym sensownym wyjątkiem od tej reguły jest przejście danego fragmentu kodu przez testy jednostkowe lub podobny mechanizm sprawdzający, ale to także kwestia brzegowa, która nie powinna być uznawana za oddzielną regułę - zawsze trzeba brać pod uwagę możliwość błędnego napisania samych testów lub “bazylion” [bardzo lubię tą liczbę jako ekwiwalent “ilości nieokreślonej” ;]] innych rzeczy, których nie jesteśmy świadomi nawet jako programiści z kilku[nasto]letnim doświadczeniem w branży.

Dzisiejszy wpis jest sponsorowany właśnie przez moją pomyłkę polegającą na złamaniu powyższej reguły i karze czasowej, podczas której szukałem błędu tam, gdzie go nie było. Czas to dosyć cenny “surowiec”, także nie warto go marnować - stąd moja przestroga - uczcie się na moich błędach. ;]

Część “problemowa”.

Teraz trochę bardziej technicznie. Na czym polegał problem? Nie wnikając w jakość kodu, który odziedziczyłem, pracowałem nad jedną z funkcjonalności w pliku, który zawierał kod obsługujący kilka “akcji kontrolera” i drugim, zawierającym odpowiadające mu “akcje” widoku, że tak pozwolę sobie to nazwać. Poszczególne fragmenty były zawarte w opcjach “case” instrukcji switch. Wyglądało to mniej więcej tak:

<?php
switch($action)
	{
	case 'add':
		{
		// and so you code...
		}
	case 'edit':
		{
		// and so you code...
		}
	case 'delete':
		{
		// and so you code...
		}
	// and so you code...
	default:
		{
		// and so you code...
		}
	}
?>

Można mieć wiele obiekcji do tego typu “p0działu”, ale tak jak powiedziałem - kod był odziedziczony, więc nikogo nie interesowały moje komentarze na jego temat, po prostu miało działać. Nie byłoby w tym kodzie nic specjalnego, gdyby nie to, że jedna z akcji, załóżmy, że będzie to “add”, nie wykonywała się do końca poprawnie. Zamiast wyświetlić poprawnie kod dodający [widok formularza dodawania rekordu], wyświetlała formularz edycji krzycząc wniebogłosy, że brakuje mu danych rekordu.

Dopisałem wszędzie kody sprawdzające, czy zmienne istnieją i im podobne tak, że wyglądało to już nieźle, ale nadal nie było funkcjonalne. Przez długi czas patrzyłem się na oba pliki - jeden z widokiem formularza, drugi z kodem “kontrolera” i za nic nie mogłem znaleźć czegokolwiek, co naprowadziłoby mnie chociażby na trop błędu.

Zacząłem więc testować wszystko “starym, dobrym sposobem” wyrzucając na ekran każdą możliwą wartość w kilkunastu miejscach, żeby zobaczyć którędy przebiega “egzekucja” kodu. ;] Dopiero po kilkunastu minutach zauważyłem w pewnym momencie “dziwny ciąg znaków”. Zaczynał się on mniej więcej tak:

<?
// and so you code

Powoli w mojej świadomości pojawiło się światełko, niczym pociąg w tunelu… zbliżający się coraz szybciej, rosnący w oczach… stukoczący kołami niczym stado bizonów… już widziałem jego metalową karoserię, był już centymetr od mojej twarzy… jego zimne lico właśnie stykało się z moim nosem powoli zaczynając dzieło destrukcji… kiedy doznałem olśnienia, które skwitowałem słowem “co najmniej niecenzuralnym”.

Międzynarodowy Dzień [Caps Lock]‘a już minął, aczkolwiek mam nadzieję, że wybaczycie mi tą “niegrzeczność” - w myślach wykrzyczałem do siebie: “KTO OŚMIELIŁ SIĘ UŻYĆ W KODZIE SHORT OPEN TAGÓW???!!!ONEONEONE”. Ze względu na to, że moje myśli w tamtej chwili biegły szybciej niż pakiety w łączu światłowodowym, możecie swobodnie wstawić dowolny zestaw niecenzuralnych słów w miejsce spacji powyższego “krzyku”.

Być może w tym miejscu padnie pytanie, dlaczego nie skorzystałem z debuggera, które odbijam prostym stwierdzeniem “debugger to ja mam w oczach” i pozostawiam temat na dyskusję w innym terminie. ;] Tak na poważnie, to odpowiednie oddelegowanie prostych funkcjonalności do wielu miejsc [funkcji, klas] pozwala na dobrą analizę kodu bez wykorzystania oddzielnych narzędzi czyniąc debugger ostatecznością <- to tak w razie gdyby ktoś zamierzał mnie oskarżyć o barbarzyństwo programistyczne i stosowanie metod programowania znanych z epoki kamienia łupanego. Stosuję i owszem, ale nie w każdym przypadku. To nie był ten przypadek. ;]

Otóż, chciałbym zakomunikować wszem i wobec - jeśli rozpoczniecie plik / blok kodu używając “zła wszetecznego” w postaci short open tagów:

<?
// and so you code
?>

to jeśli na serwerze będzie wyłączona opcja short_open_tag [plik php.ini]:

short_open_tag = Off

Kod, który zostanie zawarty w tych tagach zostanie potraktowany jako zwykły tekst i wyrzucony bezpośrednio na ekran. W moim przypadku wyglądało to następująco [plik widoku]:

<?php
switch($action)
	{
	case 'add':
		{
		// and so you code...
		?>
		<!-- kod HTML widoku dodawania rekordu -->
		<?
		// cały ten blok zostanie wyrzucony bezpośrednio jako wynik działania skryptu
		break;
		}
	case 'edit':
		{
		// chyba już widać, że zostały ominięte sktruktury kontrolne
		?>
		<!-- kod HTML widoku edytowania rekordu -->
		<?php
		// and so you code...
		break;
		}
	case 'delete':
		{
		// and so you code...
		break;
		}
	// and so you code...
	default:
		{
		// and so you code...
		}
	}
?>

Tak jak napisałem w komentarzach w powyższym listingu, nie brzmi to zbyt ciekawie. Na szczęście błąd został znaleziony relatywnie szybko i bezlitośnie naprawiony. Udało mi się zdążyć z innymi poprawkami jeszcze tego samego dnia, także nie było aż tak strasznie.

Nie zmienia to jednak faktu, że takie praktyki programistyczne pozwalają na zastanowienie się, jak bardzo leniwy musiał być autor kodu, że nie chciało mu się nawet dopisać “php” po znaczniku otwarcia bloku. Swoją drogą, co Wy pomyślelibyście sobie w takim przypadku, zarówno o samym problemie w kodzie, jak i o programiście, który go napisał?

Podsumowanie.

Mam nadzieję, że powyższy wpis pozwoli Wam na wyciągnięcie odpowiednich wniosków i poszerzeniu doświadczenia o kolejną pozycję “czego nie robić”. Jak zawsze jestem otwarty na wszelkiego rodzaju sugestie i komentarze, do których zostawiania serdecznie zapraszam.

Trochę to niezwiązane z tematem wpisu, ale pewnie co niektórzy z Was zauważyli, że przez chwilę był dostępny post z moimi pomysłami na wpisy. Cóż, zachciało mi się edytować posta, w którym trzymam jedną z części takiej listy i niestety niezbyt świadomie “pomyliłem guzik”, klikając “Publikuj”, zamiast oczywistego “Zapisz szkic”. Z tego powodu uprzejmie uprasza się te 6 osób, które zanotowało jego “odsłonę” o nieujawnianie zawartych w nim informacji. Z góry dzięki. ;] I tak miałem dużo szczęścia, że wpis nie poleciał do kanału RSS ani do profilu na Facebooku.

PS Zgłaszam feature request do zespołu tworzącego WordPressa o możliwość oznaczenia pewnych wpisów jako “niepublikowalne”. ;]