
Omówmy, dlaczego narzędzia CI i CI to zupełnie różne rzeczy.
Jaką bolączkę rozwiązuje CI, skąd pochodzi ten pomysł, jakie ostatnie potwierdzenia, że to działa, jak zrozumieć, że macie rzeczywiście praktykę, a nie tylko zainstalowanego Jenkinsa.
Pomysł na przedstawienie dotyczące Continuous Integration pojawił się jeszcze rok temu, kiedy chodziłem na rozmowy rekrutacyjne, szukając pracy. Rozmawiałem z 10-15 firmami, z których tylko jedna potrafiła zrozumiale odpowiedzieć, czym jest CI i w jaki sposób zrozumiała, że go nie mają. Pozostałe mówiły niezrozumiałe bzdury o Jenkinsie 🙂 No cóż, mamy Jenkinsa, on robi kompilacje, CI! W wystąpieniu postaram się wyjaśnić, czym w rzeczywistości jest Continuous Integration i dlaczego Jenkins oraz podobne narzędzia mają z tym bardzo mało wspólnego.

A więc, co zazwyczaj przychodzi na myśl przy słowie CI? Większości ludzi przychodzą na myśl Jenkins, GitLab CI, Travis itd.

Nawet jeśli poszukamy w Google, to znajdziemy te narzędzia.

Jeśli zapytasz znajomych, to zaraz po wymienieniu narzędzi opowiedzą ci, że CI to wtedy, gdy w Pull Request na commit odbywa się kompilacja i uruchamianie testów.

Continuous Integration to nie o narzędziach, nie o kompilacjach z testami w gałęzi! Continuous Integration to praktyka bardzo częstej integracji nowego kodu, a do jej stosowania zupełnie niekonieczne jest budowanie Jenkinsów, GitLabów itd.

Zanim rozwiążemy, jak wygląda pełnoprawne CI, najpierw zagłębmy się w kontekst ludzi, którzy to wymyślili, i poczujmy tę bolączkę, którą próbowali rozwiązać.

A rozwiązali bolączkę pracy zespołowej!

Zobaczmy na przykładach, z jakimi trudnościami spotykają się programiści przy pracy zespołowej. Mamy projekt, gałąź master w git i dwóch programistów.

I zaczęli pracować jak wszyscy od lat przywykli. Wzięli zadanie w JIRA, założyli feature branch, piszą kod.

Jeden skończył funkcję szybciej i połączył z masterem.

Drugiemu zajęło to więcej czasu, połączył później i napotkał konflikt. Teraz, zamiast pisać potrzebne funkcje biznesowe, programista marnuje czas i siły na rozwiązywanie konfliktów.

Im bardziej skomplikowane jest połączenie swojej funkcji z głównym repozytorium, tym więcej czasu na to poświęcamy. I to jeszcze pokazuję dość prosty przykład. To przykład, w którym jest tylko 2 programistów. A wyobraźcie sobie, jeśli 10, 15 lub 100 osób w firmie pisze w jednym repozytorium. Oszalejecie, próbując rozwiązać wszystkie te konflikty.

Jest trochę inny przypadek. Mamy główne repozytorium i kilku programistów, którzy coś robią.

Stworzyli po jednej gałęzi.

Jedna się zmergowała, wszystko dobrze, zadanie zakończone.

W międzyczasie drugi programista zrealizował swoje zadanie. Załóżmy, że oddał je do przeglądu. W wielu firmach istnieje praktyka przeglądów. Z jednej strony to praktyka – dobra i użyteczna, z drugiej strony w wielu przypadkach nas spowalnia. Nie będziemy się w to zagłębiać, ale oto świetny przykład, do czego może prowadzić krzywa historia z przeglądami. Oddałeś pull request do przeglądu. Programista nie ma już nic do roboty. Co zaczyna robić? Bierze się za inne zadania.

W tym czasie drugi programista jeszcze coś zrobił.

Pierwszy wykonał trzecie zadanie.

I po pewnym dłuższym czasie, jego przegląd został przetestowany i próbuje się zmergować. I co się dzieje? Napotyka ogromną liczbę konfliktów. Dlaczego? Ponieważ podczas gdy jego pull request wisiał na przeglądzie, w kodzie wiele się zmieniło.
Oprócz historii z konfliktami, jest kwestia komunikacji. Dopóki twoja gałąź wisi na przeglądzie, czekając na coś, podczas gdy długo pracujesz nad funkcją, przestajesz śledzić, co jeszcze zmienia się w bazie kodu twojego serwisu. Może to, co teraz próbujesz rozwiązać, zostało już rozwiązane wczoraj i możesz skorzystać z jakiejś metody. Ale tego nie zobaczysz, ponieważ zawsze pracujesz na przestarzałej gałęzi. A ta przestarzała gałąź zawsze prowadzi do tego, że będziesz musiał rozwiązywać konflikty połączeń.
Okazuje się, że jeśli pracujemy w zespole, tzn. nie jedna osoba grzebie w repozytorium, lecz 5-10 osób, to im dłużej nie dodajemy naszego kodu do głównego repozytorium, tym bardziej cierpimy z powodu tego, że ostatecznie coś trzeba zmergować. I im więcej mamy konfliktów oraz im starszą wersję używamy, tym więcej mamy problemów.

Wspólna praca nad czymś – to ból! Zawsze sobie przeszkadzamy.

Na ten problem zwrócono uwagę ponad 20 lat temu. Pierwsze wzmianki o praktyce Continuous Integration znalazłem w ekstremalnym programowaniu.
Ekstremalne programowanie to pierwszy framework agile. Strona powstała w 1996 roku. Pomysł polegał na stosowaniu różnych praktyk programowania, planowania i innych, aby rozwój był jak najbardziej elastyczny, abyśmy mogli szybciej reagować na zmiany oraz wymagania naszych klientów. 24 lata temu zaczęli się z tym borykać, że jeśli robi się coś zbyt długo w izolacji, to marnuje się na to więcej czasu, ponieważ pojawiają się konflikty.

Teraz przeanalizujemy wyrażenie „Continuous Integration” na poszczególne słowa. Jeśli przetłumaczymy dosłownie, wychodzi nieprzerwana integracja. Ale jak to nieprzerwana, to nie jest do końca jasne, ponieważ jest bardzo przerywana. Ale na ile jest to integration, też nie jest oczywiste.
Dlatego teraz przytaczam cytaty z ekstremalnego programowania. Rozbierzemy oba słowa osobno.
Integration — Jak już wspomniałem, dążymy do tego, aby każdy inżynier pracował z najnowszą wersją kodu, aby jak najczęściej dodawał swój kod do wspólnej gałęzi, aby to były małe gałęzie. Bo jeśli są duże, możemy utknąć przez tydzień z konfliktami podczas mergowania. Szczególnie, jeśli mamy długi cykl rozwoju typu waterfall, gdzie programista poszedł na miesiąc pisać jakąś ogromną funkcjonalność. A na etapie integracji zablokuje się na długo.
Integration – to moment, gdy bierzemy swoją gałąź i integrujemy ją z masterem, robimy merge. Jest również ostateczna wersja, gdy jesteśmy tzw. transbase developer, gdzie dążymy do tego, aby pisać bezpośrednio do mastera, bez żadnych dodatkowych gałęzi.
W skrócie, integration – to wziąć swój kod i dostarczyć go do mastera.

Co oznacza słowo „ciągły”, co to jest ciągłość? Praktyka zakłada, że programista stara się jak najszybciej integrować swój kod. To jest jego cel podczas wykonywania każdej zadania – sprawić, aby jego kod pojawił się w masterze jak najszybciej. W idealnym świecie programiści robiliby to co kilka godzin. Tzn. bierzesz małe zadanie, łączysz je z masterem. Wszystko jest w porządku. Dążysz do tego. I trzeba to robić nieprzerwanie. Gdy tylko coś zrobisz, od razu wrzucasz to do mastera.
A programista, który coś robi, ponosi odpowiedzialność za to, co zrobił, aby to działało i nic nie zepsuło. Tutaj zwykle pojawia się problem z testami. Chcemy uruchomić jakieś testy na naszym commitie, na naszym merge, aby upewnić się, że to działa. I tutaj właśnie może pomóc Jenkins.
Ale z historią: a może zmiany będą małe, a może zadania będą małe, a może zrobimy zadanie i od razu spróbujemy je włączyć do mastera – tutaj żadne Jenkins nie pomogą. Ponieważ Jenkins pomoże tylko w uruchamianiu testów.
Możesz obejść się bez nich. To wcale nie przeszkodzi. Ponieważ celem praktyki jest łączenie tak często, jak to możliwe, aby nie tracić ogromnych ilości czasu na konflikty w przyszłości.
Wyobraźmy sobie, że mamy 2020 rok, ale z jakiegoś powodu bez internetu. I pracujemy lokalnie. Nie mamy Jenkins. To nic złego. Wciąż możesz utworzyć lokalną gałąź. Napisz w niej jakiś kod. Zrób zadanie w 3-4 godziny. Przełącz się na master, zrób git pull, połącz swoją gałąź. Gotowe. Jeśli robisz to często – gratulacje, masz Continuous Integration!

Jakie są współczesne dowody na to, że warto na to poświęcać siły? Ponieważ w ogólnym zarysie to jest trudne. Jeśli spróbujesz tak pracować, zrozumiesz, że wymaga to planowania, będziesz musiał poświęcić więcej czasu na dekompozycję zadań. Ponieważ jeśli będziesz robić man…, nie będziesz mógł szybko się zmergować i w ten sposób wpadniesz w kłopoty. Nie będzie już praktyki.
I to będzie drogie. Nie da się pracować od jutra w ramach Continuous Integration. Wszyscy będziecie długo przyzwyczajać się do dekompozycji zadań, długo będziecie przyzwyczajać się do poprawiania praktyki przeglądów, jeśli takową posiadacie. Naszym celem jest, aby to wszystko zostało scalone jeszcze dziś. A jeśli przegląd zajmuje wam trzy dni, to macie problemy i Continuous Integration wam nie wychodzi.
Ale czy mamy jakieś aktualne dowody, które wskazują, że inwestowanie w tę praktykę ma sens?

Pierwsze, co przyszło mi do głowy, to State of DevOps. To badanie, które prowadzą od 7 lat. Obecnie robią to jako niezależna organizacja, ale pod Google.
Ich badanie z 2018 roku pokazało korelację między firmami, które starają się używać krótkotrwałych gałęzi, które integrują się szybko i często, a ich wyniki wydajności IT są znacznie lepsze.
Jakie to są wskaźniki? To 4 metryki, które zbierają od wszystkich firm w swoich ankietach: częstotliwość wdrożeń, czas wprowadzania zmian, czas na przywrócenie usługi, wskaźnik awaryjności zmian.
I po pierwsze, jest ta korelacja, wiemy, że firmy, które często się scalają, mają te metryki znacząco lepsze. Dzielą firmy na kilka kategorii: to firmy wolne, które działają powoli, medium performer, high performer oraz elita. Elita to Netflix, Amazon, które są super szybkie, wykonują wszystko szybko, pięknie i w wysokiej jakości.

Druga historia, która wydarzyła się zaledwie miesiąc temu. W Technology Radar pojawił się wspaniały artykuł o Gitflow. Gitflow różni się od wszystkich innych tym, że jego gałęzie żyją długo. Są gałęzie release, które żyją długo, oraz gałęzie funkcjonalne, które również długo trwają. Ta praktyka w Technology Radar została przeniesiona do HOLD. Dlaczego? Ponieważ ludzie borykają się z problemami integracyjnymi.
Jeśli twoja gałąź żyje bardzo długo, staje się problematyczna, traci świeżość, zaczynamy poświęcać więcej czasu na wprowadzenie w niej jakiejkolwiek zmiany.
Niedawno autor Gitflow stwierdził, że jeśli dążysz do ciągłej integracji, jeśli chcesz wdrażać zmiany jak najczęściej, to Gitflow jest złym pomysłem. Dodał w artykule, że jeżeli masz backend, gdzie możesz do tego dążyć, to Gitflow jest dla ciebie zbędne, ponieważ Gitflow cię spowolni, a także stworzy problemy z integracją.
To nie oznacza, że Gitflow jest złe i nie należy go używać. Jest on przeznaczony do innych przypadków. Na przykład, gdy musisz wspierać kilka wersji usługi, aplikacji, czyli tam, gdzie musisz oferować wsparcie przez dłuższy okres czasu.
Ale jeśli porozmawiasz z ludźmi, którzy wspierają takie usługi, usłyszysz wiele narzekań na to, że ta wersja to 3.2, która była 4 miesiące temu, a ten fix nie został w niej uwzględniony i teraz, aby go wprowadzić, musisz zrobić masę zmian. I znów utknęli, a teraz przez tydzień próbują wziąć i wpoić jakąś nową funkcjonalność.
Jak słusznie zauważył Aleksander Kowalew w czacie, korelacja nie jest równoznaczna z przyczynowością. Tak jest. Tzn. nie ma bezpośredniego związku, że jeśli masz ciągłą integrację, to wszystkie metryki będą doskonałe, nie. Ale istnieje pozytywna korelacja, że jeśli jedno, to z dużym prawdopodobieństwem drugie też. Nie ma pewności, ale przeważnie. To tylko korelacja.

Niby już coś robimy, niby już się łączymy, ale jak zrozumieć, że ciągła integracja faktycznie istnieje, że łączymy się wystarczająco często?
Jez Humble to autor Handbook, Accelerate, strony Continuous Delivery oraz książki „Continuous Delivery”. Proponuje taki test:
- Kod inżyniera trafia do mastera codziennie.
- Przy każdym commicie uruchamiacie testy jednostkowe.
- Budowa w masterze padła, została naprawiona w ciągu około 10 minut.
Proponuje używać takiego testu, aby upewnić się, że praktyka u was na pewno istnieje.
Ostatnie wydaje mi się nieco kontrowersyjne. Tzn. jeśli możesz naprawić to w 10 minut, to znaczy, że masz Continuous Integration, co brzmi trochę dziwnie, moim zdaniem, ale ma to sens. Dlaczego? Ponieważ, jeśli często scalasz, to znaczy, że twoje zmiany są małe. Jeśli mała zmiana spowodowała, że twoja wersja główna padła, będziesz w stanie szybko znaleźć przyczynę, ponieważ zmiana jest niewielka. Miałeś małe scalanie, w którym zmieniło się 20-30 linii. W związku z tym możesz szybko zrozumieć, w czym był problem, ponieważ zmiany są bardzo drobne, masz bardzo mały obszar poszukiwań problemu.
Nawet jeśli po wydaniu nasz produkcyjny system się rozsypuje, jeśli mamy praktykę Continuous Integration, dużo łatwiej jest nam działać, ponieważ zmiany są minimalne. Tak, wpłynie to na planowanie. Będzie to bolesne. A może najtrudniejszą częścią tej praktyki jest przyzwyczajenie się do rozdzielania zadań, tzn. jak sprawić, by wziąć coś i zrobić to w ciągu kilku godzin i przy tym przejść przez recenzję, jeśli ją masz. Recenzja to osobny ból.
Testy jednostkowe to tylko pomocnik, który pomaga ci zrozumieć, czy twoja integracja przebiegła pomyślnie, czy nic nie zostało uszkodzone. Moim zdaniem to także nie jest całkowicie obowiązkowy punkt, ponieważ sens praktyki nie polega na tym.
To krótko o Continuous Integration. To wszystko, co jest w tej praktyce. Jestem gotów na pytania.
Krótko jeszcze raz podsumuję:
- Continuous Integration to nie Jenkins, to nie Gitlab.
- To nie narzędzie, to praktyka o tym, że jak najczęściej scalimy nasz kod z główną gałęzią.
- Robimy to, aby uniknąć ogromnego bólu, który pojawia się przy scalaniu w przyszłości, tzn. odczuwamy mały ból teraz, aby nie odczuwać dużego w przyszłości. W tym wszystkim chodzi.
- Komunikacja odbywa się poprzez kod, ale rzadko to widzę, ale też do tego została stworzona.
Pytania
Co zrobić z nierozdzielonymi zadaniami?
Rozdzielić. Jaki jest problem? Możesz podać przykład, że jest zadanie i nie da się go rozdzielić?
Są takie zadania, które z zasady nie mogą być rozdzielone, na przykład te, które wymagają bardzo głębokiej ekspertyzy i które mogą być realizowane przez miesiąc, aż do uzyskania jakiegoś zadowalającego rezultatu.
Jeśli dobrze cię rozumiem, istnieje jakieś duże i skomplikowane zadanie, którego wynik będzie widoczny dopiero za miesiąc?
Tak, dokładnie. Tak, wynik można będzie ocenić nie wcześniej niż za miesiąc.
Dobrze. W sumie to nie jest problem. Dlaczego? Ponieważ w tym przypadku, gdy mówimy o gałęziach, nie mówimy o gałęzi z funkcją. Funkcje mogą być duże i skomplikowane. Mogą obejmować wiele komponentów. I możliwe, że nie możemy ich zrobić w jednej gałęzi w całości. To normalne. Musimy po prostu podzielić tę historię. Jeśli funkcja nie jest w pełni gotowa, to nie oznacza, że niektóre jej fragmenty kodu mogą być scalane. Na przykład dodałeś migrację, a wewnątrz funkcji są jakieś etapy. Na przykład masz etap - zrobić migrację, dodać nową metodę. Te rzeczy możesz już scalać codziennie.
Dobrze. Jaki w tym sens?
Jaki sens ma łączenie małych rzeczy każdego dnia?
Tak.
Jeśli coś się zepsuło, widzisz to od razu. Masz mały fragment, który coś zepsuł, łatwiej to naprawić. Sens polega na tym, że łatwiej jest teraz scalić mały fragment, niż dużą rzecz za kilka tygodni. A trzeci sens jest taki, że inni inżynierowie będą pracować z aktualną wersją kodu. Zobaczą, że dodano jakieś migracje, a tutaj pojawiła się jakaś metoda, którą także mogą chcieć użyć. Wszyscy będą wiedzieć, co się dzieje w twoim kodzie. To właśnie z tych trzech powodów praktyka jest wprowadzana.
Dziękuję, pytanie zamknięte!
(Oleg Soroka) Mogę dodać? Powiedziałeś wszystko dobrze, chcę tylko dodać jedno zdanie.
Dobrze.
W przypadku Continuous Integration kod jest scalany do wspólnej gałęzi nie wtedy, gdy funkcja jest całkowicie gotowa, ale wtedy, gdy przestaje psuć się build. I śmiało możesz commitować do mastera ile chcesz razy dziennie. Drugi aspekt - jeśli z jakiegoś powodu nie możesz podzielić miesięcznego zadania na zadania chociażby co trzy dni, nie mówiąc już o trzech godzinach, to znaczy, że masz ogromny problem. A to, że nie masz Continuous Integration - to najmniejszy z tych problemów. To oznacza, że masz problemy z architekturą, a praktyki inżynieryjne są na zerze. Ponieważ nawet jeśli to badania, w każdym przypadku musisz to sformułować w postaci hipotez lub cykli.
Mówiliśmy o czterech metrykach, które odróżniają udane firmy od tych, które zostają w tyle. Trzeba doprowadzić te cztery metryki do sukcesu. Jeśli średni czas realizacji zadania to miesiąc, to najpierw skupiłbym się na tej metryce. Zmniejszyłbym go najpierw do trzech dni. A potem zacząłbym myśleć o Continuous.
Czy dobrze cię rozumiem, że sądzisz, iż na razie nie ma sensu inwestować w praktyki inżynieryjne, jeśli jakiekolwiek zadanie trwa miesiąc?
Masz Continuous Integration. I jest tam taka możliwość, że w ciągu 10 minut albo naprawiasz błąd, albo go wycofujesz. Wyobraź sobie, że wprowadziłeś zmiany. I to nawet w przypadku continuous deployment, wprowadziłeś to na produkcję i dopiero potem zauważasz, że coś poszło nie tak. Musisz to wycofać, ale już zaszła migracja bazy danych. Masz już schemat bazy danych w nowej wersji, a więcej, że jeszcze jakiś backup przeszedł, jeszcze dane zostały tam zapisane.
I jaka jest twoja alternatywa? Jeśli cofasz kod, to on już nie może współpracować z tą zaktualizowaną bazą danych.
Baza rozwija się tylko do przodu, prawda.
Ludzie, którzy mają słabe praktyki inżynieryjne, prawdopodobnie też nie czytali grubej książki o ... Co robić z backupem? Jeśli odzyskujesz z backupu, to oznacza, że tracisz dane, które zostały zgromadzone w tym czasie. Na przykład, pracowałeś trzy godziny z nową wersją bazy danych, użytkownicy się zarejestrowali. Wracasz do starego backupu, ponieważ nowa wersja schemat nie działa, odpowiednio, tych użytkowników straciłeś. A oni są niezadowoleni, narzekają.
Aby opanować cały zakres praktyk wspierających Continuous Integration i Continuous Delivery, nie wystarczy nauczyć się po prostu … Po pierwsze, może ich być bardzo wiele, co będzie niepraktyczne. Ponadto istnieje wiele innych praktyk, takich jak naukowe. Istnieje taka praktyka, która była popularizowana przez GitHub. Chodzi o to, że jednocześnie działają zarówno stary, jak i nowy kod. To wtedy, gdy tworzysz niedokończoną funkcję, która może zwracać jakąś wartość: albo jako funkcja, albo jako Rest API. Wykonujesz zarówno nowy kod, jak i stary kod, a następnie porównujesz różnicę między nimi. Jeśli różnica istnieje, logujesz to zdarzenie. W ten sposób wiesz, że nowa funkcja jest gotowa do wdrożenia zamiast starej, jeśli w określonym czasie nie było różnicy między tymi dwoma.
Takich praktyk jest setki. Zasugerowałbym rozpocząć od transbase development. Nie jest to w 100% związane z Continuous Integration, ale praktyki są takie same, jedno bez drugiego źle funkcjonuje.
Czy podałeś transbase development jako przykład, gdzie można zobaczyć praktyki, czy sugerujesz ludziom, aby zaczęli używać transbase development?
Zobaczyć, ponieważ nie będą mogli tego używać. Aby je wykorzystać, trzeba przeczytać wiele rzeczy. Jeśli osoba zadaje pytanie: „Co zrobić z funkcją, która zajmuje miesiąc”, to oznacza, że nie czytała o transbase development. Nie polecałbym tego na razie. Sugerowałbym skoncentrować się wyłącznie na tym, jak prawidłowo architektonicznie dzielić duże zadania na mniejsze. W tym chodzi o dekompozycję.
Dekonstrukcja to jedno z narzędzi architekta. Najpierw przeprowadzamy analizę, potem dekompozycję, następnie syntezę, a potem integrację. W ten sposób wszystko się układa. Aby dotrzeć do Continuous Integration, należy najpierw przejść przez dekompozycję. Problemy w pierwszym etapie się pojawiają, a my już rozmawiamy o czwartym etapie, tzn. im częściej robisz integrację, tym lepiej. Robienie jej jeszcze jest za wcześnie, lepiej byłoby najpierw podzielić swój monolit.
Trzeba narysować kilka strzałek i kwadratów na jakimś schemacie. Nie możesz powiedzieć, że teraz pokażę architektoniczny schemat nowej aplikacji i pokazać jeden kwadracik, wewnątrz którego znajduje się zielony przycisk aplikacji. W każdym razie będzie więcej kwadratów i strzałek. Na każdym schemacie, który widziałem, było ich więcej niż jeden. A dekompozycja już na poziomie graficznej reprezentacji jest dokonywana. Dlatego kwadraty można robić niezależnie. Jeśli nie, to mam dużą uwagę do architekta.
Jest pytanie z czatu: „Czy jeśli przegląd jest obowiązkowy i trwa długo, gdzieś dzień i więcej?”.
Macie problemy z praktyką. Nie powinno być przeglądu przez dzień i więcej. To ta sama historia co w poprzednim pytaniu, tylko trochę łagodniejsza. Jeśli przegląd trwa dzień, to oznacza, że najprawdopodobniej dotyczy to jakiejś bardzo dużej zmiany. Trzeba go robić mniejszym. W transbase development, który Oleg polecił, jest taka historia, która nazywa się ciągłym przeglądem. Jej ideą jest to, że robimy celowo tak mały pull request, ponieważ dążymy do ciągłego merge’a po kawałku. I dlatego pull request zmienia jedną abstrakcję lub 10 linijek. Dzięki temu przegląd zajmuje nam kilka minut.
Jeśli przegląd zajmuje dzień i więcej, to znaczy, że coś jest nie tak. Po pierwsze, być może macie jakieś problemy z architekturą. Albo to duży kawałek kodu, na przykład 1 000 linii. Albo macie tak skomplikowaną architekturę, że człowiek nie może jej zrozumieć. To jest problem, ale również trzeba go rozwiązać. Może w ogóle przegląd nie jest konieczny. Nad tym też trzeba się zastanowić. Przegląd to ta rzecz, która was spowalnia. Ma swoje plusy ogólnie, ale trzeba zrozumieć, po co to robicie. Czy to dla was sposób na szybkie przekazywanie informacji, czy dla ustalenia jakichś standardów wewnętrznych, czy co? Po co wam to? Ponieważ przegląd trzeba robić albo bardzo szybko, albo w ogóle go anulować. To jak transbase development – historia bardzo piękna, ale tylko dla doświadczonych ludzi.
Jeśli chodzi o 4 metryki, to zalecałbym jednak je zrealizować, aby zrozumieć, do czego to prowadzi. Sprawdzić w liczbach, zobaczyć obrazek, jak bardzo jest źle.
(Dmitrij) Jestem gotów podjąć dyskusję na ten temat z tobą. Liczby i metryki - to wszystko jest świetne, praktyki - to świetne. Ale musimy zrozumieć - czy jest to potrzebne biznesowi. Są firmy, które nie potrzebują takiej szybkości zmian. Znam firmy, w których nie można wprowadzać zmian co 15 minut. I nie dlatego, że są jakieś złe. To taki cykl życia. A żeby robić funkcje branches, funkcje toggle, potrzebna jest głęboka wiedza.
To trudne. Jeśli chcesz poczytać więcej o funkcji toggle, to bardzo polecam. . Jest też wspaniały artykuł Martyna Faulera o funkcjach toggle: o różnych typach, cyklach życia itd. Funkcja toggle - to skomplikowane.
A jednak nie odpowiedziałeś na pytanie: „Czy Jenkins jest potrzebny, czy nie?”
Jenkins w rzeczywistości nie jest potrzebny w żadnym przypadku. Jeśli mówimy poważnie, narzędzia: Jenkins, Gitlab przynieść wygodę. Zobaczysz, czy kompilacja się udała, czy nie. Mogą ci pomóc, ale nie zapewnią ci praktyki. Mogą tylko dać znacznik – Ok, nie Ok. I to, jeśli piszesz jeszcze testy, bo jeśli nie masz testów, to to jest prawie bezsensowne. Dlatego jest potrzebny, ponieważ – to bardziej wygodne, ale w ogóle można bez niego żyć, nie stracisz zbyt wiele.
T. j. jeśli masz praktyki, to znaczy, że nie jest ci potrzebny?
Zgadza się. Polecam test Jez Humbles. Mam mieszane uczucia co do ostatniego punktu. Ale ogólnie, jeśli masz trzy rzeczy, ciągle się łączysz, uruchamiasz testy na commitach w masterze, szybko naprawiasz build w masterze, to być może, że nie potrzebujesz nic więcej.
Jak czekamy na pytania od uczestników, mam pytanie. Mówiliśmy teraz o kodzie produktowym. A czy używałeś tego do kodu infrastrukturalnego? To taki sam kod, ma te same zasady i ten sam cykl życia, czy są tam inne cykle życia i zasady? Zwykle, gdy wszyscy mówią o Integracji i Rozwoju Ciągłym, zapominają, że jest jeszcze kod infrastrukturalny. I ostatnio jest go coraz więcej. Czy należy tam wprowadzać wszystkie te zasady?
Nawet nie to, czy należy, to będzie świetne, ponieważ na pewno uprości to życie. Gdy tylko pracujemy z kodem, nie ze skryptami na bash, a mamy normalny kod.
Stop, stop, skrypt na bash – to też jest kod. Nie dotykaj mojej starej miłości.
Dobrze, nie będę stąpać po twoich wspomnieniach. Mam osobistą niechęć do basha. Psuje się brzydko i strasznie cały czas. I psuje się często w sposób nieprzewidywalny, więc go nie lubię. Ale dobrze, załóżmy, że masz kod w bashu. Może rzeczywiście się nie znam i są tam normalne frameworki do testowania. Po prostu nie jestem w temacie. I w takim razie mamy te same zalety.
Kiedy pracujemy z infrastrukturą jak z kodem, mamy te same problemy co programiści. Kilka miesięcy temu natknąłem się na sytuację, w której kolega przesłał mi pull request na 1000 linii basha. I utknąłeś na recenzji przez 4 godziny. Problemy są te same. To nadal kod. I nadal współpraca. Utknęliśmy z pull request i utknęliśmy z tym, że musimy rozwiązywać te same konflikty merge w tym samym bashu, na przykład.
Obecnie bardzo aktywnie przyglądam się całemu temu procesowi maksymalnie pięknym programowaniem infrastruktury. Wprowadziłem do infrastruktury Pulumi. To czystsze programowanie. Tam jest to jeszcze bardziej atrakcyjne, ponieważ mam wszystkie możliwości języka programowania, więc z tymi samymi ifami zrobiłem eleganckie przełączniki w powietrzu i wszystko działa dobrze. To znaczy, moja zmiana znajduje się już w gałęzi master. Już wszyscy to widzą. Inni inżynierowie są tego świadomi. Już miało to na coś wpływ. Ale w tym czasie włączyło się to nie dla całej infrastruktury. Włączyło się to na przykład dla moich testowych środowisk. Dlatego odpowiadając na twoje pytanie jeszcze raz, jest to potrzebne. Nam, jako inżynierom pracującym z kodem, znacznie ułatwia życie.
Czy są jeszcze jakieś pytania?
Mam pytanie. Chcę kontynuować dyskusję z Olegiem. Ogólnie myślę, że masz rację, że jeśli zadanie zajmuje ci miesiąc, to masz problem z architekturą, masz problem z analizą, dekompozycją, planowaniem itd. Ale mam takie wrażenie, że jeśli zaczniesz próbować żyć według continuous integration, to zaczniesz rozwiązywać bóle związane z planowaniem, ponieważ nie uciekniesz od tego.
(Oleg) Tak, to prawda. W zakresie nakładów praca ta jest porównywalna z każdą inną poważną praktyką zmieniającą kulturę. Najtrudniejsze w przezwyciężaniu są przyzwyczajenia, zwłaszcza te złe. A jeśli aby wdrożyć tę praktykę wymagana jest znacząca zmiana nawyków otoczenia: deweloperów, zarządu, menedżera produkcji, czekają na was niespodzianki.
Jakie mogą być niespodzianki? Przypuśćmy, że postanowiliście częściej przeprowadzać integracje. I w przypadku integracji zależą od siebie różne rzeczy, na przykład artefakty. A w waszej firmie może istnieć polityka, że każdy artefakt musi być w jakiś sposób uwzględniony w systemie archiwizacji artefaktów. I to zajmuje pewną ilość czasu. Osoba musi zaznaczyć, że jako menedżer wydania zatwierdziła ten artefakt do produkcji. Jeśli to zajmuje 5-10-15 minut, ale przeprowadzacie wydanie raz w tygodniu, to wydanie raz w tygodniu zajmowałoby pół godziny – to niewielki podatek.
Jeśli robisz Continuous Integration 10 razy dziennie, to 10 razy trzeba pomnożyć przez 30 minut. I to przekracza ilość czasu pracy tego menedżera wydania. Po prostu się męczy to robić. Istnieją stałe koszty dla różnych praktyk. I to wszystko.
I musisz albo anulować tę zasadę, aby nie zajmować się takimi sprawami, tzn. nie przypisujesz ręcznie poziomu zgodności czegoś z czymś. Całkowicie polegasz na jakimś zautomatyzowanym zestawie testów gotowości.
A jeśli musisz uzyskać od kogoś potwierdzenie, aby szef podpisał, i nie wchodzisz do produkcji bez tego, że Wania powiedział, że to dozwolone itd. – wszystkie te bzdury staną na drodze praktykom. Ponieważ jeśli są jakieś związane działania w postaci podatku, wszystko 100 razy się zwiększa. Dlatego zmiana nie zawsze jest przyjmowana z entuzjazmem. Ponieważ trudno jest zmieniać nawyki ludzi.
Kiedy człowiek wykonuje znaną pracę, robi to praktycznie bez zastanowienia. Obciążenie poznawcze jest równe zeru. Po prostu działa według ustalonych procedur, ma w głowie listę kontrolną, robił to tysiące razy. I gdy tylko przychodzisz i mówisz mu: „Zrezygnujmy z tej praktyki i od poniedziałku wdrożymy nową”, dla niego staje się to poważnym obciążeniem poznawczym. A jednak obciąża to wszystkich jednocześnie.
Dlatego najprościej, chociaż w rzeczywistości nie wszyscy mogą sobie na to pozwolić, ja zawsze postępuję dokładnie w ten sposób, to jest następujące. Kiedy rozpoczyna się nowy projekt, zwykle od razu wpychane są do niego wszystkie nieprzetestowane praktyki. Dopóki projekt jest młody, właściwie nie ryzykujemy. Produkcja jeszcze nie istnieje, nie ma czego rozwalić. Dlatego można to wykorzystać jako trening. Takie podejście działa. Ale nie wszystkie firmy mają możliwość często rozpoczynać takie projekty. Chociaż to też jest trochę dziwne, bo teraz trwa pełna transformacja cyfrowa, wszyscy powinni uruchamiać eksperymenty, aby dogonić konkurencję.
Tutaj natrafiasz na to, że najpierw musisz zrozumieć, co musisz zrobić. Świat nie jest idealny, produkcja też nie jest idealna.
Tak, te rzeczy są ze sobą powiązane.
Biznes też nie zawsze ma zrozumienie, że powinien iść dokładnie w tym kierunku.
Są sytuacje, w których jakiekolwiek zmiany są całkowicie niemożliwe. To sytuacja, gdy na zespół wywierane jest większe ciśnienie. Zespół jest już całkiem wypalony. Nie ma żadnego zapasu czasu na eksperymenty. Od rana do wieczora pracują nad funkcjami. A kierownictwu ciągle mało i mało tych funkcji. Wymagana jest coraz większa liczba. W takiej sytuacji jakiekolwiek zmiany są całkowicie niemożliwe. Zespołowi można tylko powiedzieć, że jutro zrobimy tak samo jak wczoraj, po prostu musimy zrobić trochę więcej funkcji. Żadne przejścia do jakichkolwiek praktyk w tym sensie nie są możliwe. To klasyczna sytuacja, w której nie ma czasu na ostrzenie topora, trzeba rąbać drzewa, więc rąbią tępy toporem. Tutaj nie ma prostych rad.
(Dmitrij) Przeczytam doprecyzowanie z czatu: "Ale potrzebne jest duże pokrycie testami na różnych poziomach. Ile czasu poświęca się na testy? To jakoś drogo, zajmuje dużo czasu."
(Oleg) To jest klasyczne nieporozumienie. Testów powinno być wystarczająco dużo, abyście sami mieli pewność. Continuous Integration to nie jest taka rzecz, gdzie najpierw przeprowadza się 100% testów, a dopiero potem zaczyna się wdrażać tę praktykę. Continuous Integration zmniejsza obciążenie poznawcze, ponieważ każda z widocznych zmian jest na tyle oczywista, że rozumiecie, czy coś się zepsuje, nawet bez testów. Możecie to szybko przetestować w myślach, ponieważ zmiany są małe. Nawet jeśli macie tylko testerów manualnych, im też jest łatwiej. Wypuściliście nową wersję i powiedzieliście: „Spójrz, nic się nie zepsuło?”. Oni sprawdzili i powiedzieli: „Nie, nic się nie zepsuło”. Ponieważ tester wie, na co zwrócić uwagę. Możecie mieć jeden commit związany z jednym fragmentem kodu. I to eksploduje w konkretne zachowanie.
Tutaj oczywiście lekko przesadziłeś.
(Dmitriy) Z tym się nie zgodzę. Istnieje praktyka – rozwój przez testowanie, która właśnie ratuje przed tym.
(Oleg) Jeszcze do tego nie doszedłem. Pierwszą iluzją jest to, że trzeba pisać dokładnie 100% testów lub w ogóle nie zajmować się Continuous Integration. To nieprawda. To są dwie równoległe praktyki. I one nie są od siebie bezpośrednio zależne. Wasze pokrycie testami powinno być optymalne. Optymalne oznacza, że sami jesteście pewni, że jakość mastera, w której pozostanie po commicie wasz master, pozwala wam z pewnością nacisnąć przycisk „Deploy” w piątek wieczorem, będąc pod wpływem alkoholu. Jak to osiągniecie? Dzięki przeglądowi, dzięki pokryciu, dzięki dobremu monitoringowi.
Dobry monitoring jest nierozróżnialny od testów. Jeśli uruchamiacie testy raz na pre-prod, to sprawdzają one wszystkie wasze scenariusze użytkownika tylko raz. A jeśli uruchamiacie je w nieskończonej pętli, to jest wasz rozbudowany system monitorowania, który testuje wszystko w nieskończoność – czy system padł, czy nie. W tym przypadku różnica polega tylko na jednorazowości lub wielokrotności. Bardzo dobry zestaw testów, który … jest uruchamiany bez końca, to monitorowanie. I prawidłowy monitoring powinien być taki.
I dlatego jak dokładnie osiągniecie ten stan, kiedy w piątek wieczorem wdrożycie nową wersję i wrócicie do domu, to już inna kwestia. Może po prostu jesteście odważnym szaleńcem.
Cofnijmy się nieco do Continuous Integration. Trochę odbiegliśmy w stronę innej skomplikowanej praktyki.
Druga iluzja to przekonanie, że MVP należy szybko wytwarzać, więc testy w ogóle nie są potrzebne. To nie do końca prawda. Gdy piszesz user story w MVP, możesz ją rozwijać albo na spontanicznie, tzn. usłyszałeś, że istnieje jakaś user story i od razu przystępujesz do kodowania, albo pracując wg TDD. Z praktyki wynika, że w przypadku TDD nie trwa to dłużej, tzn. testy to efekt uboczny. Praktyka TDD nie polega na testowaniu. Mimo że nazywa się to Test Driven Development, to w rzeczywistości chodzi bardziej o podejście architektoniczne. To metoda, jak pisać dokładnie to, co jest potrzebne, a nie pisać rzeczy niepotrzebne. Ta praktyka koncentruje się na następnej iteracji twojego rozwoju myśli w zakresie tworzenia architektury aplikacji.
Dlatego tak trudno pozbyć się tych iluzji. MVP i testy nie wykluczają się nawzajem. Wręcz przeciwnie, jeśli tworzysz MVP w praktyce TDD, zrobisz to lepiej i szybciej niż bez tej praktyki, na ślepo.
To bardzo nieoczywista i skomplikowana myśl. Kiedy słyszysz, że teraz będę pisał jeszcze testy i przy tym coś zrobię szybciej, brzmi to absolutnie absurdalnie.
(Dmitrij) Tutaj wielu, kiedy mówi o MVP, po prostu nie chce im się napisać czegokolwiek sensownego. To dwie różne rzeczy. Nie należy przekształcać MVP w coś złego, co nie działa.
Tak, masz rację.
A potem nagle MVP w prod.
Na zawsze.
A TDD brzmi bardzo dziwnie, gdy słyszysz, że piszesz testy i wydaje się, że wykonujesz więcej pracy. To brzmi dość dziwnie, ale w rzeczywistości sprawia, że idzie to szybciej i przyjemniej. Gdy piszesz test, już w głowie myślisz o tym, jaki kod i jak będzie wywoływany, a także jakie zachowanie od niego oczekujemy. Nie po prostu mówisz, że napisałeś jakąś funkcję, która coś robi. Najpierw myślisz, że ma takie i takie warunki, tak będzie wywoływana. Pokrywasz to testami i z tego rozumiesz, jak będą wyglądały interfejsy wewnątrz twojego kodu. To ma ogromny wpływ na architekturę. Twój kod automatycznie staje się bardziej modularny, ponieważ najpierw próbujesz zrozumieć, jak będziesz go testować, a dopiero potem go piszesz.
W moim przypadku z TDD było tak, że w pewnym momencie zatrudniłem mentora do Ruby, kiedy jeszcze byłem programistą Ruby. I on mówi: 'Zróbmy to w TDD'. Pomyślałem: 'O nie, teraz muszę pisać coś dodatkowo'. Uzgodniliśmy, że przez dwa tygodnie będę pisać cały działający kod w Pythonie w TDD. Po dwóch tygodniach zrozumiałem, że już nie chcę wracać. Próbując to wdrażać przez dwa tygodnie, zrozumiałem, jak bardzo ułatwiło mi to myślenie. Ale to nie jest oczywiste, dlatego wszystkim polecam, że jeśli macie wrażenie, że TDD to skomplikowane, długie i niepotrzebne, spróbujcie stosować to przez dwa tygodnie. Dwa były wystarczające dla mnie.
(Dmitrij) Możemy rozwinąć tę myśl z perspektywy eksploatacji infrastruktury. Zanim uruchomimy coś nowego, robimy monitorowanie, a potem uruchamiamy. W takim przypadku nasze monitorowanie staje się normalnym testowaniem. I jest rozwój poprzez monitorowanie. Ale prawie wszyscy mówią, że to trwa długo, nie chce mi się, zrobiłem tymczasowy szkic. Jeśli zrobiliśmy monitorowanie we właściwy sposób, rozumiemy stan systemu CI. A w systemie CI jest dużo monitorowania. Rozumiemy stan systemu, wiemy, co się w nim dzieje. I podczas rozwoju robimy system, aby osiągnąć pożądany stan.
Te praktyki są znane od dawna. Rozmawialiśmy o tym jakieś 4 lata temu. Ale przez 4 lata praktycznie nic się nie zmieniło.
Na tej nucie proponuję zakończyć oficjalną dyskusję.
Wideo (wstawione jako element multimedialny, ale z jakiegoś powodu nie działa):
Źródło: habr.com
