Przeglądarka repozytoriów, którą zrobiłem zeszłego lata, ma osiem commitów w pierwszym tygodniu. Cztery pierwsze nie dotykają ani jednej linii aplikacji: to init z mojego szablonu i trzy commity dokumentacji. Piąty to już cała aplikacja.
To nie była dyscyplina. To było to, że pisząc dokument, odkryłem, że mam zły model mentalny integracji, a naprawienie tego w pliku .md kosztowało dwadzieścia minut zamiast dwóch dni.
Błąd zaczynania od kodu w cudzej integracji
Kiedy logika jest twoja, czytanie kodu jest najlepszą dokumentacją, jaka istnieje. Kiedy zależy od cudzego API — nie: to, co musisz wiedzieć, nie jest ani w twoim kodzie, ani w ich. Jest w limitach, kwotach i przestarzałych parametrach.
W tej przeglądarce przez chwilę myliłem dwa połączenia z tym samym dostawcą. OAuth do logowania i token serwerowy do czytania API wyglądają tak samo, a nie są tym samym. Jedno służy do tego, żeby wszedł człowiek, drugie do tego, żeby czytał serwer. Padają inaczej. Rozpisanie tego w dokumencie na dwie sekcje sprawiło, że zobaczyłem, że przeglądarka publicznych repo potrzebuje tylko tego drugiego.
Gdybym zaczął od kodu, odkryłbym to przy trzecim 401.
Co dokładnie dokumentuję przed pierwszą linią
Nie dokumentuję projektu mojej aplikacji. Dokumentuję to, czego nie kontroluję. Konkretnie:
- Realne limity, zmierzone, nie zacytowane. Nie wystarczy skopiować "5000 żądań na godzinę" ze strony. Zapytałem o to endpoint kwoty: 60/h bez uwierzytelnienia, 5000/h z uwierzytelnieniem, 30/min w wyszukiwaniu. I zmierzyłem, że
304 Not Modifiednie zjada kwoty — 4972, 4972, 4972 przy warunkowych, i 4971 przy tym samym wywołaniu bezIf-None-Match. Ta powtarzająca się liczba jest centralnym argumentem dokumentu, a bez uruchomienia tego jej nie masz. - To, czego nie da się zautomatyzować. OAuth App nie tworzy się z CLI: nie ma endpointu do jej stworzenia ani do odczytania client secret, który pokazuje się raz w przeglądarce. To krok ręczny, a w dniu, w którym będziesz stawiał środowisko od nowa, już o nim zapomnisz.
- Sufity, które zaskakują na produkcji. 2000 utworzeń tokena na godzinę, co jest pułapem logowań, a nie żądań. I wymuszona ponowna autoryzacja, jeśli użytkownik wejdzie dziesięć razy w ciągu godziny. Żadne z tych dwóch nie pojawia się, kiedy czytasz szczęśliwą ścieżkę.
- Czego decyzja NIE rozwiązuje. Zanotowałem, że wymuszenie logowania nie usuwa potrzeby cache'u warunkowego, bo bucket użytkownika jest współdzielony z jego PAT-em i z innymi aplikacjami działającymi w jego imieniu. Jeśli tego nie napiszesz, decyzja daje ci fałszywe poczucie bezpieczeństwa.
Nic z tego nie podlega opinii. To fakty, które albo sprawdzasz, albo zmyślasz.
Cookbooki: przetestować każdy element osobno, zanim się go zintegruje
W usłudze AI do analizy dokumentów zrobiłem to samo inaczej. Zanim jakikolwiek element wszedł do usługi, każdy miał cookbook: skrypt, który uruchamia go samego, zapisuje jego wyjście do katalogu i rejestruje się w run_all_cookbooks. Parser PDF, chunker, embeddingi, detektor liczb, weryfikowalna ekstrakcja. Jeden na element.
Wartością nie jest test. Wartością jest to, że kiedy wyjście chunkera zmienia kształt, widać to w pliku tekstowym, a nie w błędzie trzy warstwy wyżej. Przy przejściu pipeline'u na asynchroniczny cookbooki zamieniły się w runnery z asyncio.run, a migracja szła element po elemencie, nie naraz.
Kosztem jest to, że trzeba je skasować, kiedy przestają mieć wartość. Miesiące później jest commit usuwający przestarzałe cookbooki. Cookbook, który nie odpowiada już swojemu elementowi, jest gorszy niż żaden.
Dokumentacja jako źródło prawdy do późniejszego audytu
W galerii konceptów UI napisałem najpierw przewodnik projektowy dla tego elementu — rozmiary, skalę odstępów, skalę szarości, który prymityw służy do czego — a potem zaudytowałem kod względem niego.
Ciekawe jest to, że audyt poprawił przewodnik, a nie tylko kod. Trzy rzeczy, których .md mówił za mało: że towarzyszący szary stopniuje się w pięciu krokach, a nie w dwóch; że skala odstępów ma cztery szczeble, a nie trzy, bo nazywanie jej trzystopniową zamieniało sześć uprawnionych użyć w odchylenie; i że trzy dialogi to w rzeczywistości ten sam dialog, z tym samym pudełkiem i tym samym nagłówkiem, a przez to, że tego nie napisano, poprawkę zastosowano tylko do jednego.
Bez wcześniejszego dokumentu te trzy znaleziska nie istnieją. Nie masz do czego porównywać i wszystko, co znajdujesz, jest "moim zdaniem".
Kiedy to jest stratą czasu
Kryterium, którego używam:
- Jeśli integracja jest twoja od początku do końca, nie dokumentuj wcześniej. Kod jest prawdą, a dokument się starzeje.
- Jeśli to prototyp, który wyrzucisz, też nie. Gra, którą napisałem w jeden dzień, nie ma nic spisanego i tak jest dobrze.
- Jeśli dokument powtórzy to, co mówi oficjalna dokumentacja, nie pisz go. Wnosi coś tylko wtedy, gdy zawiera twoje pomiary, twoje wersje i pułapki, w które ty wdepnąłeś.
I jest błąd, który drogo mnie kosztował: dokument napisany wcześniej niesie w sobie twoje założenia. W tej przeglądarce sekcja o hostingu opisywała platformę serverless, bo tak sobie przyjąłem, redagując zlecenie, a nie dlatego, że ktoś tak zdecydował. Poprawienie tego na długo żyjący kontener zmieniło cały wniosek: na serverless SDK agentów się nie mieścił; w workerze z piętnastominutowym limitem żądania — mieścił.
Dlatego kolejny krok nie jest opcjonalny. Jest commit, którego jedynym zadaniem jest zweryfikowanie dokumentu wobec żywych źródeł: uruchomić zapytanie, które pojawia się w przewodniku, wysłać nieprawidłową wersję API, żeby samo API wyliczyło prawidłowe, sprawdzić, że per_page=500 po cichu przycina się do 100, otypować snippet, który nie kompilował się w trybie ścisłym. Ten commit wykazał, że 18 z 20 zwróconych "issues" to w rzeczywistości pull requesty.
Niezweryfikowany dokument to założenie w ładnym formacie. Zweryfikowany dokument to jedyna część integracji, która nie skłamie ci za pół roku.
