What to store
- Identities: platform user and chat IDs (as strings; Telegram IDs can exceed 32-bit integers and Discord snowflakes exceed JavaScript's safe integer range).
- Preferences: language, time zone (as an IANA name), notification settings.
- Conversation state: the current step of a flow and the answers collected so far.
- Jobs: scheduled reminders, pending broadcasts, retries.
- Idempotency keys: processed update IDs or event IDs, so a redelivered webhook is not handled twice.
Store as little as possible. Not storing message content avoids a whole class of privacy problems.
Choosing storage
| Need | Good fit |
|---|---|
| Prototype, single process | In-memory map (lost on restart) or SQLite |
| Small to medium bot on one server | SQLite with WAL mode, or PostgreSQL |
| Several instances, relational data | PostgreSQL or MySQL |
| Sessions, rate limiting, locks, short TTL data | Redis or Valkey |
| Edge or serverless runtimes | The platform's key-value store, a serverless SQL service or Durable Objects style coordination |
Pick the boring option you know how to back up. Most bots never outgrow one PostgreSQL instance.
Session keys
Decide what a "session" belongs to:
- Per user for personal settings.
- Per chat for group features.
- Per user in chat for multi-step flows in groups, so two people filling a form at once do not overwrite each other.
Frameworks like grammY, python-telegram-bot and Telegraf ship session middleware with pluggable storage adapters.
Modeling flows
Store the flow as a small state machine: {flow: "signup", step: "email", data: {...}, expires_at}. Add an expiry so abandoned flows do not trap users, and always offer /cancel. Design flows visually with the flow designer.
Concurrency
Platforms can deliver several updates for one user at nearly the same time (double taps, retries). Protect read-modify-write sequences:
- Use database transactions or conditional updates (
UPDATE ... WHERE version = ?). - Process updates for the same chat sequentially (a per-chat queue or lock).
- Record processed event IDs with a unique constraint and skip duplicates.
Caching
Cache platform data you read often (chat titles, member roles) with a short TTL and refresh on relevant events. Never treat a cache as the source of truth for permissions on sensitive actions.
Operations
- Version schema changes with a migration tool and run them in CI/CD (CI/CD for bots).
- Back up regularly and test restores.
- Define retention: delete conversation state after it expires, and remove a user's data when they ask or block the bot if your policy says so. See legal, terms and privacy.
- Encrypt sensitive fields and keep database credentials out of code.