Czasem w pracy developera pojawiają się zadania, z którymi nie potrafimy sobie poradzić od ręki. Nawet częściej niż czasem. Kiedy nabyta wiedza nie wystarcza, choć człowiek bardzo chciałby mieć całą wiedzę świata.
Niestety, nie da się zdobywać wiedzy z prędkością Neo z Matrixa. Nawet jeśli by się dało, to przecież wciąż potrzeba by było wybierać, w jaką wiedzę inwestować, a jaka wiedza może poczekać. A i tak może się okazać, że wylądujemy z technologią, której jeszcze nie znamy (filmowo: w helikopterze) i potrzebne będzie szybkie zdobycie jakiejś wiedzy, by miękko wylądować.
Takie sytuacje potrafią budzić odczucia bezsilności i bezradności. Bezsilności – gdyż człowiek chciałby szybo coś zrobić, a nie może – na drodze stoi brak wystarczającej wiedzy. Bezradność włącza się z kolei, gdy wewnętrzny krytyk mówi – ależ tę technologię powinnaś znać! A nie znam, ponieważ czas nie jest z gumy a wiedza nie chce wchodzić szybciej, niż wchodzi. Mimo to czasem, zderzając się ze ścianą niewiedzy, zamiast rozmasować czoło, dalej uparcie próbuję rozwalić ścianę głową.
Z czasem zauważyłam, że bezsilność i bezradność są normalne, a niewiedza – częstsza niż wiedza. Pozostanie przy tych stanach utrudnia za to poszukiwanie rozwiązań, które – w dobie dynamicznie rozwijających się technologii – jest bardzo istotne. A w wielu sytuacjach to właśnie właściwe szukanie rozwiązań jest istotniejsze, niż znajomość na wylot danej technologii. Coraz częściej – kluczowe.
Bezsilność (nie tylko) juniora
Kiedy zaczynałam pracę w IT i mogłam dumnie tytułować się juniorem, wydawało mi się, że poczucie bezsilności i bezradności jest właściwe tylko juniorom i wynika z braku wiedzy. W końcu to wiedza jest kluczem do wszelkich drzwi. A skoro jej brak jest problemem, jest na to sposób – należy wiedzę zdobywać w każdej wolnej od snu, pracy i ogarniania domu chwili.
Na tapet wjechały więc podcasty, blogi, newslettery, książki i – moje ulubione – wideokursy. Wtedy wydawało mi się, że po zrobieniu odpowiedniej liczby kursów, ukończeniu bootcampa, codziennym kodowaniu przez rok (i jeszcze może dostaniu się do jakiegoś programu mentoringowego), nie będę już nigdy walić głową w mur. Nigdy! Wystarczy, ze nauczę się w rok frontendu, potem w rok backendu, w kolejny rok cybersecurity, następnie pocisnę devops i LLMy, to dam radę. I będę jak Irena Kwiatkowska:
W końcu, ja kobieta pracująca jestem i żadnej pracy się nie boję! 😂
Z czasem okazało się, że skupiłam się tylko na jednym rodzaju wiedzy – znajomości technologii. Jest jednak jeszcze jeden, cenniejszy rodzaj – wiedza, jak rozwiązywać problemy. I ta wiedza jest o wiele cenniejsza. Ten, kto próbował nauczyć się wszystkiego na kursach wie, że ani kursy, ani lata doświadczeń nie pomogą, jeśli nie zaczniemy zadawać właściwych pytań. Lub nie opracujemy jakiegoś systemu, który nam pozwoli te właściwe pytania zadań.
Jak możecie się domyślać, kursoza nie przyniosła spodziewanych efektów, poza wtedy niespodziewanym, acz całkowicie zrozumiałym wzrostem frustracji. Potrzeba było czasu i trochę uderzania głową w mur, bym mogła przekonać się, że wieczne zdobywanie wiedzy dla samego jej zdobywania nie doprowadzi mnie do celu. A już na pewno nie rozwiąże magicznie problemów z pisaniem kodu. Tak samo, jak ich nie rozwiąże samo „klepanie” kodu. Nawet czysta praktyka, bez rozumnego podejścia do rozwiązywania problemów programistycznych nie będzie wystarczająca.
Zadawanie właściwych pytań
Co więc należy zrobić, by zadawać właściwe pytania? Bez pewnej dozy wiedzy się nie obejdzie, jednak często przydaje się ogólna wiedza z danego języka programowania czy rozwiązania (architektura, metodologia, biblioteka etc.). Potem przydaje się wiedza o projekcie, w którym dane narzędzia (język czy rozwiązanie) wykorzystujemy. To są fundamenty, które pozwalają budować dalej. Wiedząc, na czym stoimy, możemy zacząć praktykować i wykorzystywać wiedzę o tym, jak szukać rozwiązań.
I tu przez wiele lat przyjacielem programistów były strony typu Stack Overflow, na których albo można zadać pytanie, albo przeszukać bazę pytań i odpowiedzi, by znaleźć podobny problem i zainspirować się rozwiązaniem. Do tego przydaje się znajomość słów kluczowych, a ta przychodzi wraz ze zrozumieniem problemu. Jeśli więc odpowiemy sobie na pytania:
- co robi nasz kod,
- co chcemy, by robił,
- jaką drogą chcemy cel osiągnąć,
- jakie są na tej drodze przeszkody,
- i co musimy wiedzieć, by ją przebyć i dotrzeć do celu,
to możemy określić słowa kluczowe i zacząć szukać rozwiązania.
Z czasem nabywając wiedzę praktyczną łatwiej nam będzie formułować problem tak zwięźle, by móc łatwiej wyszukać lub stworzyć rozwiązanie. Jednak umiejętność wyszukiwania informacji to jeszcze nie jest coś, co nam może pomóc rozwiązać złożone problemy.
To jest moment, w którym bez umiejętności dzielenia problemu na mniejsze kawałki ciężko jest wymyślić rozwiązanie.
Czasem to kwestia pośpiechu, częściej nawyku – chcemy zrobić większy krok, niż jest możliwe. Chcemy szybciej schudnąć, szybciej załatwić sprawy na mieście, albo napisać szybciej kod – byle przed końcem sprintu. A często tak się nie da. Wiele czynności jest złożonych z mniejszych klocków, których – dopóki się nie zatrzymamy i nie przyjrzymy uważnie – nie zauważamy.
Odchudzanie wymaga planu, czasu na jego wykonanie, na dojazdy czy zakupy częściej niż zazwyczaj. Wypad na miasto – czasem uwzględnienia korków, problemów z parkowaniem, a i tak nie przewidzimy wypadków drogowych. Tak samo programowanie – czasem składa się z dnia, kiedy mamy masę spotkań i czujemy się wybici z rytmu, często jednak wymaga przetestowania, przemyślenia pewnych elementów, wymyślenia jak je połączyć i przynajmniej kilku testów po drodze. Czasem coś działa od razu, częściej wymaga rozbicia na mniejsze problemy i rozwiązania ich, zanim poskładamy te rozwiązania w całość prowadzącą do celu.
Sztuka zdobywania wiedzy i szukania rozwiązań
Kwestia przemyślenia problemu zanim się do niego usiądzie jest ważniejsza, niż może się wydawać. Jakiś czas temu Daniel Roziecki w swoim wpisie na LinkedIn przypomniał 4 punkty przydatne w rozwiązywaniu programistycznych problemów:
- Przemyśl problem
- Rozpisz pseudokod (na kartce papieru).
- Przeanalizuj swoją twórczość pod kątem tego, czy ma sens. Prześpij się z tym (Popraw – pewnie się okaże, że nie ma sensu).
- Zakoduj.
Jeśli od tego nie zaczniemy, możemy mieć na następnych etapach sporo problemów i z zadawaniem dobrych pytań, i z dzieleniem problemów na mniejsze (pseudo)kawałki.
Przyjmijmy jednak, że jesteśmy w punkcie czwartym. Co jeszcze można zrobić, by nie zaprzyjaźniać się z bezsilnością i bezradnością?
Od jakiegoś czasu coraz śmielej korzystam z ChataGPT czy z Gemini do programowania (i nie tylko). Moje drugie podejście do oswojenia się z AI w kontekście pracy czy nauki zakończyło się owocnym zaimplementowaniem rozwiązań AI w codzienności.
Moje pierwsze podejście zakończyło się porażką, ponieważ nie wiedziałam, w jaki sposób formułować prompty tak, by uzyskać pomocną odpowiedź. To z kolei wynikało z odruchowego, całościowego podejścia do problemu. Trzeba napisać wtyczkę – to piszę ChatowiGPT, co powinno w niej być i jak powinna działać. Trzeba napisać kwestionariusz – przedstawiam cały algorytm działania. Takie podejście pomijało dziesiątki detali, które składają się na działający kod, a które widać dopiero, gdy rozbije się problem na mniejsze kawałki.
W ten sposób piszę szybciej kod, ale też uczę się więcej przy pisaniu niż wcześniej. To nie oznacza, że nie mam wciąż wpadek, gdy piszę kod, przykładowo nieodpowiednio otworzonych znaczników PHP (<? zamiast <?php ) na koncie. I tak, nie zawsze AI pomoże w debugowaniu i czasem trzeba się trochę poobijać po ślepych uliczkach, by dojść do tego, co nie zadziałało i jak to naprawić.
Mimo wszystko – rozbicie problemu na mniejsze kawałki jest kluczowe, niezależnie od tego, czy korzysta się z AI czy nie. Choć niewątpliwie rozbicie na mniejsze kawałki pomaga pracować z AI, gdy wyczerpie się darmowe tokeny do lepszego modelu 😉
Mając taki mindset łatwiej jest zrezygnować z kolejnego przełomowego kursu, inspirującego newslettera czy wpisu na blogu czy jakiegoś tutorialu z YT obiecującego ostateczne nauczenie się danej technologii. Łatwiej z kolei przychodzi dobieranie środków do nauki tak, żeby rzeczywiście posiadały tę wiedzę, która jest nam niezbędna do pracy.
W krainie sprawczości
Czy to oznacza, że wystarczy wiedzieć, jak rozwiązywać problemy? Oczywiście, że nie. Jednak ta umiejętność pozwala lepiej gospodarować czasem i zasobami w poszukiwaniu rozwiązania. I nie prowadzi aż tak często w rewiry, gdzie bezsilność i bezradność mają się dobrze.
Co prawda jako człowiek, który uwielbia zdobywać wiedzę boleję, że nie mogę jej nabywać w tempie Neo. Zdobywam jednak wystarczająco dużo wiedzy, by rozwijać się jako programista i, potocznie mówiąc, „dowozić” projekty – a to jest ważniejsze od gromadzenia wiedzy.
Mój kod nie jest idealny. Jednak dopóki ten kod działa i przynosi pieniądze, i nie sprawia znaczących problemów, to jest wystarczająco dobry. Ok, nie zawsze jest odpowiednio rozlokowany w modułach i napisany bez wstydliwego kodu typu znacznik !important w CSS. Pewnie za kilka miesięcy sporo kodu będzie mogło nosić zaszczytne miano Shame Code. I tak, to potrafi generować pewien dług technologiczny, ale który projekt go nie ma? Dopóki jest to dług, który jest do udźwignięcia, nie wpływając na być albo nie być projektu – jest on akceptowalny.
Dzięki takiemu podejściu zamiast wiecznie czekać na swoją szansę mam do czynienia z żywym, projektowym kodem i zdobywam bezcenne doświadczenie, rozwijając „programistyczne myślenie”. Najważniejsze jednak jest to, że takie podejście pozwala mi budować moje poczucie kompetencji i obracać się w krainie, w której nie ma problemów nie do rozwiązania – są za to: wyzwania, na które jestem gotowa albo jeszcze nie jestem, ale mogę być. Albo takie, których się nie podejmuję, ponieważ nie leżą w zakresie moich kompetencji, co też jest jak najbardziej w porządku.