Problem & my contribution
A running community already has rules, accumulated data and familiar workflows. A full rewrite with one big switch makes every missing detail a user problem.
My contribution. I created KOM17 as my own product: the first version was a monolith, then I split it into modules. I own the community and economy logic, both architecture stages and the migration requirements: which workflows must survive, where module responsibilities sit and when the old execution path can be removed.
New handlers were introduced behind a compatible entry point. During migration, unclaimed updates fell back to the legacy bot. Parity tests pinned existing behavior, including its less obvious rules. Once the required paths were covered, the bridge was removed.
Outcome
On 26 May 2026, the cutover completed to a single FastAPI → aiogram 3 path. The bridge is no longer a runtime fallback. A regression test guards against restoring the old entry point; the monolith remains evidence for parity tests.
Engineering highlights
Migrate behavior, not just code
Economy and moderation rules are pinned before replacement. A cleaner implementation can otherwise silently change the product.
Finish the transition
A bridge is useful while rollback depends on it. After cutover, a regression test guards its removal so temporary architecture does not become permanent.
Migrations follow data ownership
Five SQLite databases have separate Alembic lineages. A wrapper explicitly selects each database.
Architecture
| Layer | Technology & purpose |
|---|---|
| Frontend | Telegram; public web pages and administration |
| Backend | Python, aiogram 3, FastAPI, dishka |
| Data | SQLAlchemy 2 async, five SQLite databases, Alembic |
| Infrastructure / AI | Docker / systemd; metrics, Sentry; text and speech providers |
Trade-offs & lessons
Incremental replacement needs more compatibility code and tests than starting over. In return, changes can be checked in smaller steps. Separate databases establish data ownership but complicate migrations and cross-database consistency.
What the implementation taught
The code documents an operational lesson: probing the database from liveness could turn a transient SQLite lock into a process restart. /healthz now checks liveness; /readyz checks database readiness. A temporarily unavailable dependency does not always mean the process should be killed.
What I would improve now
I would define the contracts between economy, activity and moderation earlier, and include redelivery and restart-recovery scenarios in the mandatory verification set from the start.
Operations & data
The webhook checks Telegram’s secret header before dispatch. /healthz, /readyz and /metrics provide separate signals for process health, database readiness and application behavior. Payment integrations have their own validation and replay accounting.
Data & migrations
users, economy, activity, moderation and message_stats have independent schema versions. scripts.alembic_run selects the migration lineage. A plain Alembic command without selecting the database does not replace migrating all five stores.
| Database | Responsibility |
|---|---|
| users | Users and profiles |
| economy | Balances and economy operations |
| activity | Community activity |
| moderation | Moderation actions |
| message_stats | Message statistics |
Limits
The public repository is a snapshot, not the complete development history. Local operation requires a separate bot and configuration. End-to-end exactly-once is not claimed for every handler; redelivery safety belongs to the individual operation.
Evidence & source
Verified 24 Sep 2026: 3 completed-cutover regression checks. These are targeted checks of the described decisions, not a full application audit.
Claims above are tied to public code and documentation. Evidence links point to the reviewed revision; CI status may change.
