Sytuacja kobiet w IT w 2024 roku
1.12.202210 min
Jakub Wójcik
Comarch

Jakub WójcikFront-End DeveloperComarch

Jak zacząć programować lepiej

Poznaj osiem praktyk, dzięki którym zaczniesz programować na wyższym poziome.

Jak zacząć programować lepiej

Na początku swojej kariery większość programistów błądzi. Nie ma w tym nic złego. Nawet doświadczony programista nie czuje się od razu „pewnie”, zanurzając się w nowej technologii. Jednak nieznajomość pewnych reguł, właściwości języka i ogólnej wiedzy programistycznej zdobytej na przestrzeni lat (często przekładającej się na szybsze zrozumienie niejednego nowego frameworka), może poskutkować kodem napisanym niezgodnie z przeznaczeniem lub po prostu błędnie. W tym krótkim artykule postaram się wypunktować rzeczy, na które moim zdaniem warto teraz lub warto było zwrócić uwagę na początku przygody z programowaniem.

Artykułu nie należy traktować jako jedynego źródła wiedzy na temat wymienionych problemów. Raczej ma być on wskazówką, początkiem drogi ku lepszemu. Zachęcam do czynności, której ćwiczenie i stosowanie pomoże Wam rozwiązać niejeden problem. Mówię oczywiście o umiejętnym wyszukiwaniu informacji na dany temat, bądź problem w Internecie.

Niedocenienie debuggera

Debugger to potężne narzędzie, często niedoceniane na początku kariery. Umożliwia głównie (choć jego możliwości nie kończą się na tym) analizę kodu „krok po kroku”, w celu wykrycia błędów w kodzie lub często również zrozumienia niejasnej dla nas metody. Pierwsze spotkania z debuggerem (w moim przypadku Chrome DevTools) zawsze kończyły się dla mnie tak samo. Ogrom wbudowanych narzędzi do optymalizacji wydajności, monitorowania zapytań sieciowych, wbudowana konsola, przeglądarka lokalnych storage i wiele innych opcji po prostu przytłaczały. Przymknijcie na czas poprawki oko na okna wokół podglądu kodu źródłowego, ale nie zamykajcie ich. Sprawdźcie po wykonanej pracy możliwości danego obszaru.

Może, zamiast najeżdżać kursorem na interesującą Was zmienną, wystarczyło spojrzeć w podgląd zmiennych w danym scope lub włączyć monitorowanie ich zmian w czasie działania programu? Jest wiele feature’ów do odkrycia i zawsze znajdzie się coś, czego nie wiedział nawet doświadczony programista.

Piękny kod

Rozwiązanie problemu czytelności wpływa pozytywnie nie tylko na szybkość czytania kodu, ale głównie na komfort pracy samego programisty. Przez większość czasu kod czytamy, nie piszemy. Zdefiniowanie reguł formatowania w projekcie gwarantuje zespołowi (i nie tylko, działa to również na znacznie szerszym obszarze) spójne wcięcia, długości linii, style komentarzy, czy ograniczenia bloków kodu. Ważne, żeby Wasz zespół ustalił reguły, które następnie zastosujecie w swoim IDE. Mogą one przybrać formę zmiany ustawień samego IDE lub specjalnych plików z regułami (dla przykładu - editorconfig).

Tak ustawione środowisko będzie Wam na swój sposób przypominać o niespójnym formatowaniu, gdy zagalopujecie się ze wcięciami lub ustawicie klamrę otwierającą metodę pod jej nazwą - jeśli oczywiście w regułach jest inaczej. Korzystanie z tego narzędzia polecam również w prywatnych projektach, gdzie jedynym zaangażowanym jesteście Wy. Pomoże wyrobić to dobry nawyk i przygotować Was do wydajniejszej pracy w zespole.

Checkouty? Pulle? Merge? Wystarcza mi Dropbox

Tak, opis pod tym punktem i aspekty, na jakie wpływa, mógłby spokojnie swoją długością przysłonić następne punkty i jeszcze kilkadziesiąt następnych artykułów. To, że spotkacie się z tym narzędziem w firmach IT, czy nawet na studiach związanych z tą branżą, jest praktycznie pewne. Niekorzystanie z kontroli wersji na pewnym etapie swojej pracy (im wcześniej, tym lepiej) niesie ze sobą ogromne ryzyko poniesienia straty naszej własności intelektualnej, a co za tym idzie - czasu i pieniędzy. Nawet pracując samemu, powinniście zadbać o odpowiedni backup swoich prac (i nie, nie mówimy o “zipach” z kodem źródłowym).

Kontrole wersji takie jak GIT, Mercurial czy SVN, umożliwiają nie tylko backup ostatniej wersji plików w wybranym repozytorium, ale przede wszystkim historię zmian i konkretne rewizje (wersje w określonym czasie). To daje nam możliwość szybszego dojścia do tego, która zmiana (a konkretniej kiedy, czyja, w którym pliku i linijce kodu została zaimplementowana) spowodowała problem, który może psuć „środowisko” u klienta.

Oczywiście, jeśli chodzi o historię zmian - nie o samo szukanie winnych tutaj chodzi. Możecie dzięki temu bezpośrednio skontaktować się z twórcą metod, których mimo prób nie udało Ci się zrozumieć. Co ważne, najpierw spróbuj samemu zrozumieć kod. Jeśli nie przyniesie to skutku, jak najbardziej polecam udać się do kogoś starszego stażem lub samego twórcy kodu, który sprawia Ci problem.

Zalety kontroli wersji

Dużym niedopowiedzeniem byłoby pominięcie innych zalet kontroli wersji:

  • możliwość błyskawicznego “cofnięcia w czasie” wybranego pliku lub nawet całego projektu
  • możliwość stworzenia brancha/gałęzi pod konkretną funkcjonalność/błąd - pozwala to zachować stabilność zarówno głównej developerskiej gałęzi - ze względu na nasze próby rozwiązania problemu/wprowadzenia nowej funkcji, jak i tej “własnej” - ze względu na ciągłość rozwoju głównej gałęzi, która może nie być jeszcze do końca przetestowana i generować problemy wpływające na to, co tworzymy.

Kluczowy jest też dobór kontroli wersji. Systemy scentralizowane, takie jak SVN lub CVS, przechowują dane repozytorium wyłącznie na serwerze zdalnym. Dlatego osobiście polecam GITa jako dobry przykład systemu rozproszonego, który przechowuje wszystkie metadane również lokalnie - u każdego, kto sobie repozytorium lokalnie „wycheckoutował”. Dla mniejszych, prywatnych projektów, równie dobrze sprawdzi się Mercurial, którego zawsze traktowałem jako bardziej przystępną i lżejszą wersję GITa.

Niepotrzebny i martwy kod

Niepotrzebny else po ifie? Kod, który nigdy się nie wykona, bo wcześniej postanowiliśmy zwrócić wynik metody bezwarunkowo? Czy może dwie te metody robiące to samo, bo skopiowaliśmy sobie działającą metodę z innej klasy? Jest wiele postaci nadmiarowości, czyli redundancji kodu, które nawet nie muszą (ale mogą) wpływać na funkcjonalność programu.

Bardziej zaawansowane IDE podpowie, a nawet zaproponuje automatyczne usunięcie niepotrzebnego kodu. Dobrym nawykiem jest zapamiętanie, co zostało podkreślone i przede wszystkim dlaczego. Na tym etapie zachęcam do korzystania z wbudowanych narzędzi ułatwiających życie programisty. Jednak pamiętajcie, że dociekliwość co do działania wszelkiego rodzaju oprogramowania, to bardzo pożądana cecha programisty. Przeanalizujcie po fakcie, jak zmienił się Wasz kod.

Jeśli w innej klasie widzicie prywatną metodę realizującą to, co akurat jest Wam potrzebne, nie kopiujcie jej. „Wynieście ją wyżej” i zmieńcie widoczność (jeśli język ją obsługuje). Nie przyczynicie się w ten sposób do zwiększenia rozmiaru paczki z modułem/projektem, niepotrzebnego powiększenia bazy kodu i dodatkowo udostępnicie narzędzie innym programistom, jeśli też będą potrzebować tej funkcjonalności.


Wyjątek

W kwestii redundancji występuje jednak wyjątek. Jest ona dopuszczalna, a nawet w niektórych przypadkach wskazana, w systemach przesyłających dane w postaci cyfrowej lub systemach o wysokiej odpowiedzialności. Najczęściej w urządzeniach medycznych, samolotach, oprogramowaniu zarządzającym majątkiem. Dla zminimalizowania ryzyka zawieszenia się lub wykonania złych obliczeń przez konkretny moduł, stosuje się celowe skopiowanie krytycznych metod lub całych modułów. Zanim nasz “sprytnie” napisany moduł wymieni nasz dobytek na kryptowaluty, chcemy mieć przecież pewność, że ta decyzja jest słuszna. Z takich względów nie musimy czuć się winni i możemy zaimplementować trio bliźniaczych modułów, które wykonują w istocie to samo, ale w przypadku niezgodności wyników - dwójka “przegłosuje” decyzję odstającego od nich modułu w celu minimalizacji ryzyka.

Więcej komentarzy, niż kodu

Dobry kod to samo-dokumentujący się kod.

Na pewno spotkaliście się z tym stwierdzeniem. A jeśli nie, to w którymś punkcie swojej kariery, wybierając się na szkolenie z jakości kodu lub dobrych praktyk wszelkiego rodzaju (do czego gorąco zachęcam), na pewno je usłyszycie. Zgadzam się z tym i uważam, że dokumentacja powinna znajdować się w zupełnie innym miejscu niż kod źródłowy.

Zostawmy zatem komentarze dokumentujące. Ale nie oznacza to wcale, że nie możesz ich pisać. Najczęściej jednak będzie to komentarz adnotowany, jako TO DO, przypominający o refaktoryzacji, rozszerzeniu metody lub komentarz wyjaśniający, dlaczego Wasz kod jest tak niezrozumiały/dziwny/obrzydliwy i dlaczego działa. W jednym i drugim przypadku, jako „początkujący”, pisałem zazwyczaj krótkie zwroty, skróty myślowe skierowane tak naprawdę tylko do mnie. Nieprzyzwyczajony do pracy w większym zespole, nawet nie wziąłem pod uwagę, że ktoś inny może refaktorować klasę, w którym ten komentarz się znajduje. Jeśli już dodajecie komentarz, spróbujcie zrozumiale i zwięźle wyjaśnić (lecz opisując problem wystarczająco, nigdy mniej), dlaczego ten kod działa i dlaczego tak wygląda. Jeśli inny programista będzie potrzebował tenże kod zmienić lub rozszerzyć, to bardzo pomoże w zrozumieniu tego, „co autor miał na myśli".

Unikajcie także wyjaśniania kluczowych słów języka lub operacji, które są jasne już na pierwszy „rzut oka”:

/ * Set the value of the salary to 15000 */
int salary = 15000;

Powyższy komentarz jest nadmiarowy i nie wnosi absolutnie nic. Jest to standardowa operacja, która powinna być jasna dla wszystkich programistów lub nawet osób zainteresowanych programowaniem. Uwierzcie w umiejętności swoich kolegów i koleżanki po fachu! :)

Jakie testy? Przecież testuje dział QA

Oczywiście, testuje i na pewno przetestuje większość przypadków (bądźmy dobrej myśli), ale nie możemy pominąć tu czynnika ludzkiego. Coś zostanie pominięte, czy to przez zapomnienie, czy ograniczenia techniczne. Czy przypadek graniczny jest testowalny przez testera manualnego? Czy UI pozwala na tego typu testy? Najbezpieczniej samemu pokryć swój kod testami, czasami jeszcze przed etapem jego tworzenia (Test Driven Development - Google it!). Zorientujcie się, jakie narzędzia są wykorzystywane w Waszym stacku technologicznym i spróbujcie napisać swój pierwszy test jednostkowy.

Może dotyczyć nawet najkrótszej, najprostszej metody i ustawiać tylko prawidłowe parametry do testów. To jest zawsze start w kierunku dobrze „otestowanego” kodu.

Dobrze jednak, żebyście zaraz po „teście idealnym” dopisali obok takie, które testują ścieżki niepowodzeń (oczekiwany błąd) i przypadki graniczne. Głównie skupcie się na danych, które mają szansę być wprowadzone, ale nie zapominajcie, że przyszli użytkownicy Waszego systemu niekoniecznie będą podążać wymarzoną przez Was ścieżką. Mogą zapomnieć wpisać wymaganą przez system wartość lub wpisać tekst w pole, które oczekuje numeru.

Są to banalne przykłady, ale dobrze ilustrujące to, że testy pomogą w trakcie ich implementacji znaleźć niedopatrzenia w kodzie. Czy powinniście pozwolić użytkownikowi wpisać numer telefonu w pole z nazwiskiem? Co, jeśli to zrobi? Czy taki błąd jest odpowiednio obsłużony? Jeśli nie, gratuluję. Właśnie pisząc testy, czyli przed oddaniem swojego kodu do weryfikacji, znaleźliście błąd.

Wykonując testy przed kompilacją swoich modułów, oszczędzacie nie tylko Wasz czas, ale także kolegów-testerów, do których kod, który testów automatycznych nie przechodzi, nie powinien nawet trafić. Napotykane przez Was problemy mogą wynikać i najczęściej wynikają z problemów, jakie mogłyby wyłapać testy automatyczne.

Oddajcie tę energochłonną pracę automatom, które oczywiście sami napisaliście. :)

Brak analizy statycznej

Dobrą praktyką do „pielęgnowania” jakości kodu i kolejną warstwą testów jeszcze przed jego uruchomieniem jest skorzystanie z reguł analizy statycznej. Sprawdza ona, czy w Waszym kodzie nie występują niebezpieczne konstrukcje powodujące przykładowo: wycieki pamięci, kody nieosiągalne czy błędy logiczne.

Reguły potrafią analizować przepływ metod i przedstawić ich złożoność za pomocą współczynnika zwanego złożonością cyklomatyczną, czyli wartości określających liczbę punktów decyzyjnych w metodzie (wliczając w to wywołanie metod wewnątrz metod). To daje obraz złożoności kodu i określenia jego niestabilności jeszcze na etapie implementacji.

Do zalet tego typu analizy na pewno należy jej czas trwania. Po odpowiedniej integracji z Waszym IDE analizuje kod już na etapie jego pisania (live analysis) i robi to szybko. Analiza statyczna może też służyć, podobnie, jak plik konfigurujący formatowanie kodu do ustandaryzowania i niejako wymuszenia pewnych zasad pisania kodu w Waszym zespole.

Pozwoli Wam również na zwiększenie wydajności i stabilności dzięki zdefiniowanym zasadom opartych na dobrych praktykach i uniknięciu typowych błędów przy implementacji. Z ostrzeżeń zwracanych przez to narzędzie również można wynieść naukę i tychże błędów nie popełnić w skryptach, które dopiero czekają w kolejce na implementację.

Pamiętajcie, że żaden automat nie jest idealny. Może się zdarzyć, że kod, który napiszecie, podświetli się. Warto wtedy poprosić o opinię starszego stażem programistę. Jeśli akurat takiego pod ręką nie macie, to spokojnie możecie o to spytać w kolejnym etapie analizy kodu.

Podejście do code review

Tak, Wasz kod powinien być sprawdzony i skrytykowany, jeśli sobie na to zasłużył – to nie jest nic złego! Ważne jest posiadanie mentora, który wskaże błędy logiczne. Zakładamy, że błędy składni zostały wykryte na etapie analizy statycznej i przez samo IDE, a przede wszystkim przez błędy kompilacji. Mentor zaproponuje rozwiązanie lepsze, czyli algorytm w prostszej, czytelniejszej lub po prostu optymalnej formie. Zdrowym i motywującym podejściem jest, by wskazane poprawki potraktować jako wyzwanie, nie „pstryczek w nos”.

Recenzujący ma dobre intencje i też zależy mu nie tylko na powodzeniu projektu, a przede wszystkim na tym, żeby kod, w którym też przecież pracuje, był czytelny i zgodny ze standardami, z którymi jego kod również musi być zgodny. Code review w żadnym wypadku nie służy do skarcenia za zły kod. Jego zadaniem jest konstruktywna krytyka, poprawki i ewentualnie kilkukrotne powtórzenie tych czynności. Służy również do utrwalenia wiedzy samego recenzującego. Na pewno przyjdzie taki moment, w którym ktoś przypnie zadanie recenzji do Was. Czytanie cudzego kodu i próba jego refaktoryzacji może tylko poszerzyć umiejętności i przy okazji pomóc innym.

Podsumowanie

Jak sami widzicie, początki prac nigdy nie należą do najprostszych. Na szczęście, dotyczy to każdej dziedziny życia. Ucząc się prowadzić samochód, jeździć na rowerze, czy programując, musicie się przygotować na błędy, które naturalnie będziecie popełniać. Ważne, żeby nie traktować ich w kategorii porażki czy niewiedzy, a lekcji, której efekty szybko zauważycie i docenicie. Pamiętajcie, że Nie myli się tylko ten, co nic nie robi. Dlatego programujcie, piszcie, testujcie, bo tylko w taki sposób zdobędziecie potrzebne doświadczenie.

<p>Loading...</p>