Mam malować?

Uwierzyć czy zrozumieć?

Znamiennym jest fakt, że podczas wykładów z programowania studenci, którzy do tej pory nieźle przyswajali wiedzę, przestają cokolwiek rozumieć, jeśli w temacie pojawiają się wskaźniki i rekurencja. Z tej opresji wychodzi obronną ręką niewielki odsetek tych, których sposób myślenia jest na tyle plastyczny, iż potrafią sobie te sprawy wyobrazić i przyswoić.

Powiedzmy sobie szczerze: niektórzy już na etapie szkoły średniej wiedzieli, że nie podołają umysłowo naukom ścisłym, więc od razu uczciwie zdefiniowali swą przyszłość w sferze nauk humanistycznych (stąd tak wysoki obecnie popyt na socjologię, psychologię, turystykę - co nijak nie przystaje do zapotrzebowania rynku na specjalistów z tych dziedzin). Ci zaś, którzy byli wobec siebie nieuczciwi, oszukiwali siebie samych w kwestii własnych umiejętności, nie poddali się wcześniej testom psychologicznym określającym ich predyspozycje umysłowe, a wydawało im się, że w informatyce znajdą swoją przyszłość, wybrali studia informatyczne.

Pół biedy, jeśli droga do studiowania była ciernista, a więc wymagała zdania egzaminów i pokonania licznej konkurencji, co na szczęście towarzyszyło i ciągle towarzyszy naborowi na prestiżowe politechniki. Również sam cykl studiowania, naszpikowany dużą dozą nauk matematycznych, jest tam zdecydowanie odmienny, aniżeli na kierunkach o tym samym profilu na uczelniach prywatnych. Jest to jednak kwestia jakości dydaktyki, warta poruszenia w zupełnie innym artykule.

Na szczęście nie każdy absolwent informatyki zostaje programistą. Co więcej, aby zostać programistą nie są na ogół wymagane studia kierunkowe. Wystarczy odpowiednio długa praktyka. Jeśli jednak praktyka jest uskuteczniana bez jakiejkolwiek kontroli jakości stworzonego kodu, jak również sam programista nie przejawia tendencji do samodoskonalenia i korygowania jakości swojej produkcji, to efektem tego jest najczęściej duża ilość złej jakości programów. Jak już wspominałem, aby zostać dobrym programistą, trzeba mieć odpowiednie inklinacje umysłowe. Trudno mi w tej chwili stwierdzić, co tak naprawdę temu sprzyja, ale przypuszczam, że są to uwarunkowania genetyczne. Taka sama umiejętność, jak talent do muzyki lub plastyki. Po prostu nie każdy go posiada. Nieumiejętność poruszania się w obrębie dynamicznych i relatywizowanych struktur danych jest według mnie także pochodną nieznajomości programowania w językach niskiego poziomu. Często w asemblerach mamy do czynienia z adresacją pośrednią, opierającą się na parze rejestrów określających bazę oraz przemieszczenie względne. Postrzeganie tego relatywizmu jest dla niektórych ciernistą drogą nie do przejścia.

Dlaczego w takim razie złych programistów nie eliminuje skutecznie rynek komercyjny, jak i rynek wolnego oprogramowania? Sprawa jest dosyć prosta. W przypadku otwartego oprogramowania istnieją raczej małe szanse na akceptację niewypałów programistycznych. W tym środowisku cała prawda jest z natury naga, co nie rokuje dobrze beztalenciom. A jeśli już zdarzają się błędy - może nie w sztuce, ale w logice - to inni programiści najzwyczajniej je weryfikują. W przypadku "zamkniętej" komercji jakość można kontrolować jedynie wewnątrzfirmowymi procedurami, jeśli takowe istnieją i są na tyle skuteczne, aby sprawdzać nie tylko poprawność formalną kodu. Zbyt wiele jest jeszcze firm, które wypuszczają na rynek oprogramowanie tak jak je "programista stworzył". Kontrola jakości bywa ciągle piętą achillesową procesu produkcji oprogramowania.

Gdy trzęsą się ręce

Testując kandydata ze znajomości języka programowania, najlepiej zadać mu krótkie zadanie, które nie dość, że udowodni, czy rzeczywiście metody stosowane w danym środowisku są przez niego opanowane, to ujawni sposób jego myślenia. Do takich typowych zadań testujących można zaliczyć odwrócenie "w miejscu" łańcucha znaków, odwrócenie listy połączonej czy zliczenie ustawionych bitów w bajcie.

Ze względu na ograniczenia czasowe nie powinno zadawać się zagadnień, których rozwiązanie wymaga pisania zbyt wielu instrukcji. Jeśli natomiast kandydat utrzymuje w swoim CV, że zna kilka języków programowania, powinien to udokumentować właśnie przy okazji tych prostych zadanek. Takie np. odwracanie ciągu znakowego można przeprowadzić na różne sposoby w zależności od języka, ale znacznie lepiej, jeśli zostanie to wykonane poprawnie w jednym języku, aniżeli niewłaściwie za pomocą pięciu. Najczęściej programiści popełniają tu trywialne błędy metodologii, próbując powołać nowe zmienne (a więc zajmujące miejsce w pamięci) pełniące rolę buforów przyjmujących przekształcony tekst. Prawidłowo zadanie to można spokojnie wykonać w jednej instrukcji, przypisując zmiennej źródłowej jej przekształconą wartość. Czy do tej operacji wykorzysta się wskaźniki i rekurencję (C, Pascal), czy tylko metody i funkcje tekstowe (Java, Visual Basic), zależy od zaplecza danego języka.

Niemniej ktoś uważający się za programistę, a więc twierdzący, że ma określone ku temu umiejętności, powinien w znanym sobie języku przeprowadzić tę operację w optymalny sposób. Zdarza się jednak aż nadto często, że programiści (a w zasadzie osobnicy podający się za nich) nie rozumieją zupełnie operacji na wskaźnikach, dynamicznych strukturach danych, rekurencji. Przerasta to zwyczajnie ich poziom abstrakcyjnego myślenia. Pomimo tego ciągle jeszcze piszą programy.

Może dziwić, jakiemu celowi służy takie akurat przepytywanie, skoro większość funkcji tego pokroju została już nieraz napisana i są one składnikami wielu bibliotek. Raczej wątpliwe, aby programista miał wyczyniać podobne rzeczy w nowej pracy. Tym bardziej, że obecnie - z wyjątkiem firm technologicznych oczywiście - oprogramowane komercyjne tworzone jest przeważnie za pomocą wszelakiego rodzaju narzędzi wspomagających, w dużej mierze generujących kod na podstawie projektu graficznego. Owszem, narzędzia klasy RAD<sup>5</sup> wspomagają proces produkcji oprogramowania, niemniej wcale nie zwalniają z konieczności tworzenia kodu "na piechotę". Rozrysowanie formatki ekranowej i rozplanowanie na niej obiektów graficznych nie powoduje, że za pomocą automatu określimy zachowanie tych elementów. Zdarzenia związane z obiektami należy oprogramować, mechanizmy dostępu do danych i obsługę błędów także. I wykonuje się to podobnie zarówno w tak lekceważonym przez "poważnych programistów" Accessie, jak i w każdym innym środowisku programistycznym wspieranym przez narzędzia typu builder (Java, C++, Delphi, Visual Basic). Na tym właśnie poziomie wymagane jest tworzenie kodu wprowadzającego dynamikę w statyczne plansze. Od tego, jak ten kod zostanie zbudowany, zależy jakość całego projektu, niemniej zdarza się, że programowanie przy współudziale środowiska RAD jest czynnikiem sprzyjającym powstawaniu kodu nieefektywnego, często okraszonego "magicznymi elementami". Nie jest to jednak wina samego środowiska narzędziowego, ale tego, co wyczyniają w nim programiści bez wyobraźni lub bez wiedzy. Zresztą nie tylko współcześnie i nie za sprawą graficznych metod projektowania zdarzały się produkcje nieefektywne z dużą liczbą redundantnych fragmentów funkcjonalnych. Przyczyną było bezmyślne wielokrotne kopiowanie tych samych linii kodu, zamiast tworzenia bardziej uniwersalnych metod czy funkcji. Narzucone odgórnie ostre terminy wykonawcze nie sprzyjają z reguły optymalizacji. Są to jednak pozory, gdyż docelowo optymalizacja nie jest trwonieniem czasu i w efekcie może przynieść więcej korzyści, nawet jeśli początkowo spowalnia nieco prace.

Powstawaniu nieefektywnego kodu sprzyjają coraz szybsze komputery maskujące opóźnienia wynikające z wykonywania nadmiarowego przetwarzania. Wiele lat temu rozmawiałem z szefem zespołu projektowego tworzącego w Clipperze oprogramowanie dla dużych zakładów produkcyjnych. Gdy zapytałem o wydajność przetwarzania w tej technologii (plikowe bazy danych), dowiedziałem się, że jeśli dostęp do danych zaczyna się zbytnio ślimaczyć, wmawia się klientowi konieczność zmodernizowania sprzętu. Klient niebędący specjalistą w tej dziedzinie z pewnością uwierzy firmie autorskiej bez wnikania, co jest rzeczywistą przyczyną słabnącej wydajności. Nie każdy jednak zespół programistyczny tworzy komercyjną chałturę za niezłe pieniądze. Spora ilość software'u ma zastosowanie pomocnicze, narzędziowe dla programistów aplikacyjnych i przy ich wytwarzaniu rzeczywiście istnieje wewnętrzny reżim kontrolny, nie zezwalający, aby te zestawy funkcji miały złą jakość. Niemniej najlepszym kontrolerem jakości jest jawność kodu źródłowego, pozwalająca innym programistom na jego ulepszanie.

W trakcie wywiadu rekrutacyjnego nie ma okazji do tworzenia zbyt obszernych źródeł oprogramowania, niemniej czasami kandydat dostarcza gotowe fragmenty swego dorobku programistycznego, tzw. portfolio. Jest to bardzo dobre źródło poznania metodologii i uprawianego stylu programowania. Czasami widać od razu, że praktyka tworzenia programów należy z gruntu rzeczy do nieprawidłowych, czy to pod względem stylu, czy logiki. Niedawno dane mi było oglądać taki właśnie przykład dorobku człowieka podającego się za programistę PHP, czyli z natury rzeczy języka skryptowego używanego na stronach internetowych. Niedługa sekwencja prezentacyjna, składająca się w głównej mierze z procedury logowania, zawierała w sobie jeden z najbardziej symptomatycznych błędów - popełnianych przez nowicjuszy i nieumiejętnych programistów w tego rodzaju systemach - a dopuszczających możliwość przedarcia bariery ochronnej metodą tzw. SQL Injection. A więc nie było czym się chwalić.

Mając możliwość osobistej weryfikacji kandydata na stanowisko programisty, można przyglądać się bezpośrednio stylowi jego pracy, czyli pospolicie rzecz ujmując patrzeć mu na ręce. Ta część "wywiadu" bardzo dużo mówi o jego umiejętnościach i wprawie warsztatowej. Otóż dobry programista ma wiele poprawnych przyzwyczajeń, będących pochodną nauczek na własnych błędach, na których kiedyś w przeszłości nieźle się "przejechał". Przede wszystkim doświadczony programista ma wypracowany sposób nazewnictwa zmiennych, klas i metod. Najczęściej stosuje jeden z ogólnie przyjętych stylów nazewniczych i nawet jeśli będzie to tzw. styl węgierski<sup>6</sup>, należy to uszanować, jeśli tylko jest stosowany konsekwentnie. Cechą charakterystyczną niedoświadczonych programistów bywa często zbyt rozwlekły sposób nazywania zmiennych, zwłaszcza tych często używanych. Jeśli w kodzie programu występują zmienne o przydługich nazwach, do których występują częste odwołania, to na pewno nie jest to najwłaściwszy styl. Doświadczony programista raczej jest oszczędny pod tym względem i zamiast nazywać zmienną "wartoscBleduPrzekazanaPrzezSerwer", zastosuje równie wiele mówiącą, a nieporównywalnie krótszą "wartoscBleduSerwera" lub "kodBleduSerwera". Ja osobiście użyłbym "serverErCode" lub "erCodeServer", ale to już kwestia osobistych preferencji, także językowych. Podobnie lokalne wskaźniki kontrolujące warunki wykonywania pętli nie powinny nosić zbyt rozwlekłych nazw, nie mówiąc o tym, że te rzeczywiście lokalne zmienne nazywa się tradycyjnie "i" lub "j" dla wartości liczbowych lub "ok" dla wartości boolowskich.


TOP 200