Capstone - Note-Taking App
Introduction: Building a Real-World CLI Application
Welcome to the capstone project for Chapter 27! Everything you've learned in Lessons 1-4 comes together here. You're going to build a complete, production-quality CLI note-taking application that integrates:
- Lesson 1: Console I/O with input validation (menu-driven interface)
- Lesson 2: Safe file operations (reading and writing notes)
- Lesson 3: Cross-platform paths (organizing notes in directories)
- Lesson 4: JSON data format (persisting structured note data)
This isn't a toy project—it's a real application that demonstrates professional-level coding practices. When you're done, you'll have a program that:
- Runs without crashing, even with invalid user input
- Persists data to disk in organized directories
- Handles a dozen notes (or dozens) with responsive performance
- Recovers gracefully from common errors (missing files, corrupted data)
- Organizes code clearly so others can read and extend it
Time Estimate: 90-120 minutes of focused development (plus extension ideas if you want to go deeper)
What You'll Build: A fully functional Note-Taking App with these features:
- Menu-driven interface - Display options, accept user choice, execute action, return to menu
- Create notes - Prompt for title and body, save with UUID and timestamp
- Read notes - List existing notes, display selected note
- Update notes - Modify title, body, or category
- Delete notes - Remove notes with confirmation
- Search notes - Find notes by keyword across title and body
- List notes - Show all notes organized by category
- Exit gracefully - Clean shutdown with goodbye message
Application Architecture Introduction
Before writing code, let's understand the design. A well-architected application separates concerns:
Components
Menu Loop (Lessons 1)
- Displays menu options
- Accepts user choice
- Routes to appropriate function
- Returns to menu after operation
CRUD Functions (Lessons 2-4)
get_all_notes()→ Load all notes from disksave_note()→ Write or update a note to disksearch_notes()→ Find notes by keyworddelete_note()→ Remove note file
Data Structure (Lesson 4)
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"title": "Python Study Notes",
"body": "Today learned about file I/O...",
"category": "learning",
"created": "2025-11-09T10:30:00",
"modified": "2025-11-09T14:45:00"
}
File Organization (Lesson 3)
notes/
├── learning/
│ ├── 550e8400....json
│ └── 6f47fe8e....json
├── work/
│ └── a1b2c3d4....json
└── personal/
└── x9y8z7w6....json
Why This Design?
- Separation of Concerns: Menu loop doesn't know about file operations; functions don't know about UI
- Scalability: File-per-note approach handles 10-50 notes without performance issues
- Maintainability: Clear functions mean future extensions are easy
- Testability: Each function can be tested independently
💬 AI Colearning Prompt
"I'm designing a note-taking app. Should I store all notes in one JSON file or one file per note? What are the tradeoffs?"
Expected Outcome: You'll understand data persistence patterns and trade-offs before writing code. AI can help you think through design before implementation.
Setting Up Project Structure
Every good application starts with organization. Let's initialize the directory structure and default categories.
Creating the Notes Directory
First, we ensure the notes/ directory exists with default categories for organization:
Loading Python environment...
Output:
✅ Note-taking app initialized!
Data directory: /home/user/notes
Why this matters:
- Uses pathlib (Lesson 3) to create cross-platform paths
- Creates directories only if they don't exist (idempotent)
- Resolves absolute path to show user exactly where data is stored
🎓 Expert Insight
In AI-native development, you don't debug path errors manually—you design paths clearly at startup. Your job: specify the directory structure. AI can help you verify it's correct with
Path.resolve().
Code Example 5.1: Project Structure and Security
Before we write the full app, let's look at how to initialize the project safely with security validation:
Loading Python environment...
Specification Reference: Path traversal prevention using Path.resolve() + is_relative_to() from Chapter 27, Lesson 3
Prompt Used: "Show me how to safely construct file paths that prevent directory traversal attacks using pathlib"
Validation Steps:
- ✅ Test with normal path:
get_note_path("learning", uuid_string)→ returns valid path in notes/learning/ - ✅ Test with traversal attempt:
get_note_path("../../../etc/passwd", "id")→ raises ValueError - ✅ Verify resolved path is canonical:
resolved_path.is_relative_to(BASE_DIR.resolve())→ True for legitimate paths
Core CRUD Functions
Now let's implement the functions that handle data persistence. These are the workhorses of the application—they orchestrate file I/O, error handling, and JSON operations.
Code Example 5.2: Complete CRUD Functions
Loading Python environment...
Specification Reference: Lessons 2, 3, 4 - file I/O, pathlib directory creation, JSON serialization
Prompts Used:
- "Write a function that loads all JSON files from nested directories using glob()"
- "Implement save_note that creates directories if needed and writes with UTF-8 encoding"
- "Write a search function that finds notes by keyword in title or body"
Validation Steps:
- ✅ Load 3+ notes from different categories →
get_all_notes()returns all - ✅ Save new note → file created in correct category directory with proper JSON formatting
- ✅ Search finds matches →
search_notes("python")returns notes with "python" in title/body - ✅ Delete removes file →
delete_note(id)removes file and returns True - ✅ Error handling → corrupted JSON shows warning but doesn't crash
🚀 CoLearning Challenge
Ask your AI Co-Teacher:
"My app is slow when I have 1000 notes—every operation loads all notes from disk. How would you optimize this? What data structure would help? When would you use in-memory caching?"
Expected Outcome: You'll understand performance trade-offs and scaling patterns beyond this capstone's 10-50 note scope.
Menu Loop Implementation
The menu loop is where user interaction happens. It displays options, accepts input with validation, routes to appropriate functions, and returns to menu after each operation.
Code Example 5.3: Complete Application Menu Loop
Loading Python environment...
Specification Reference: Lessons 1, 2, 3, 4 - all I/O concepts combined
Prompts Used:
- "Write a menu loop that displays options 1-7 and validates user input"
- "Create functions for each CRUD operation with error handling"
- "Implement search that finds notes by keyword"
Validation Steps:
- ✅ Menu displays and accepts 1-7 → invalid input shows error and re-prompts
- ✅ Create note → prompts for title/body/category, saves to file, returns to menu
- ✅ Read note → lists notes, accepts selection, displays selected note, returns to menu
- ✅ Update note → allows changing title/body, updates timestamp, returns to menu
- ✅ Delete note → requires confirmation before removing file
- ✅ Search → finds notes by keyword, shows results
- ✅ List → shows all notes grouped by category
- ✅ Exit → graceful shutdown with goodbye message
✨ Teaching Tip
Use Claude Code to test edge cases in your implementation:
"What happens if I create a note with an empty title? Or try to delete a note twice? Show me each error and how my code should handle it."
Error Handling and Validation
Production-quality applications handle errors gracefully. Let's review the validation patterns used throughout:
Input Validation (Lesson 1 Pattern)
Loading Python environment...
File Operation Errors (Lesson 2 Pattern)
Loading Python environment...
Path Safety (Lesson 3 Pattern)
Loading Python environment...
Data Validation
Loading Python environment...
Testing and Refinement
How to Test Your Application
-
Test Create: Run the app, select "Create Note", enter a title and body, verify file is created
$ ls notes/personal/
# Should show one or more .json files -
Test Read: Select "Read Note", choose one from the list, verify it displays correctly
-
Test Update: Select "Update Note", change the title, verify file is updated
-
Test Delete: Create a note, delete it, verify file is removed
-
Test Search: Create notes with different keywords, search for them, verify results
-
Test Menu Loop: Go through several operations, verify app returns to menu each time
-
Test Error Handling:
- Enter invalid menu choice → should show error and re-prompt
- Try to read notes when none exist → should show "No notes found"
- Try to delete non-existent note → should handle gracefully
Edge Cases to Consider
- What happens if someone creates a note with Unicode emoji? (UTF-8 should preserve it)
- What if the notes/ directory is deleted while the app is running? (mkdir will recreate it)
- What if someone has 50 notes—does the app still respond quickly? (Yes, file-per-note is efficient)
- What if you edit a note file manually while the app is running? (Next read will get latest version)
🎓 Expert Insight
In AI-native development, you don't test by hand 50 times—you test strategically. Your AI can help generate test cases: "What edge cases should I test for a note-taking app?" Then you verify each one systematically.
Project Deliverables
Your complete application should consist of:
-
Main Application File (e.g.,
note_app.pyormain.py)- Imports all necessary modules
- Defines BASE_DIR and initializes directories
- Implements all CRUD functions
- Implements menu loop with input validation
- Has
if __name__ == "__main__"guard withmain()call
-
Data Directory (
notes/)- Automatically created when app runs
- Contains category subdirectories (work, personal, learning)
- Stores notes as JSON files organized by category
-
Working Features - All of these must function:
- Menu displays correctly
- Create note saves to JSON file
- Read note loads and displays from file
- Update note modifies file and updates timestamp
- Delete note removes file after confirmation
- Search finds notes by keyword
- List displays notes organized by category
- Exit closes app gracefully
Success Criteria Checklist
Your application is complete when:
- ✅ Application runs without crashing on valid input
- ✅ All CRUD operations work correctly
- ✅ User input is validated (menu choices, required fields)
- ✅ Notes persist between sessions (data saved to JSON files)
- ✅ Search finds notes by keyword in title or body
- ✅ Application handles errors gracefully (missing files, corrupted JSON)
- ✅ Code is organized with functions for each operation
- ✅ Application supports 10-50 notes at responsive speed
- ✅ Menu returns to top after each operation
Try With AI
Build a production-ready note-taking application integrating all Chapter 27 I/O concepts.
🔍 Explore Application Architecture:
"Design note-taking app with CRUD operations (Create/Read/Update/Delete/Search/List). Explain: menu loop structure, pathlib organization by category (work/personal/learning), JSON persistence with context managers, input validation strategies, and error handling layers (input validation vs file operation errors)."
🎯 Practice CRUD Implementation:
"Implement note-taking CRUD: Create note with title/content/category validation, Read with context managers and JSON decoding, Update by category/title lookup, Delete with confirmation, Search by keyword, List organized by category. Show FileNotFoundError, PermissionError, JSONDecodeError handling with user-friendly messages."
🧪 Test Edge Cases:
"Handle edge cases: corrupted JSON lines (skip with warning), Ctrl+C during operation (context manager cleanup), 1GB log file (stream line-by-line), concurrent access (file locking), 50+ notes (performance optimization), international characters (ensure_ascii=False), and invalid category (validation/creation prompt)."
🚀 Apply Complete Integration:
"Build full app: menu (Create/Read/Update/Delete/Search/List/Exit), pathlib category directories, JSON storage with proper encoding, input validation throughout, error handling (missing files, corrupted data, permissions), search filtering, timestamp tracking (datetime preview), backup export, and reflection integrating all Chapter 27 concepts (console I/O, file I/O, pathlib, JSON)."
Congratulations! You've completed the Note-Taking App capstone, integrating all I/O concepts from Chapter 27. Your application demonstrates professional-level CLI development using Python 3.14+, pathlib, JSON, file I/O, and menu-driven interaction. Your reflection document proves you understand not just the "how" but the "why" of each design decision. You're now ready to extend this pattern to larger applications and ready for Chapters 23+ (datetime operations, OOP, etc.).