27 maj 2024 POIT #248: Software Craftsmanship: Błędy i kompromisy
Witam w dwieście czterdziestym ósmym odcinku podcastu „Porozmawiajmy o IT”. Tematem dzisiejszej rozmowy w serii podcastów o software craftsmanship są błędy i kompromisy w programowaniu.
Dziś moim gościem jest Łukasz Drynkowski, z którym mam przyjemność współtworzyć portal z ofertami pracy dla branży IT o nazwie SOLID.Jobs.
Główne myśli o błędach i kompromisach z tego odcinka to:
- konieczna jest świadomość, dlaczego robimy tak jak robimy a nie inaczej(to jest właśnie software craftsmenship),
- źródłem kompromisów są: zagadnienia techniczne, wymagania po stronie biznesu, zagadnienia związane z zarządzaniem,
- kompromisy i błędy z nich wypływające są stałą cechą wytwarzania oprogramowania.
Subskrypcja podcastu:
- zasubskrybuj w Apple Podcasts, Google Podcasts, Spreaker, Sticher, Spotify, przez RSS, lub Twoją ulubioną aplikację do podcastów na smartphonie (wyszukaj frazę „Porozmawiajmy o IT”)
- ściągnij odcinek w mp3
- poproszę Cię też o polubienie fanpage na Facebooku
Linki:
- Profil SOLID.Jobs na LinkedIn – https://www.linkedin.com/showcase/solid.jobs/
- SOLID.Jobs – https://solid.jobs/
Wsparcie na Patronite:
Wierzę, że dobro wraca i że zawsze znajdą się osoby w bliższym lub dalszym gronie, którym przydaje się to co robię i które zechcą mnie wesprzeć w misji poszerzania horyzontów ludzi z branży IT.
Patronite to tak platforma, na której możesz wspierać twórców internetowych w ich działalności. Mnie możesz wesprzeć kwotą już od 5 zł miesięcznie. Chciałbym oddelegować kilka rzeczy, które wykonuję przy każdym podcaście a zaoszczędzony czas wykorzystać na przygotowanie jeszcze lepszych treści dla Ciebie. Sam jestem patronem kilku twórców internetowych i widzę, że taka pomoc daje dużą satysfakcję obu stronom.
👉Mój profil znajdziesz pod adresem: patronite.pl/porozmawiajmyoit
Pozostańmy w kontakcie:
- 📧 Jeśli masz jakieś pytania lub komentarze, pisz do mnie śmiało na krzysztof@porozmawiajmyoit.pl
- 📩 Zapisz się na newsletter, aby nie przegapić kolejnych ciekawych odcinków
- 🎙 Subskrybuj podcast w lub
Muzyka użyta w podcaście: „Endless Inspiration” Alex Stoner (posłuchaj)
Transkrypcja podcastu
To jest 248. odcinek podcastu Porozmawiajmy o IT, w którym w cyklu rozmów z Łukaszem Drynkowskim z portalu z ogłoszeniami pracy IT SolidJobs, który mam przyjemność współtworzyć, dyskutujemy o software craftsmanship, czyli o rzemiośle programisty.
Zapraszamy do słuchania i komentowania. A teraz życzymy Ci miłego słuchania.
Odpalamy!
Cześć, Łukasz!
Cześć, Krzysztof!
Spotykamy się po raz ósmy w ramach specjalnej serii podcastów o software craftsmanship i dzisiaj będziemy mówić o temacie nieco odmiennym, bo wcześniej poruszaliśmy takie, mam wrażenie, nieco wyniosłe tematy. Mówiliśmy o etosie programisty, o tym, jakie są dobre praktyki w tym naszym rzemiośle. Mówiliśmy trochę o paradygmatach programowania, a nawet wzorcach projektowych, a dzisiaj spojrzymy sobie na błędy, spojrzymy sobie na kompromisy, które w codziennej pracy musimy podejmować, ale które też, mam wrażenie, są taką immanentną cechą programowania i po prostu nie da się bez nich tworzyć softu, więc brzmi ciekawie.
To jednocześnie będzie ostatni odcinek zamykający nam tę serię podcastów, więc też jesteśmy bardzo ciekawi: jeśli chcielibyście coś skomentować, dodać, dać nam jakiś feedback, to oczywiście do tego zapraszamy.
Łukasz, myślę, że trzeba sobie zacząć od tego, żeby powiedzieć, czym te trade-offy w programowaniu są, jaką funkcję, jaką rolę sprawują.
Trade-offy w programowaniu to jest taka sztuka wyboru między jedną pożądaną cechą a drugą pożądaną cechą i często jest tak, że nie jesteśmy w stanie zapewnić tych dwóch cech naraz albo w takim samym stopniu. Bo wiadomo, że to jest jakiś taki suwak, który przesuwamy albo w jedną stronę, albo w drugą. Nie jest to wbrew pozorom zero-jedynkowe. I takie typowe trade-offy to czy robimy coś w sposób jak najprostszy, taka wersja, żeby działało, czy może robimy trochę takiej inżynierii i staramy się zrobić jednak coś, co będzie miało jakieś przyszłościowe cechy i od razu robimy troszkę bardziej skomplikowany soft. To jest taki przykład, żeby tutaj pokazać, o czym będziemy dzisiaj rozmawiać.
Tak, chciałbym tylko jedną rzecz skomentować, że faktycznie to jest może takie istotne rozróżnienie tych kompromisów, trade-offów vs błędy, bo błędy się zdarzają albo popełnia się je gdzieś tam, mając je z tyłu głowy, że po prostu w którymś momencie trzeba będzie je naprawić, natomiast tak jak powiedziałeś, trade-offy to jest sztuka wyboru pomiędzy jednym a drugim i w zależności od kontekstu, od projektu, sytuacji, od tego, w którym momencie rozwoju projektu jesteśmy, to wybór tych dwóch nieraz skrajnych elementów może mieć znaczenie i to nie znaczy, że my robimy jakiś błąd albo jesteśmy czegoś nieświadomi, tylko np. decydujemy się zrobić coś prosto na początku, ponieważ zależy nam na tym, żeby szybko dostarczyć produkt, żeby szybko zweryfikować go na rynku.
Albo też wiemy, że poruszamy się w takiej domenie, która jest np. skomplikowana, jest regulowana i tej inżynierii na początku jest trochę więcej wymagane. Wiemy, że będzie się w danym określonym obszarze to rozwijało i też musimy na początku przynajmniej spróbować tam architekturę zaprojektować.
Wówczas decydujemy się może na troszkę dłuższy development, ale mimo wszystko na stworzenie czegoś bardziej skomplikowanego, więc jak gdyby dążę do tego, że w tych kompromisach nie ma nic złego. Myślę sobie, że tylko trzeba być świadomym, że decydujemy się właśnie na taki, a nie inny kompromis, że w związku z tym będziemy musieli być może z czegoś zrezygnować, że być może w pewnym momencie będziemy musieli zweryfikować te nasze założenia, ale mimo wszystko robimy to świadomie.
Tak, jeszcze ciekawa rzecz, którą tutaj podjąłeś – może ja trochę będę kontynuował wątek: to, jaką decyzję podejmujemy, to może zależeć od tego, w jakim po prostu jesteśmy momencie danego projektu. I tę samą decyzję podejmiemy zupełnie inaczej, będąc na samym początku, będąc gdzieś tam w połowie projektu, a na samym końcu po prostu. To zależy od różnych czynników też zewnętrznych, od różnych uwarunkowań, w których jesteśmy, i może na początku jesteśmy gotowi pójść na jakiś kompromis i po prostu coś zrobić szybciej, coś zrobić mniej dokładnie. A jeśli jest to już ten końcowy etap projektu, już jesteśmy blisko wydania danego oprogramowania, to podejmujemy zupełnie inne decyzje, inne priorytety mamy.
Dokładnie, myślę, że to jest istotne. Dobrze, to może porozmawiajmy właśnie o tych najczęściej występujących trade-offach, kompromisach, które musimy na swojej drodze programistycznej prędzej czy później podejmować. Tutaj przywołałeś ten przykład zrobienia czegoś prosto vs być może włożenia więcej czasu, wysiłku po to, żeby zbudować solidniejsze fundamenty.
Oczywiście możemy pójść w jakąś tam skrajność i wpaść w taką pułapkę over engineeringu, czyli robienia czegoś zbyt skomplikowanego, próbując przewidzieć różne ewentualności, które tak jak wiemy, rzadko kiedy dochodzą do skutku. I myślę sobie, że takim przykładem tutaj, niekoniecznie może z tej naszej domeny programistycznej, ale i z informatyki ogólnie jest multicloud, czyli próba stworzenia takiego rozwiązania, które będzie działało tak dosyć agnostycznie na różnych chmurach. Wtedy musimy tego wysiłku programistycznego, inżynieryjnego włożyć dużo więcej, a wiemy, że mało jest tak dużych, tak skomplikowanych projektów, które będą musiały albo będą chciały w pewnym momencie np. przełączyć się na inną chmurę, dajmy na to.
Drugi przykład, który możemy tutaj na pewno przywołać, to jest bardzo klasyczne skupienie się na różnych zasobach, które mamy w przypadku, dajmy na to, serwera. Mówię tutaj o pamięci vs procesor. Albo będziemy chcieli zawsze dane przetwarzać, wyliczać za każdym razem, kiedy użytkownik ich zażąda, wtedy wiemy, że są świeże, jak to się ładnie mówi: zawsze up-to-date.
I aktualne.
I aktualne, ale nie zawsze to jest najlepsze rozwiązanie, bo oczywiście wtedy poruszamy się w tym wąskim gardle procesora. Możemy oczywiście zastosować klasyczne podejście związane z cachem po to, żeby składować sobie te wstępnie wyliczone jakieś elementy naszego przetwarzania danych po to, żeby np. później skorzystać już z tych danych, a nie przetwarzać je po raz kolejny.
Oczywiście to też nie jest jak gdyby żaden, jak to się mówi ładnie, darmowy lunch, bo wiemy, że utrzymanie cashe’a, jego tej aktualności to też jest jak gdyby kolejny problem, tutaj nic nie przychodzi za darmo, ale decydując się na jedno albo drugie, w zależności od też potrzeby biznesowej, niektóre rzeczy musimy po prostu za każdym razem wyliczyć na nowo, bo takie są wymagania, a inne możemy sobie spokojnie założyć, że np. w ciągu pięciu minut będziemy je dostarczać z cache’a, a nie wyliczać.
Tak, tutaj podobne rozkminy mogą być typu, czy ten eventual consistency jest okej, czy bardziej chcemy np., żeby od razu użytkownik otrzymał te aktualne dane, czy np. zależy to od tego procesu biznesowego, co ktoś robi, czy właśnie teraz podejmie na podstawie tego wyniku decyzję użytkownik, czy jest to tylko taka informacja, nie wiem, o statusie np. zamówienia, i jeśli ten status się zmieni za jakiś czas na to, że jest w realizacji, to nic się nie stanie.
Tak że tutaj dochodzimy do kwestii tego, jak kompensować te sytuacje, kiedy coś jest nie do końca aktualne. Wtedy np. musimy włożyć energię swoją w to, żeby rozwiązać problem, jeśli coś zrobiliśmy nad wyraz. Takim przykładem tutaj jest Amazon. Nie wiem, czy to jeszcze jest dalej aktualne, ale z tego co pamiętam, to Amazon właśnie robił w ten sposób, że jak kupowałeś dany przedmiot, to dostawałeś informację, że okej, udało Ci się kupić, a jeśli potem w tych ich background serwisach się okazało, że jednak nie mają już na stanie danego produktu, no to dostawałeś maila z przeprosinami i z jakimś rabatem. I w ten sposób oni to rozwiązywali.
Tak, czyli nie zawsze da się tylko i wyłącznie w sposób techniczny problem rozwiązać, nieraz biznesowe podejście i taka kompensacja jak najbardziej też się sprawdza. I tak sobie ogólnie myślę, że te trade-offy właśnie pewnie można podzielić na takie dwie grupy, jedne bardzo techniczne, wynikające z jakichś limitów, chociażby to użycie bardziej pamięci vs bardziej procesora, a inne, które wynikają właśnie z tego biznesowego case’a, w ramach którego się poruszamy.
Mówiliśmy tutaj o takim podejściu, że na początku tworzymy coś prostego, po to, żeby zweryfikować to chociażby szybko na rynku, zobaczyć, czy są klienci na tę naszą usługę, czy produkt. I to jest z kolei właśnie taki przykład zastosowania, czy takiego rozróżnienia bardziej z gatunku wpływu biznesowego. I myślę sobie, że kolejnym przykładem właśnie takiego typu trade-offu jest szybkość dostarczenia.
To oczywiście może być powiązane z tą prostotą, że robimy sobie jakieś MVP i weryfikujemy na rynku, ale też szybkość, myślę sobie tutaj, która jest niestety kompromisem dokładności, tego craftsmanshipu, o którym tutaj rozmawiamy. Wiemy, że nie zawsze to, co tworzymy, to jest taki state of the art, ale mimo wszystko zależy nam albo są przesłanki właśnie biznesowe do tego, żeby jednak po prostu to gdzieś upublicznić i zweryfikować.
Tak, czyli tutaj masz taki trade-off szybko kontra dobrze, tak, ale to szybko to tutaj też możesz, masz trade-off w tym, bo szybko to może znaczyć właśnie, no to zróbmy mniej, ale to, co zrobimy, no to niech będzie tip-top, albo zróbmy, pójdźmy szeroko, dajmy tu dużo funkcji, ale takich, niedogotowanych, że gdzieś tam happy-puffy działają, ale gdzieś np. jakaś obsługa błędów jeszcze tutaj wymagać będzie dodatkowej pracy w przyszłości.
I jeśli jesteśmy przy szybkości, to myślę sobie, że rozwój języków programowania to jest takie nakładanie kolejnych warstw abstrakcji, prawda? Już dawno nie musimy się gdzieś tam babrać w asemblerze, żeby dodać jedną liczbę do drugiej. Ale wszyscy ci, którzy pracują w pewnych szczególnych domenach, wiedzą, że zejście właśnie do tego niskiego poziomu powoduje, że jesteśmy w stanie wycisnąć więcej z tej maszyny albo że jesteśmy w stanie szybciej pewne rzeczy przetwarzać, wykorzystując po prostu mniej zasobów, więc taki trade-off tego, że bawimy się zabawkami, tworzymy ten soft z użyciem zabawek takiego wysokiego poziomu, ma właśnie ten kompromis, że one nie będą maksymalnie szybkie, maksymalnie wydajne, ale z drugiej strony dla programistów daje to taką możliwość łatwiejszego, szybszego budowania tych rozwiązań.
Przykładem może być low-code, to są tego typu rozwiązania, że jesteśmy w stanie w miarę szybko zbudować rozwiązanie, które wydajnościowo nie jest może optymalne, nie zawsze nadaje się do tych wszystkich obliczeń, które faktycznie są wymagające, ale z drugiej strony pozwala dostarczyć dosyć szybko rozwiązanie, które może być już szybko dostępne, szybko pomagać w rozwiązywaniu jakiegoś problemu.
Tak, czyli np. tutaj też ten trade-off by się objawił w zakresie tego, jak tutaj bardzo chcemy mieć wpływ, czy też możliwość konfiguracji tej wynikowej funkcji, a jak dużo tutaj po prostu jesteśmy gotowi poświęcić w ramach tego, że po prostu dostaniemy coś out of the box i dostaniemy coś gotowego. To też mi się wiąże z takim tematem np. używania zewnętrznych bibliotek, czy też szerzej frameworków. Framework daje nam dużo, tak? Ale też z drugiej strony w dużej mierze nas ogranicza i też nam daje jakieś tutaj ramy, w których się musimy poruszać. I czasami jest tak, że więcej na tym stracimy w frameworku, bo będziemy musieli jakby płynąć pod prąd, próbować zawrócić rzekę kijem i finalnie niekoniecznie będzie to najlepsze rozwiązanie, jeśli chodzi o ilość włożonej przez nas pracy.
Natomiast nie jestem wrogiem w żadnym razie bibliotek. Ale trzeba zawsze tutaj wziąć to pod uwagę i gdzieś mieć z tyłu głowy, że rzeczywiście nie zawsze użycie biblioteki to będzie mniej pracy, a z drugiej strony właśnie może warto jest użyć tej biblioteki, żeby nie robić jakiejś rzeczy, które już ktoś wymyślił, już raz to koło zostało tutaj opracowane, że jednak jest okrągłe. To jest to.
Tak, dokładnie. W odcinku o Secure by Design też poruszyliśmy taki ciekawy trade-off, że jednocześnie chcemy zapewnić, aby to rozwiązanie było bezpieczne. Chcemy maksymalnie wstępnie okroić ilość możliwych opcji, żeby to bezpieczeństwo zapewnić, ale z drugiej strony chcielibyśmy, żeby mimo wszystko ktoś korzystał z tego naszego software’u i żeby przede wszystkim ten user experience był na wysokim poziomie.
Więc to też jest zawsze taka sztuka zbalansowania, czy chcemy się poruszać i działać w sposób bardzo bezpieczny z użyciem wielu mechanizmów zabezpieczających, które będą wymagały na przykład jakiejś akcji ze strony użytkownika, czy też chcemy być funkcjonalni i przydatni dla użytkownika.
To tutaj z bezpieczeństwem także łączy się drugi taki temat, czyli jakość, bo im wyższa jakość, to też tym wyższe bezpieczeństwo tego rozwiązania i myślę, że tutaj też trade-offy, które codziennie podejmujemy, to też są właśnie odnośnie do tej jakości tych testów, jak testujemy dane oprogramowanie, ile czasu ma nam zejść na samo testowanie, a ile na dodawanie nowych funkcji, dodatkowej funkcjonalności. I to też jest taki trade-off, który ten programista codziennie w swojej pracy podejmuje.
To, co tutaj się łączy z tym, to czytelność kodu a jego wydajność. Często jest tak, że chcielibyśmy fajnie coś wyinżynierować, użyję takiego słowa, ale np. ze względów wydajności lepiej jest coś zrobić trochę gorzej, ale nie wyliczać czegoś kilkukrotnie. Albo może da się zrobić to i czytelnie i wydajnie, ale to w ten sposób zajmie nam trzy dni zamiast jednego dnia, i niekoniecznie to jest akceptowalne z kolei pod względem kosztów.
To tutaj z bezpieczeństwem także łączy się drugi taki temat, czyli jakość, bo im wyższa jakość, to też tym wyższe bezpieczeństwo tego rozwiązania i myślę, że tutaj też trade-offy, które codziennie podejmujemy, to też są właśnie odnośnie do tej jakości tych testów, jak testujemy dane oprogramowanie, ile czasu ma nam zejść na samo testowanie, a ile na dodawanie nowych funkcji, dodatkowej funkcjonalności. I to też jest taki trade-off, który ten programista codziennie w swojej pracy podejmuje.
Powiedziałeś tutaj o tym testowaniu w kontekście zapewnienia jakości, czyli jak często, co chcemy testować. I myślę, że to też się wiąże z tym, jakiego typu w ogóle testy będziemy chcieli stosować. Czy będziemy z takim klasycznym podejściem stosowali przewagę unit testów i nieco bardziej zmniejszali tę objętość testów, idąc w kierunku tych właśnie integracyjnych, ale to też właśnie zależy od przypadku, od naszego projektu. Wiadomo, że im więcej będziemy mieli tych testów jednostkowych, tym może będziemy mieli w miarę pewność, że określone fragmenty kodu działają, ale niekoniecznie, czy cała funkcjonalność, którą chcemy dostarczyć, zrobi to, co będzie miała zrobić.
W drugą stronę, jeśli chcielibyśmy przeważyć tą ostatnią kategorię testów, to będziemy mieli oczywiście dużo wolniejsze testy, w związku z tym nasz cały CI pipeline będzie wolniejszy. Znowu ta sztuka wyważenia i rezygnacji na rzecz tego rozwiązania, które w naszym przypadku będzie odpowiednie.
Mówiliśmy tutaj sporo do tej pory o wytwarzaniu kodu, ale trzeba pamiętać, że trade-offy to nie tylko tworzenie, ale również utrzymanie tego, co już tam wyprodukowaliśmy. I tutaj oczywiście też musimy się decydować na to, czy będziemy chcieli szybko zajmować się np. długiem technicznym, czy być może nie jest on dla nas tak istotny, być może możemy go zaadresować dużo później, bo jesteśmy gdzieś, tak jak mówiliśmy na początku fazy istnienia naszego startupu i nie do końca nas to w tym momencie interesuje…
Tak, ale pytanie np., co to jest za aplikacja, czy ją piszemy właśnie dla klienta, czy może to jest jakiś tutaj nasz wewnętrzny tylko dla jakiegoś działu, coś, co ma coś pokazać, szybko wyliczyć, to są różne właśnie te czynniki zewnętrzne, które nam wpływają na to, jak finalnie ten kod będzie wyglądać, jak samo oprogramowanie będzie wyglądać, bo niekoniecznie musimy tutaj zatrudniać jakiegoś UX-eksperta, jeśli to będzie po prostu jakaś prosta apka dla naszego wewnętrznego działu QA. Może oni sobie poradzą tutaj, ale zrobimy to szybko i tanio.
Czyli mamy te trade-offy techniczne związane z oprogramowaniem, które tworzymy i na to oczywiście składa się ta domena, ta specyfika naszego projektu, faza rozwoju itd., to, czy stosujemy tego typu frameworki, biblioteki albo nie itd., ale trzeba pamiętać, że na całość kompromisów, które musimy podejmować związanych z oprogramowaniem wiążą się też takie trade-offy, powiedziałbym miękkie, związane bardziej z zarządzaniem grupą programistów.
Tak i tutaj pierwszym w ogóle trade-offem to jest jak ten zespół ma wyglądać, jakich ma mieć specjalistów, czy mają być tu dedykowani, dajmy na to, specjaliści od UX-a, ilu testerów, i też, jaki jest tutaj seniority tego zespołu, czy budujemy zespół składający się z pięciu super ekspertów seniorów, czy jednak tutaj jakiś balans wprowadzamy i jednak jakiś junior, mid, senior, to wszystko zależy od projektu, od tych zewnętrznych czynników, od budżetu.
Czas, koszt, wielkość zespołu, to, jaki będzie skład pod względem seniority, wszystko to też ma swoje plusy i minusy, w związku z tym będzie się odbijało na tym, w jaki sposób pracujemy na co dzień, w jaki sposób pewnie też wygląda ten soft końcowy, więc trzeba to jak najbardziej mieć z tyłu głowy.
Tak, i tutaj podejmując te codzienne trade-offy oczywiście możemy popełniać błędy i nawet więcej: pewnie te błędy są nie do uniknięcia. I może tutaj trochę porozmawiajmy o tym, jak te trade-offy tutaj mogą wpłynąć, jakie błędy można popełnić.
Tak, tak. Pierwsza rzecz to jest stosowanie zawsze tych nowych, lśniących zabawek, tych technologii, o których się dużo mówi. Wydaje się, że to jest rozwiązanie na wszelkie problemy. Niech pierwszy rzuci kamień ten, kto po pojechaniu na konferencję nie pomyślał sobie, że to jest właśnie to rozwiązanie, które musimy koniecznie u nas użyć, to nam rozwiąże wszelkiego typu problemy, spędzimy tylko kilka miesięcy i już będziemy tę nową architekturę mieli, i będzie pięknie i cudownie.
Tak, tak że nie powinniśmy tutaj pozwalać, żeby hype na nas wpłynął, tylko powinniśmy właśnie twardo przeanalizować wszystkie za i przeciw danej technologii czy danego narzędzia. I myślę, że podobna kwestia to np. wzorce projektowe, ostatnio o tym mówiliśmy, czyli żebyśmy też się zastanowili, nie używali tych wzorców tam, gdzie one nie mają sensu, tylko właśnie wybrali to, co ma sens, a czasami może trzeba w ogóle powiedzieć: nie no tutaj zróbmy to po swojemu, bo ten wzorzec ma tutaj jakieś zalety, ale w takiej długiej perspektywie to niekoniecznie da nam tyle zysku, co ta włożona praca tutaj się nie zwróci.
Dokładnie i to się też wiąże myślę z takim ogólnym over engineeringiem, czyli próbujemy na początku przewidzieć, w jakim kierunku ten projekt pod względem technicznym będzie się rozwijał, wyobrażamy sobie, że za chwilę to już będzie tak duża aplikacja, która będzie wymagała niesamowicie złożonej architektury, w związku z tym próbujemy nieco sobie ułatwić pracę nam z przyszłości przez zastosowanie właśnie zbyt skomplikowanych rozwiązań na początku, które trzeba pamiętać, należy później utrzymywać, należy dokumentować, należy komuś wyjaśniać.
A bardzo często niestety okazuje się, że oczywiście rynek nas tutaj zupełnie zaniesie w inne miejsca i te takie decyzje, które próbowaliśmy podjąć na początku, żeby się zabezpieczyć przed różnymi sytuacjami, tak naprawdę przed niczym nas nie zabezpieczą, bo te sytuacje się zwyczajnie nigdy nie wydarzą, a z tym kodem musimy żyć, musimy go utrzymywać, musimy go gdzieś tam rozwijać.
Tak, przypomina mi się teraz jeszcze taka jedna sytuacja, czyli dążenie do takiego dry za wszelką cenę, czyli żeby nie było żadnej duplikacji kodu w naszym code base’ie, nazwę to tak, a czasami duplikacja kodu nie jest zła, a czasami wręcz nawet jest pożądana, bo to, że kod wygląda w tym momencie podobnie w dwóch miejscach systemu, może być wynikiem przypadku. I się okaże, że dalsze wymagania spowodują, że tutaj będzie taki branching i jednak ten kod zacznie inaczej się zachowywać, a to, że on był w danym momencie czasu taki sam, to nie jest wynikiem tego, że jakby spełnia tę samą rolę, tylko przypadku, tak bym to nazwał.
Właśnie, jak chcemy skonstruować, łowić ten kod w jednym miejscu, ale żeby on jednocześnie przechodził nam przez wiele różnych możliwości, ścieżek i obsługiwał różne warianty, to albo będziemy mieli niesamowicie wymyślny wzorzec projektowy, który będziemy musieli tam zastosować, albo szybko okaże się, że debugowanie różnych case’ów jest niezwykle trudne, ponieważ zwyczajnie ten kod co prawda jest w jednym miejscu, ale jest na tyle skomplikowany, że ciężko się go analizuje.
Myślę sobie, że takim kolejnym błędem, którego nie bierzemy pod uwagę w sytuacji, kiedy tworzymy soft, to jest to, że mimo wszystko działa on na jakimś fizycznym urządzeniu, a komunikacja odbywa się po sieci. I tak przywykliśmy do tego, że traktujemy ten serwer, ten hardware, na którym to uruchamiamy, jako coś takiego niezawodnego, że tam komunikacja odbywa się prawie natychmiast, jednocześnie nie musimy się w ogóle o to przejmować. Ale w przypadku, kiedy musimy się komunikować po sieci, tutaj się już może wydarzyć wiele różnych rzeczy. Mogą być opóźnienia, możemy czegoś nie dostarczyć, coś może nie w tej kolejności być dostarczone itd.
Jeśli tych rzeczy nie uwzględnimy w naszym kodzie, zwłaszcza tutaj myślę oczywiście o rozwiązaniach rozproszonych, to może się okazać, że kończymy z takim stanem, który nie tylko nie oddaje rzeczywistości, ale może powodować jakieś wręcz straty biznesowe, więc te rzeczy trzeba mieć po prostu z tyłu głowy.
Co jeszcze? To jest częsty, wydaje mi się, błąd początkujących programistów, którzy nie biorą pod uwagę, że wejście do software’u, do metody, do funkcji to jest coś, co powinniśmy zawsze walidować, sprawdzać i po prostu nie polegać na tych danych, które od tego przysłowiowego użytkownika spływają. Z kilku powodów: bo nie jesteśmy w stanie przewidzieć wszelkich możliwości, nie jesteśmy w stanie wykluczyć, że ktoś będzie miał po prostu złe zamiary z tym związane, więc taka zasada ograniczonego zaufania, albo wręcz bardzo ograniczonego zaufania w stosunku do inputu wejściowego do naszego kodu powinna być, ponieważ jeśli tego zabraknie, to mamy szeroki wachlarz różnych błędów później do ogarnięcia.
Trochę mi tutaj brakuje właśnie tego trade-offu, z czego wynika ten błąd.
To ogólnie są błędy, o których teraz rozmawiamy. Część z nich oczywiście wynika z trade-offów, część nie. Takim pewnie najprostszym trade-offem jest to, że chcemy coś zrobić szybko. Chcemy coś dostarczyć szybko, coś zweryfikować. Zakładamy, że tylko ten happy-puff się wydarzy, i właśnie brakuje tutaj czasu, zasobów. Albo jest to świadoma decyzja, żeby tego nie robić, tak to zostaje, nigdy do tego nie wracamy, bo wiemy, jak życie wygląda, i potem nas gdzieś to nagle gryzie w nieoczekiwanym momencie.
No dobrze, Krzysztofie, myślę, że tutaj przedstawiliśmy typowe błędy albo jak to może wyglądać, jeśli coś podejmiemy tę decyzję po prostu niepoprawnie. Myślę, że powoli będziemy zdążać tutaj do podsumowania naszego odcinka.
To, co jest ważne, to jest konieczność, żebyśmy byli świadomi, dlaczego podejmujemy daną decyzję i żebyśmy byli świadomi, że ten trade-off tutaj jest, jakie są te uwarunkowania po prostu zewnętrzne, i też pamiętajmy, że tę samą decyzję można podjąć w inny sposób, w zależności od tego, na którym etapie projektu jesteśmy, jakie jeszcze mamy zapasy czasowe w projekcie, jaki mamy zespół, czy jesteśmy w stanie tutaj np. też część zadań delegować np. na seniorów, czy już jakby ci seniorzy mają tutaj tych zadań pod kokardkę i jednak musimy część zadań tutaj przekazać, nawet nie mówię, że od razu juniorom, ale po prostu osobom, które z daną funkcjonalnością nie miały wcześniej nic wspólnego. To też wiadomo, że jeszcze dojdzie ten czas właśnie na wdrożenie się w dany moduł. Tak że jest tutaj dużo różnych uwarunkowań. Powinniśmy być po prostu świadomi tego.
To, co jest ważne, to jest konieczność, żebyśmy byli świadomi, dlaczego podejmujemy daną decyzję i żebyśmy byli świadomi, że ten trade-off tutaj jest, jakie są te uwarunkowania po prostu zewnętrzne, i też pamiętajmy, że tę samą decyzję można podjąć w inny sposób, w zależności od tego, na którym etapie projektu jesteśmy, jakie jeszcze mamy zapasy czasowe w projekcie, jaki mamy zespół, czy jesteśmy w stanie tutaj np. też część zadań delegować np. na seniorów, czy już jakby ci seniorzy mają tutaj tych zadań pod kokardkę i jednak musimy część zadań tutaj przekazać, nawet nie mówię, że od razu juniorom, ale po prostu osobom, które z daną funkcjonalnością nie miały wcześniej nic wspólnego.
Tak, dorzuciłbym jeszcze dwa punkty. Po pierwsze to, że te trade-offy zawsze są w mniejszej lub większej skali. Nie jesteśmy w stanie stworzyć takiego oprogramowania, które byłoby wolne zupełnie od tych kompromisów, więc też nie biczujmy się z racji tego, że musimy te kompromisy stosować, to jest normalne. A druga rzecz, pewnie tutaj w trakcie naszej rozmowy wyszło nam, że mamy trzy takie kierunki czy takie obszary, które wpływają na te nasze kompromisy.
Pierwsze to są oczywiście rzeczy techniczne, które musimy brać pod uwagę. Drugie to są te, które spływają z biznesu, konieczność szybkiego dostarczenia, konieczność zweryfikowania bez dopieszczenia tego naszego dzieła. A trzecie to są właśnie te, które wynikają z konstrukcji zespołu, ogólnego zarządzania zespołem, więc różna jest natura, różne są źródła pochodzenia tych kompromisów, ale zawsze w projekcie będziemy mieli z jakimś typem tych kompromisów do czynienia.
Tak, i podsumowując, to wydaje mi się, że właśnie świadomość tych trade-offów i w ogóle umiejętność podejmowania tych decyzji to jest właśnie ten software craftmanship, o którym tutaj przez ostatnie tygodnie rozmawialiśmy. I z tą myślą zostawimy naszych słuchaczy. Tak że dziękujemy Wam bardzo za odsłuchanie tej serii i zapraszamy oczywiście ponownie.
Tak, zapraszamy do wcześniejszych odcinków, do wcześniejszej serii też o narzędziach programisty.
Łukasz, bardzo Ci dziękuję za tę rozmowę, za całą serię. Fajnie było się spotkać, porozmawiać, zderzyć nasze doświadczenia. Klasycznie odsyłamy wszystkich również do SolidJobs. Tam oczywiście możecie znaleźć dla siebie ciekawe oferty zawsze z widełkami wynagrodzeń. Tam możecie również wystawić ogłoszenia. Nie obiecujemy jeszcze, ale pewnie za jakiś czas wrócimy na antenę z kolejną ciekawą serią podcastów.
Tak, myślę, że na pewno tutaj coś fajnego wymyślimy. Tak że jesteśmy otwarci na Wasz feedback. Jeśli zostawicie jakiś komentarz, to będzie nam miło na pewno.
Dokładnie. Dzięki wielkie, do usłyszenia i cześć!
Dzięki bardzo, cześć!