2021-12-13 / Bartłomiej Kurek
Od czego zacząć? #1 (ogólnie)

Częstym pytaniem osób początkujących jest: "od czego zacząć naukę?". Nie ma jednej właściwej odpowiedzi, każdy z nas uczy się inaczej, ma inne priorytety i dążenia. Droga do celu prowadzi jednak przez praktykę, przez cały stos narzędzi, informacji, drobnych i większych projektów.
Skutecznie uczymy się przez obserwacje i naśladownictwo: "monkey see, monkey do". "Monkey do" to nasza praktyka, a "monkey see" to proces uświadamiania sobie, że coś można praktykować.

W tej serii artykułów postaram się zwrócić uwagę na sprawy, które - w mojej opinii, zgodnie z doświadczeniem - uważam za najbardziej pomocne w nauce, i które mogą przyspieszyć ten proces.
Serię zaczynam od ogólnikowych informacji i prostych tez, które rozwinę bardziej szczegółowo w dalszych etapach. Ten artykuł zawiera główne przemyślenia i figury retoryczne mające na celu pozostawienie czytelnika z pytaniami, a w kolejnych materiałach postaram się zawrzeć konkretne przykłady dostarczające możliwe odpowiedzi, podpowiedzi i sugestie rzucające więcej światła na różne zagadnienia, z którymi bez wątpienia - prędzej, czy później - każdy się zetknie.

System operacyjny.

Uważam, że jednym z kluczowych narzędzi, z którymi pracujemy na co dzień, jest sam system operacyjny. Oczywiście kierunków technologicznych jest wiele, nie każdy system spełnia takie, czy inne wymagania i cele. Programować można na każdym systemie, jednak - w mojej opinii - najwięcej styczności z programowaniem i ogólną wiedzą IT w najkrótszym czasie uzyskać można używając na co dzień systemu Unix/Linux.
Środowiska te pełne są narzędzi, idei i rozwiązań z bardzo długą historią poprzedzającą generację narzędzi i sposobów pracy uznawanych dzisiaj za "nowoczesne". Większość rozwiązań i standardów zostało wymyślonych, wytworzonych, przetestowanych i wdrożonych w ciągu wielu dekad rozwoju ekosystemu Unix.
Do programowania - oprócz wiedzy o językach programowania - potrzebujemy wiedzy o platformie (sprzęt, system, środowisko, interakcje), na której nasze programy mają działać. W dziejszym świecie wdrożenia komercyjne wykonujemy często na rozproszonych systemach, w chmurach danych, ogólnie na serwerach. Tam inne systemy niż Unix/Linux rzadko mają zastosowanie. Zatem - prędzej czy później - stykamy się z zagadnieniem serwerów i systemów serwerowych.
Wtedy pojawią się te "podstawowe" kwestie jak: bezpieczeństwo, zarządzanie użytkownikami, procesami, monitoring zasobów, konteneryzacja, skrypty, automatyzacja, usługi, itd. Słowem - to do czego narzędziem jest "programowanie".

Środowisko programistyczne.

Możemy zacząć od pracy w programie typu IDE, ale programy takie często ukrywają przed nami zagadnienia podstawowe, bez których ciężko o dedukcję i ekstrapolację. Założeniem IDE jest automatyzacja zsyntetyzowanej wiedzy podczas procesu tworzenia oprogramowania, a nie jej dostarczanie początkującemu. Znajomość podstawowych narzędzi możemy wykorzystać do budowy większego systemu. Bez znajomości podstaw nie będziemy w stanie sprawnie poruszać się wśród różnorakich zagadnień, nie wykorzystamy własnego potencjału kreatywnego, nie będziemy zdolni do ekstrapolacji wiedzy na kolejno napotykane problemy podobnej natury/klasy. IDE na pewno może pomóc, ale może też na początkowym etapie być swoistą dystrakcją.

Narzędzia systemowe.

Zachęcam do zapoznania się z prostymi programami konsolowymi i samą konsolą. Konsola to nie okienko, gdzie siermiężnie wpisujemy komendy. Nauka pracy z poziomu klawiatury otwiera cały świat możliwości i wiedzy.
Przykładowo: email to plik tekstowy, skrzynka w formacie mbox to wszystkie wiadomości - jedna za drugą - umieszczone w jednym pliku tekstowym, a skrzynka Maildir to pojedyncze pliki tekstowe wiadomości w katalogu. Możemy je przeglądać, przeszukiwać, kompresować, przenosić, archiwizować, indeksować, backupować najprostszymi komendami. Przy okazji napatrzymy się na nagłówki, domeny, adresy ip, atrybuty plików, standardy i protokoły, które składają się na wiedzę. Umiejętność wykorzystania tej wiedzy w celu automatyzacji procesów to programowanie. Tutaj wracam do sugestii doboru systemu operacyjnego - w systemach i usługach przedkładających interaktywność w okienkach nad pracę z rzeczami takimi, jakimi są, znacznie trudniej zdobyć wiedzę o naturze tych rzeczy.

Podstawowe narzędzia.

Warto poznać narzędzia podstawowe. Przykładowo:
- interpreter python to program konsolowy napisany w C, uruchamia się go w konsoli podając mu ścieżkę do pliku
- kompilator C to program konsolowy, uruchamia się go w konsoli podając mu ścieżkę do pliku
- baza danych sqlite, czy PostgreSQL, to programy/usługi "konsolowe". Kiedy je instalujemy, to otrzymujemy podstawowe programy konsolowe do obsługi tych baz. Skoro to jedyne programy, które są w domyślnej instalacji, to znaczy, że wszystko z ich poziomu musi się dać osiągnąć.
- serwer www to program usługi, który obsługujemy z poziomu konsoli, a konfiguracji dokonujemy w plikach konfiguracyjnych (tekstowych)
- zip to program, który wczytuje dane z pliku i wypisuje dane skompresowane do innego pliku. unzip to program, który robi to samo w drugą stronę

Szerokie spektrum możliwości i kreatywności daje umiejętność stosowania tych "prymitywnych" narzędzi. Na przykład w Linux to te wszystkie "zabawne": cat, ls, grep, find, wc, sort, i setki innych. Można je łączyć w potoki, budując swoje własne programy (abstrakcja, automatyzacja). Wiedza o tym co już jest, co zapewnia, co można, co jest możliwe, daje inicjatywę i pobudza kreatywność. Kreatywność to ten element który pozwala dedukować rozwiązania i ekstrapolować posiadane już umiejętności. To ten sposób myślenia: "mam coś, mam drugie coś, połączę to w jedno i będę miał coś nowego".

Nie pomijaj podstaw.

Ucząc się oczekujemy szybkich wyników. To w pełni zrozumiałe - brak wyników prowadzi do zniechęcenia. Jednak pomijanie - wydawałoby się "oczywistych" lub "trywialnych" kwestii - prowadzi do braków i zahamowania na późniejszym etapie. Brak podstaw prowadzi do braku możliwości dedukcyjnych. Jeśli np. nie wiemy jakie mamy typy danych, po co jest ich wiele i dlaczego są różne, to nie wiemy do czego się nadają, zatem nie wiemy jak napisać funkcję, która ma dane otrzymać i zwrócić, jakie klasy problemów napotkamy stosując taki czy inny typ danych, czy funkcja będzie wydajna, jak wiele operacji/algorytmów będziemy musieli zastosować/dostosować do osiągnięcia zamierzonego cel.

Zadawaj wnikliwe pytania, zastanawiaj się nad słowami.

Mówi się, że Wikipedia nie uczy. Z jednej strony - to prawda. Wikipedia ma charakter encyklopedii, nie podręcznika. Z dugiej strony, problemem często jest to, że nie rozumiemy słów, przez co nie rozumiemy definicji, a finalnie całość jawi się jako "bełkot" (często "po grecku"). Przykład: C3_linearization i cytat (algorytm stojący za dziedziczeniem w Python):

Basically, the idea behind C3 is that if you write down all of the ordering rules
imposed by inheritance relationships in a complex class hierarchy,
the algorithm will determine a monotonic ordering of the classes that satisfies all of them.
If such an ordering can not be determined, the algorithm will fail.

Algorytm ma w nazwie "linearization", czyli jest liniowy: "im dalej, tym dłużej", równomiernie, o stałym przyroście. 1 krok = metr, 2 kroki = 2 metry, 3 kroki = 3 metry. "Po drodze" w powyższej definicji mamy "ordering" (porządkowanie), hierarchię z relacjami, wyznaczenie uporządkowania tej hierarchii, oraz fragment: "the algorithm will determine a monotonic ordering of the classes". Co znaczy "monotonic ordering"?
Monotoniczny, czyli w jedną stronę, bez zawracania. Czas dla nas jest monotoniczny, starzejemy się, ale nie młodniejemy.
Czyli algorytm ma na celu wygenerowanie ze złożonej struktury (w stylu "drzewo katalogów") płaskiej listy, którą da się "przejść" element po elemencie (liniowość) w jedną stronę bez zawracania (monotoniczność).

Brak zrozumienia słów oraz zagadnień niższego poziomu uniemożliwiają - w bardzo podobny sposób - korzystanie z dokumentacji. Dokumentacja techniczna nie będzie nam najczęściej podawać rozwiązań, czy gotowego kodu/przykładu, a dokumentować będzie aspekty techniczne/budowę dokumentowanego rozwiązania. Dokumentacja to nie to samo co "how-to". W "how-to" mamy kroki: "zrób to, poźniej to, otrzymasz to". W dokumentacji znajdziemy często raczej formę: "mamy coś, to coś potrzebuje X lub Y; kiedy otrzyma X, zrobi A, kiedy otrzyma Y, zrobi B; X musi spełniać warunek ð, Y musi spełniać warunek ŋ; jeśli X nie spełnia warunku ð, to wystąpi błąd Z, a kiedy Y nie spełnia warunku ŋ, to nie można przewidzieć co się stanie, gdyż jest to zależne od środowiska, w którym to 'coś' uruchomisz;".

Programowanie i świat IT rządzą się abstrakcją. Z rzeczy małych budujemy większe. Dla efektywnej nauki warto próbować schodzić niżej w poziomie abstrakcji, ustawicznie zadając pytania: "jak?", "skąd?", "dlaczego?". Aż dojdziemy do potwierdzenia lub zaprzeczenia. Prawda, fałsz. 1, 0.

Obserwuj programy, których używasz.

Używamy komputerów i oprogramowania na co dzień. Z biegiem czasu rozwiązania stają się dla nas "oczywiste", a często przestajemy nawet wyobrażać sobie alternatywy. To co znamy staje się niejako zbiorem naszych oczekiwań. Często jednak nie zastanawiamy się jak dane rozwiązanie działa, jak jest zaimplementowane, jaka idea/koncepcja się pod nim kryje.

Przykładowo: w edytorze tekstu mamy funkcjonalnośc undo/redo. Gdzie są zapamiętywane te "wersje", po których możemy się poruszać wstecz i w przód? Jaki mechanizm za to odpowiada?
W przeglądarce internetowej mamy guziki wstecz/w przód. Jaki to mechanizm?
W systemie mogę zmieniać okna cyklicznie, w dwie strony (Alt+Tab, Alt+Shift+Tab) - jaki to mechanizm, jak byś go zrealizował?
Włączam wyszukiwarkę lotów i szukam najszybszego lotu z lotniska X do lotniska Y. Jak stworzyć program, który znajdzie najtańsze (niekoniecznie najszybsze) połączenie pomiędzy tymi lotniskami? Tak, mogą to być rożne linie lotnicze. Tak, z przesiadkami, ale maksymalnie 2 i pod warunkiem, że przy każdej przesiadce nie czekam dłużej niż 2h na danym lotnisku.

Analiza tego, co już mamy przed sobą, może usprawnić proces uświadamiania sobie, że to czego próbujemy się uczyć, co próbujemy zrozumieć w sposób "abstrakcyjny", jest przed nami jako gotowy już przykład w naszym własnym - wyżej wspomnianym - zbiorze umiejętności poznawczych i oczekiwań. Korzystajmy i dążmy do efektywniejszego korzystania z tego, co już wiemy i umiemy.

Spróbuj zrozumieć czym jest i jak działa komputer.

Przykłady:

  • Jak działa komputer - włączam zasilanie i co się dzieje?
  • Jeśli mam jeden procesor, to jak to się dzieje, że jednocześnie słucham muzyki, piszę kod, a program pocztowy pobiera wiadomości przez sieć?
  • Co się dzieje kiedy brakuje pamięci operacyjnej? Po co jest pamięć?
  • Jeśli mam plik o rozmiarze 20GB, a pamięci mam 8GB, to w jaki sposób program obsługuje taki plik?
  • Czym są "jedynki i zera zapisane na dysku"? Co to znaczy "zapisane"? Jak, gdzie?
  • Jakie znasz formy komunikacji z maszyną? Jak wydajesz maszynie instrukcje/polecenia?

Rozbijaj abstrakcje.

Masz przed sobą "chmurę" (cloud computing). Są tam setki tysięcy komputerów w jakichś klastrach. Na tych klastrach uruchomione są jakieś "kubernetes". W tych "kubernetes" są jakieś "kontenery Docker". Te kontenery to uruchomione "obrazy" systemów. Obrazy to archiwa zip zawierające katalogi i pliki systemów uruchomione jako odizolowane "programy". Programy to procesy. Procesami zarządza system operacyjny. Czyli w systemie operacyjnym jest mechanizm grupowania (i izolowania) uruchomionych programów (procesów) w hierarchii.

W systemie mamy hierarchię procesów, a w przypadku kontenerów - taką samą zagnieżdżoną hierarchię, która ma inne ograniczenia (wedle ustawień dla pierwszego procesu w tej zagnieżdżonej hierarchii).

Bez kontenerów:

proc/
├── 1
├── 2
├── 3
├── 4
└── 5

Z kontenerami.

proc/
├── 1
├── 2
├── 3
│   ├── 1_container
│   ├── 2
│   └── 3
├── 4
└── 5
    ├── 1_container
    ├── 2
    └── 3

Czysta abstrakcja istniejącego rozwiązania, oraz rekurencja. Struktury mogą być różne, natomiast cała idea polega na "samowirtualizacji" zasobów: procesów, zasobów sieciowych, pamięci, zasobów dyskowych, uprawnień użytkowników, itd. Wszystko zaczyna się w systemie operacyjnym, a "chmury" to ten końcowy abstrakt. Dalej są już "chmury chmur", "klastry klastrów".

Znajduj wytłumaczenia.

Jeśli nie umiesz czegoś wytłumaczyć pięciolatkowi, to znaczy, że tego nie rozumiesz.

Warto dążyć do źródła zagadnienia, umieć wytłumaczyć je sobie i innym. Niekoniecznie szczegółowo, ale prosto i zwięźle.
Język polski uchodzi za jeden z najtrudniejszych do opanowania, a jednak "byle dziecko" potrafi się go nauczyć, a z biegiem czasu poprawiać innych, przekazywać posiadaną wiedzę, a nawet rozwijać ten język.

Dowiedz się jak zbudowane są programy.

Napisałem kod (literki w edytorze), kliknąłem/wpisałem "uruchom" i program działa. Jak to się stało?
Napisałem kod, kliknąłem/wpisałem "kompiluj" i otrzymałem w wyniku plik wykonywalny. Czym jest plik wykonywalny, z czego się składa?
Napisałem kod w stylu print("hello world!\n") - ale nie napisałem funkcji print. Skąd ona pochodzi?

Ucz się dla wiedzy, nie do portfolio.

Prawdopodobnie nikt nie będzie przeglądał Twojego kodu na GitHubie skopiowanego z tutoriali, albo najpewniej rzuci jedynie okiem, by mieć pierwsze ogólne wrażenie. Jak przyjdziesz, to zostaną zadane pytania sprawdzające wiedzę i zdolność do dalszej i samodzielnej nauki, albo otrzymasz zadanie napisania podobnego programu samodzielnie, a przy tym wytłumaczenia co dokładnie robisz, dlaczego tak i po co.

Sięgnij do historii.

Można programować bez funkcji, po co ktoś wymyślił funkcje?
Można programować z użyciem zmiennych globalnych, po co powstały "struktury danych"?
Skoro mamy funkcje i struktury, po co ktoś wymyślił "klasy"?

Jak powstały języki programowania? Kiedy? Dlaczego jest tyle różnych?
Jak i po co powstały bazy danych?
Dlaczego w bazach danych używa się SQL, a nie np. C?

Zacznij od siebie: pisz programy, używaj programów.

Jeśli chcesz programować - to powodem jest najpewniej jedno z poniższych:
- interesuje Cię jak to działa i jak to się robi
- chcesz zarabiać pieniądze

Oba powody są dobre. Jednak pisząc programy realizujemy jakieś cele i założenia. Jakie są Twoje własne cele, przesłanki, potrzeby, dla których napisałbyś jakiś program? Jaki byłby to program, co miałby robić?
Jaki własny problem chciałbyś rozwiązać programowaniem? Jaki problem próbowałeś rozwiązać?

Jeśli chcesz poznać daną dziedzinę - używaj też we własnym zakresie narzędzi używanych w danej dziedzinie.
Jeśli chcesz być ekspertem w security, używaj narzędzi security.
Jeśli interesują Cię serwery - zacznij od swojego serwera.
Jeśli interesują Cię systemy mailingowe - spróbuj od samego dołu - od tego "czym jest wiadomość email".
Jeśli interesują Cię sieci internetowe - zbuduj sobie swoją infrastrukturę (choćby na maszynach wirtualnych).

Pisz programy.

Programowanie to tworzenie programów. Jeśli chcesz tworzyć programy, twórz programy. Nie ma innej drogi. Nikt też nie może tego procesu zrealizować za Ciebie. Pomóc, pokierować - tak. Ale praktyka i powtarzanie są po Twojej stronie.
Różnica pomiędzy mną a zawodowym kucharzem polega na ilości czasu, którą każdy z nas na tę dziedzinę przeznacza. Najpewniej oboje lubimy to co sobie przygotowujemy i jesteśmy zadowoleni z własnych efektów, jednak zawodowy kucharz robi to częściej, ma dłuższy staż, więcej doświadczenia i dla niego ma to wyższy priorytet niż dla mnie.

W skrócie: czas przeznaczony na praktykę w danej dziedzinie robi z nas ekspertów w tej dziedzinie.

Co dalej?

W kolejnych odsłonach tej serii podam parę podpowiedzi, sugestii i rozwiązań odnoszących się do powyższych ogólników.

System operacyjny