04 — Skrypty i bazy danych
Przykazania IV i V: Dry-run — Uruchomienie „na sucho” — skrypt pokazuje, co BY zrobił, ale niczego nie zmienia. Widzisz skutki przed wykonaniem; zmiana na danych idzie dopiero po świadomym potwierdzeniu. jest domyślny; backup to mechanizm Rollback — Cofnięcie zmiany do poprzedniego, działającego stanu — „Ctrl+Z” dla wdrożenia. Gdy nowa wersja psuje produkcję, rollback przywraca poprzednią w sekundy zamiast naprawiać w panice..
Skrypty — zasady
1. Tryb próbny jest domyślny
Każdy skrypt, który zmienia dane, działa domyślnie jako --dry-run i wypisuje plan:
co, ile wierszy, gdzie. Mutację włącza świadome --execute. To uratowało projekt referencyjny
niejeden raz — widzisz „4 OK, 0 pominiętych” zanim cokolwiek się zapisze.
python -m scripts.x.do_thing # dry-run: pokazuje plan
python -m scripts.x.do_thing --execute # zapisuje
2. Idempotencja
Skrypt odpalony dwa razy ma dać ten sam stan, nie podwojony. Migracja (bazy danych) — Kontrolowana zmiana układu bazy — dodanie kolumny, tabeli czy przeniesienie danych — krok po kroku. Jak remont według planu: przebudowa danych w ustalonej kolejności, żeby nic się nie „zawaliło”./loadery treści
pisz tak, by INSERT OR IGNORE / UPSERT — żeby ponowne uruchomienie było bezpieczne
(w projekcie referencyjnym loadery treści z zewnętrznej bazy są jawnie Idempotencja — Cecha operacji, którą można uruchomić wiele razy, a wynik jest zawsze taki sam — bez dublowania. Kluczowa przy skryptach i zdarzeniach: ponowne uruchomienie nie psuje danych. Jak włącznik „ON”. i to jest wymóg deployu).
3. Organizacja przez etapy pipeline’u
Skrypty grupuj wg fazy (setup/, normalize/, enrich/, validate/, images/,
dedup/, orchestration/). Zostaw shim-y kompatybilności, gdy przenosisz pliki.
Złóż je w komponowalne pipeline’y (post-scrape, full) — ale mutujące katalog
operacje trzymaj POZA automatycznym pipeline’em (dedup/merge odpalasz ręcznie:
dry-run → review → --execute).
4. Loguj, czego NIE zrobiłeś
Jeśli skrypt ucina zakres (top-N, sampling, pominięte wiersze) — wypisz to. Ciche ucięcie czyta się jako „pokryłem wszystko”, a nie pokryłeś.
5. Środowisko zapisz w dokumentacji
Interpreter, zmienne (PYTHONIOENCODING=utf-8 na Windows), skąd brać venv. Agent nie
zgaduje ścieżki do Pythona — czyta ją z CLAUDE.md.
Bazy danych i migracje
Migracje są forward-only
Brak down-migracji. Jeden numerowany plik = jeden krok naprzód. Rollback schematu = przywrócenie backupu, nie odwrotna migracja. Dlatego:
Backup PRZED każdą zmianą schematu/danych na prodzie
sqlite3 data/app.db ".backup backups/pre-deploy-$(date +%F_%H%M%S).db"
To nie ostrożność — to mechanizm cofania. Trzymaj retencję (np. 14 dni) i nazwij
snapshoty czytelnie (pre-deploy-*, pre-swap-*, replaced-prod-*).
Additive > destructive
ADD COLUMN jest backward-compatible (stary kod dalej działa). Nigdy nie DROP/RENAME
kolumny w tej samej migracji, której wciąż używa działający stary kod — rozbij zmianę
destrukcyjną na późniejszy deploy, gdy nikt już kolumny nie czyta.
Integralność po fakcie
Po każdej operacji na danych: PRAGMA integrity_check + PRAGMA foreign_key_check +
policz wiersze. Odróżnij szum zastany (osierocone wiersze staging obecne i przed, i po)
od regresji (nowe naruszenia, których wcześniej nie było). W projekcie referencyjnym scalona DB
miała mniej naruszeń niż lokalna — to był sygnał, że migracja posprzątała, nie zepsuła.
Model „aktywny wiersz” zamiast nadpisywania
Wzorzec z cen: zamiast UPDATE in-place, wygaszaj stary wiersz (expired_at) i wstawiaj
nowy; partial unique index pilnuje „jeden aktywny na klucz”. Historia zostaje, audyt jest
darmowy. Rozważ taki model wszędzie, gdzie historia ma wartość.
Snapshot do transferu rób przez .backup / VACUUM INTO, nie cp żywego pliku
Żywy SQLite — Najprostsza baza danych — cała mieszka w jednym pliku, bez osobnego serwera. Świetny default na start: zero konfiguracji, łatwy backup (kopiujesz plik). Przy wzroście migrujesz wyżej. z WAL — Write-Ahead Log: tryb bazy, w którym zmiany najpierw trafiają do dziennika, a potem do danych. Daje bezpieczeństwo (odtwarzalność po awarii) i lepszą równoczesność odczytu/zapisu. skopiowany cp bywa niespójny. .backup (online-safe) albo
VACUUM INTO dają spójną, zdefragmentowaną kopię. Nigdy nie SCP żywego .db.
Anty-wzorce
- 🚫 Skrypt mutujący bez
--dry-run. - 🚫 Deploy z migracją bez backupu „bo additive”.
- 🚫 DROP kolumny używanej przez działający kod.
- 🚫
cp app.dbprzy włączonym serwerze → niespójny snapshot. - 🚫 Wpięcie operacji mutującej katalog w automatyczny pipeline.
W praktyce
- Relacyjna baza → migracje forward-only + backup przed każdą.
- Pipeline przetwarzający dane wrażliwe (np. anonimizacja / destylacja wniosków) = osobny, idempotentny skrypt z dry-run; kontrakt: na wejściu dane surowe, na wyjściu dane bez PII, z testem, że re-identyfikacja jest niemożliwa. Trzymaj go POZA ścieżką user-facing. → 09, 11