Most Telegram bot tutorials end with the same thing: one giant main.py where handlers, keyboards, and logging are all tangled together. Works for a weekend project. Falls apart the moment you add your fifth command.
Here's the structure I use for every bot now (python-telegram-bot v20):
1. One file per concern. main.py only wires things up. Handlers live in their own module, keyboards in another. Your future self will thank you.
2. The skeleton is always the same:
from telegram import Update
from telegram.ext import Application, CommandHandler
async def start(update: Update, context):
await update.message.reply_text("Hello! I'm alive.")
def main():
app = Application.builder().token("YOUR_TOKEN").build()
app.add_handler(CommandHandler("start", start))
app.run_polling()
if __name__ == "__main__":
main()
Everything hangs off this. New command = new handler function + one add_handler line. Nothing else touches anything else.
3. Keyboards are data, not logic. Inline buttons and reply keyboards get their own builder functions that return markup — the handler just calls them. When Telegram changes button behavior (it happens), you fix it in one place.
4. Logging from day one. Not print statements — the logging module, configured once at startup. The first time your bot silently dies at 2am, you'll understand why.
5. Config out of code. Token in an environment variable, never hardcoded. This is the mistake every beginner tutorial teaches.
I got tired of rebuilding this scaffolding for every bot, so I packaged the whole structure — the wired-up main.py, requirements, a setup guide, plus add-on templates for inline buttons, reply keyboards, and logging — into a starter kit: https://shadowatrx.gumroad.com/l/dkyutc
It's the exact skeleton above, ready to run. Grab a token from @botfather, drop it in, and you're live in minutes.
What does your bot structure look like? Always curious how other people organize theirs.
Top comments (0)