La risposta predefinita a "dove salvo lo stato" è diventata un database gestito con fattura mensile. Per i tool per sviluppatori self-hosted è eccessivo: SQLite è transazionale, collaudato e già installato ovunque giri Python.
Un file è una feature
Un database che puoi copiare con cp, salvare con un cron job e ispezionare con un singolo binario cambia il modo in cui operi. Attiva la modalità WAL e ottieni lettori concorrenti con un writer — più che sufficiente per un tool single-node.
import sqlite3
conn = sqlite3.connect("tool.db")
conn.execute("PRAGMA journal_mode=WAL")
conn.execute("PRAGMA foreign_keys=ON")Migrazioni senza framework
Non ti serve Alembic per un database a file singolo. Un pragma user_version e una lista ordinata di funzioni di migrazione ti danno aggiornamenti di schema sicuri e solo in avanti, in trenta righe.
MIGRATIONS = [
"CREATE TABLE events (id TEXT PRIMARY KEY, ts INTEGER)",
"ALTER TABLE events ADD COLUMN payload TEXT",
]
def migrate(conn):
v = conn.execute("PRAGMA user_version").fetchone()[0]
for i, sql in enumerate(MIGRATIONS[v:], start=v):
conn.execute(sql)
conn.execute(f"PRAGMA user_version={i + 1}")Quando NON usare SQLite
Sii onesto sul limite: tanti writer su tante macchine è territorio di Postgres, non di SQLite. Ma per CLI, agent e servizi single-node — la stragrande maggioranza dei tool per sviluppatori — il modello embedded elimina un'intera categoria di failure operativi.
Ecco perché tutto il catalogo PyForge è costruito così: il tuo tool deve girare su un VPS da cinque dollari o su un laptop con la stessa affidabilità.