A simple to-do list app, built in Python — one deliberate step at a time.
Everyone recommends building a to-do app to learn, so here we are. There's obviously no shortage of to-do apps out there already, but as part of my training as a software developer (Fachinformatikerin für Anwendungsentwicklung), I wanted a project that's genuinely mine to learn with. Python is my favorite language, so it was the natural choice.
A note on how this was built: I worked through this with Claude (Anthropic's AI) as a sparring partner — but strictly as a teacher, not a code generator. Claude was never allowed to hand me finished code. Instead, it explained concepts, pointed me to the right resources, and walked me step by step through my own bugs and design decisions. Every line here was written, understood, and debugged by me. Claude also helped write parts of this documentation (project texts, this README) — my time is better spent learning to code than wordsmithing English sentences.
This project is planned in 7 phases:
- ✅ Local to-do program — Task class, in-memory list
- ✅ Persist data — JSON file storage (database later)
- 🔲 Build a web API around it (FastAPI)
- 🔲 Multi-user & cloud (login, deployment)
- 🔲 Calendar integration (Google Calendar API, OAuth)
- 🔲 Reminders (scheduler, notifications)
- 🔲 Mobile (app accesses the web API)
A local, in-memory to-do program with a Task class and an interactive command-line menu. Supports creating, viewing, editing, completing, and deleting tasks, all identified by a unique ID rather than title (since titles can repeat, e.g. for recurring tasks).
Tasks can be marked as recurring with a fixed interval (in days). Completing a recurring task keeps the completed one in the list (marked Completed: True) and automatically creates the next occurrence with a new due date and a new ID — nothing gets overwritten or lost.
Diagrams documenting the logic and structure (flowchart + class diagram) live in docs/.
Design note — ID reuse: IDs are assigned as "highest existing ID + 1". If the task with the currently-highest ID gets deleted, the next new task will reuse that ID. Preventing this (e.g. with a strictly increasing counter) would add complexity for no real benefit at this stage — nothing in the current logic relies on IDs being globally unique forever. So for now: not a bug, a feature. Might revisit this once it actually matters (e.g. with a database in Phase 2).
Tasks are now persisted to data/tasks.json instead of only living in memory. The Task class gained to_dict() / from_dict() methods to convert objects to and from plain dictionaries (since json can't handle custom objects directly). A new storage.py module handles the actual file reading/writing, kept separate from crud.py so the storage mechanism can be swapped out later (e.g. for a real database) without touching the task logic.
The task list is loaded from the file on startup and saved again after every change (add, edit, delete, complete) — so nothing is lost between runs.
python src/main.pyFollow the on-screen menu to view, add, edit, complete, or delete tasks. Your tasks are automatically saved to data/tasks.json and reloaded the next time you start the program.
- Python (standard library only for now —
datetimefor due dates,json+pathlibfor storage)
MIT — see LICENSE.