AI-Assisted Development Project Report¶
Student Name: Marcus Holmgren
Project Title: Flashcard Quizzer
Date: 2026-10-18
Executive Summary¶
Flashcard Quizzer is a modular Python 3 command-line application built to help learners practice, memorize, and author study decks directly from the terminal. Extending the course starter template into a production-packaged CLI tool managed with uv, the application supports loading decks from multiple JSON schemas, studying flashcards across three interchangeable quizzing algorithms (sequential, random, and adaptive), viewing colorized real-time feedback and end-of-session performance metrics, and interactively creating or appending to flashcard decks from the command line.
Architecturally, the project emphasizes clean separation of concerns and minimal reliance on third-party libraries. Core domain logic in quiz_engine.py implements the Strategy Pattern (QuizMode and its concrete strategies) and the Factory Pattern (QuizModeFactory) using only the Python standard library, keeping terminal rendering (ui.py), CLI orchestration (main.py), and file I/O (utils/file_handler.py and utils/deck_creator.py) strictly decoupled. The only runtime third-party dependency is rich, used exclusively in the presentation layer for cross-platform ANSI terminal styling.
Throughout development, I collaborated with three AI tools in distinct roles: Gemini to help structure XML-tagged prompts, Google Jules as an autonomous coding agent that executed prompts and opened GitHub Pull Requests, and Google Antigravity as a local agentic pair-programmer for diagnosing packaging issues and consolidating CI/CD workflows. Every AI-generated pull request was checked out locally, verified against automated quality gates (pytest, black, isort, flake8, mypy, bandit, and safety), and manually smoke-tested—revealing key lessons about the gap between passing unit tests and shipping well-architected software.
Project Overview¶
Problem Statement¶
Learners studying programming concepts, terminology, or general trivia often need a fast, distraction-free way to drill flashcards using active recall and spaced-repetition principles without setting up a heavy web or desktop application. Existing command-line scripts are frequently monolithic, hardcode a single traversal order, crash with raw tracebacks on malformed JSON files, or lack a built-in way to author and append new cards cleanly.
Flashcard Quizzer addresses this need by providing a resilient, zero-configuration CLI tool that validates user decks defensively, adapts dynamically to the learner's mistakes during a study session, and includes an interactive deck builder with safe overwrite/append workflows.
Solution Approach¶
My solution approach centered on four engineering pillars:
- Restrict Usage of Third-Party Dependencies: I deliberately constrained the runtime footprint to the Python 3.14 standard library (
abc,argparse,collections.deque,dataclasses,json,pathlib,random,sys,typing) for all core logic, data validation, and file persistence. Only a single runtime package (rich>=13.0.0) was permitted for terminal color output, minimizing supply-chain risk and keeping environment synchronization fast. -
Clean Architecture & Separation of Concerns: The codebase was refactored from a flat starter script into the
src/flashcard_quizzer/package divided into four layers:- Data Layer (
utils/file_handler.py,utils/deck_creator.py): Defines theFlashcarddataclass, parses both plain array and wrapped{"cards": [...]}JSON files, and persists user-created decks. - Domain Engine (
quiz_engine.py): Encapsulates card scheduling and answer tracking behind theQuizModeStrategy interface andQuizModeFactory, with zero terminal orinput()dependencies. - Presentation Layer (
ui.py): Handles colored feedback, user prompts, and formatted session statistics. - CLI Orchestration (
main.py,__main__.py): Parses CLI arguments (-f/--file,-m/--mode,--stats,--create, and thecreatesubcommand) and coordinates the quiz and deck-creation loops.
- Data Layer (
-
Modern Tooling & Automated CI/CD: Dependency locking and virtual environments are managed deterministically via
uv(pyproject.tomlanduv.lock). Two GitHub Actions workflows enforce continuous integration (ci.ymlrunning formatting, linting, static typing, security checks, and unit tests on every push/PR) and continuous documentation deployment (docs.ymlpublishing the Zensical documentation site to GitHub Pages).
Final Features¶
- Dual-Schema JSON Flashcard Loader (
utils/file_handler.py): Supports both plain list ([{"front": "...", "back": "..."}]) and wrapped dictionary ({"cards": [...]}) JSON formats with strict validation for missing keys, empty strings, and empty decks, exiting cleanly with status code1on errors. -
Three Pluggable Quiz Modes (
quiz_engine.py):SequentialQuizMode: Traverses flashcards in their original deck order (1..N).RandomQuizMode: Shuffles the deck before presenting cards.AdaptiveQuizMode: Prioritizes and re-queues incorrectly answered flashcards until the user masters them or reaches a configurable repeat cap (max_repeats=3).
-
Interactive CLI Quiz Loop & Session Analytics (
main.py,ui.py): Case-insensitive and whitespace-normalized answer checking, immediate green/red terminal feedback viarich, graceful exit handling (exit,Ctrl+C,EOFError) without tracebacks, and a end-of-session summary displaying total questions answered, accuracy percentage, and missed terms. - Interactive Deck Creator (
utils/deck_creator.py): Supports creating new JSON decks or modifying existing files via an interactive[O]verwrite, [A]ppend, or [C]ancel?workflow, complete with a running card counter ([Card 1]), empty-back re-prompting, and save confirmation on early exit. - Zensical Documentation Site & Custom Decks (
docs/,data/): Complete project documentation deployed to GitHub Pages alongside curated study decks (data/greek_mythology.json,data/earth.json, anddata/sample_flashcards.json).
AI Collaboration Experience¶
AI Tools Used¶
- Claude
- GitHub Copilot
- ChatGPT
- Gemini with 3.8 Flash
- Jules with Gemini 3.6 Flash
- Antigravity with Gemini 3.8 Flash
- Other:
Collaboration Workflow¶
- Structuring Prompts: I organized development into 8 sequential milestones documented in
docs/prompts.md. Using XML-style tags (<role>,<task>,<context>,<requirements>,<testing_requirements>, and<constraints>), each prompt specified the target files, architectural patterns, error-handling expectations, and requiredpytestcases. - Task Delegation: For this project, I forced myself to write prompts for almost all tasks. Only the initial setup of the GitHub repository, starter template, and custom JSON quiz datasets was done manually. At times, this strict prompt-only constraint was frustrating and is not how I would work on every task in the future—because there has to be a clear return on investment for writing a detailed prompt. When an issue is trivial—such as adding a missing
richdependency topyproject.toml—and I already know Python and how to runuv add richin two seconds, tying my hands behind my back and bringing out an AI sledgehammer to fix a one-line configuration issue feels counterproductive. - Review and Validation Process: Because every prompt submitted to Jules generated a GitHub Pull Request, I treated myself as the lead maintainer reviewing a developer's PR. For every PR, I fetched and checked out the branch locally, inspected the
git diff, ran the unit test suite (uv run pytest), and manually executed the CLI application to verify the behavior in real terminal usage. - Refining AI Suggestions: When local verification exposed architectural flaws or runtime bugs, I either issued a targeted follow-up prompt in the PR (for example, when Jules pushed empty documentation files in PR
#9), encoded the fix into a dedicated refactoring prompt (interaction 7 for dependency auditing and edge cases), or switched to Antigravity locally on the PR branch for interactive debugging (such as fixing the PEP 517 build system in PR#4and merging duplicate GitHub Actions workflows in PR#10).
Most Valuable AI Interactions¶
Example 1¶
Implementing the Quiz Engine with Strategy and Factory Patterns (PR #5)
- Context: I needed to build the core quizzing domain logic in
src/flashcard_quizzer/quiz_engine.pysupporting sequential, random, and adaptive card scheduling without coupling the logic to terminalinput()orprint()calls. - AI Prompt Interaction 4: Instructed Jules to implement an abstract
QuizModebase class (has_next(),get_next_card(),record_answer()), three concrete strategy classes (SequentialMode,RandomMode,AdaptiveMode), andQuizModeFactory.create_mode()using only the Python standard library, plus unit tests intests/test_quiz_modes.pywith >90% coverage. - AI Response: Jules created
src/flashcard_quizzer/quiz_engine.py(165 lines) usingabc.ABCandcollections.dequefor the adaptive re-queueing logic, along with 157 lines of unit tests achieving 96% coverage onquiz_engine.py. - Your Changes: While the engine implementation and unit tests were excellent, Jules did not export the new strategy and factory classes in
src/flashcard_quizzer/__init__.py. I updated__init__.pyto expose the public API (QuizMode,SequentialMode,RandomMode,AdaptiveMode,QuizModeFactory) and added the CI badge toREADME.md. - Outcome: A clean, extensible domain engine that met the Open/Closed and Single Responsibility principles and integrated seamlessly with the CLI layer in the next milestone.
Example 2¶
Diagnosing Packaging & Entry-Point Failures with Antigravity (PR #4)
- Context: In Prompt Interaction 3, I asked Jules to move
main.pyandutils/intosrc/flashcard_quizzer/, configure[project.scripts]inpyproject.toml, and add a GitHub Actions CI workflow. - AI Prompt Interaction 3 + Antigravity follow-up: After Jules opened PR
#4(with all CI checks passing), I checked out the branch locally and ranuv run python src/flashcard_quizzer/main.pyanduv run flashcard-quizzer. Both failed (ModuleNotFoundError: No module named 'flashcard_quizzer'). I prompted Antigravity locally: "The structure of this python app was refactored and utils was moved into the flashcard_quizzer structure. It looks like there are incorrect imports because I cant runuv run flashcard-quizzeroruv run python src/flashcard_quizzer/main.py. Please investigate and fix imports." - AI Response: Antigravity diagnosed that the imports in
main.pywere actually valid, andpytestonly passed becausepythonpath = ["src"]was configured in[tool.pytest.ini_options]. The real root cause was thatpyproject.tomllacked a[build-system]declaration (hatchling), causinguv syncto skip installing the package in editable mode and skip registering[project.scripts]. - Your Changes: With Antigravity, I added
[build-system](requires = ["hatchling"],build-backend = "hatchling.build") topyproject.toml, addedsrc/flashcard_quizzer/__main__.py, deleted the leftover hyphenatedsrc/flashcard-quizzer/and rootutils/directories, createddata/sample_flashcards.json, and addedtests/test_main.py. - Outcome: All three invocation methods (
uv run flashcard-quizzer,uv run python -m flashcard_quizzer, anduv run python src/flashcard_quizzer/main.py) executed cleanly.
Example 3¶
Interactive Deck Creator with Overwrite/Append Workflows (PR #7)
- Context: To provide full CRUD capabilities for flashcard decks, I wanted an interactive CLI workflow (
--create/create) allowing users to author new decks or append cards to existing JSON files safely. - AI Prompt Interaction 6: Specified file-existence handling (
[O]verwrite, [A]ppend, or [C]ancel?), a guided card-entry loop with running progress counters ([Card 1]), non-empty back validation without resetting the front text,"done"and"exit"save confirmations, andmonkeypatch/tmp_pathtests intests/test_deck_creator.py. - AI Response: Jules implemented
src/flashcard_quizzer/utils/deck_creator.py(182 lines), wired--createandcreateintomain.py, and wrote 273 lines of unit tests covering every interactive branch. - Your Changes: Because my prompt's
<context>referencedutils/deck_creator.pyinstead ofsrc/flashcard_quizzer/utils/deck_creator.py, Jules also created a redundant compatibility wrapper in a rootutils/directory. During later refactoring, I ensured all imports pointed strictly tosrc/flashcard_quizzer/utils/deck_creator.py. - Outcome: A user-friendly, fault-tolerant deck authoring feature with comprehensive automated test coverage.
Example 4¶
Codebase-Wide Dependency Audit and Edge-Case Hardening (PR #8)
- Context: During my review of PR
#6(interaction 5), I noticed that Jules usedrichinui.pywithout addingrichtopyproject.toml—it only worked becausesafety(a dev tool) pulledrichintouv.locktransitively. I also wanted to harden edge cases across all modules. - AI Prompt Interaction 7: Asked Jules to perform a
<dependency_audit>of all imports againstpyproject.toml, refactor class/variable names for clarity, add 100% PEP 257 docstrings and type annotations, and harden 5 specific edge cases (empty 0-card decks, division by zero when 0 questions are answered, whitespace trimming on inputs, bounded repeats in adaptive mode, and invalid deck creation paths). - AI Response: Jules added
"rich>=13.0.0"topyproject.toml, standardized strategy class names (SequentialQuizMode,RandomQuizMode,AdaptiveQuizMode), addedmax_repeats=3toAdaptiveQuizMode, guarded 0-question stats division, trimmed answer whitespace, and expanded the test suite to 54 tests. - Your Changes: Reviewed the diff and verified that
uv syncproperly categorized runtime vs. dev dependencies. I also noted in my review log that Jules used legacytyping.Optional/typing.Listinstead of Python 3.10+|union syntax. - Outcome: Production-hardened application with zero linting or type errors and 93% statement coverage.
Challenges with AI Collaboration¶
- Struggles with Repository Layout Migrations: Across prompts in interaction 1-3 and 6, Jules repeatedly struggled with moving from a flat starter layout to a
src/flashcard_quizzer/package structure—initially leaving files in the root, creating an emptysrc/flashcard-quizzer/directory with a hyphen (invalid in Python imports), omitting[build-system]inpyproject.toml, and creating duplicate shim files in rootutils/. - Transitive Dependency Blind Spots: Because Jules tests inside a dev environment where
uv sync --devis active, she did not notice when a runtime import (rich) was not defined in thepyproject.tomlfile, nor when a misspelled prompt requirement (zenzicalin interaction 1) wasn't installed inpyproject.toml. - Empty File Commits and Duplicate Workflows (PR #9 / #10): In interaction 8, Jules's initial commit claimed in its commit message to add four documentation files (
docs/index.md,getting-started.md,architecture.md,cli-reference.md), but the committed files were all 0 bytes! Only after I inspected the PR diff and asked "Where did the changes go? The files you pushed are all empty" did Jules push the actual Markdown content (bc2e31b). She also created a duplicate.github/workflows/deploy-docs.ymlalongside the existing.github/workflows/docs.yml, which I resolved with Antigravity. - Pretraining Bias Toward Legacy Python Typing: Even with
requires-python = ">=3.14,<3.15"inpyproject.toml, the AI defaulted tofrom typing import Optional, List, Dictrather than modernX | Noneandlist[...]syntax unless explicitly forbidden.
Software Engineering Practices¶
Code Quality Measures¶
- Code formatting (
black,isort): Configured with an 88-character line length targetingpy314inpyproject.tomland enforced in CI. - Linting (
flake8,mypy):flake8enforces PEP 8 compliance acrosssrc/andtests/, whilemypyruns withdisallow_untyped_defs = true,warn_return_any = true, andpython_version = "3.14". - Security & Vulnerability Scanning (
bandit,safety):bandit -r srcscans for common security issues in application code, andsafetyaudits locked dependencies. - Type hints: 100% of function and method signatures across all modules include explicit type annotations.
- Documentation/comments: Every module, class, and public function includes PEP 257 docstrings detailing arguments, return values, and exceptions, complemented by a Zensical documentation site (
docs/). - Error handling: Defensive validation traps
FileNotFoundError,json.JSONDecodeError,ValueError,OSError,KeyboardInterrupt, andEOFError, printing friendly messages tostderrand exiting with well-defined status codes (0for user cancellation,1for errors) without exposing raw tracebacks.
Testing Strategy¶
-
Layered Test Suite: I organized tests into unit and integration suites under
tests/:tests/test_flashcard_loader.py(10 tests): Tests plain arrays, wrapped{"cards": [...]}objects, missing files, malformed JSON, empty decks, and missing/whitespace fields.tests/test_quiz_modes.py(8 tests): VerifiesQuizModeFactoryinstantiation, case-insensitivity, invalid mode errors, exact sequential order, random shuffling, and adaptive re-queueing andmax_repeatsbounds.tests/test_deck_creator.py(15 tests): Usesmonkeypatchandtmp_pathto simulate creating new decks, overwriting, appending, cancelling, handling empty backs, and confirming saves on"exit"orCtrl+C.tests/test_integration.py&tests/test_main.py(13 tests): End-to-end CLI simulations testing full quiz sessions, accuracy calculation, division-by-zero protection on immediate exit, and CLI argument routing.tests/test_task_manager.py(8 tests): Retains unit tests for the starter utility module.
-
Test Coverage & Reliability: The suite comprises 54 passing tests achieving 93% overall statement coverage (with 100% on
file_handler.pyandui.py, 96% onquiz_engine.py, and 93% onmain.py). Every pull request was gated by.github/workflows/ci.ymlrunning the full quality and test pipeline automatically.
Design Patterns Used¶
-
Strategy Pattern (
src/flashcard_quizzer/quiz_engine.py):- Implementation: Defined an abstract base class
QuizMode(ABC)with abstract methodshas_remaining_cards() -> bool,get_next_card() -> Flashcard, andrecord_answer(card: Flashcard, is_correct: bool) -> None. Three concrete subclasses encapsulate distinct scheduling algorithms:SequentialQuizMode,RandomQuizMode, andAdaptiveQuizMode. - Rationale: Decouples the quiz orchestration loop in
main.pyfrom how cards are ordered or repeated.main.pysimply iterates whilequiz_strategy.has_remaining_cards()is true without knowing whether the deck is static, shuffled, or dynamically re-queueing missed cards in adeque. Allowing for a future spaced-repetition strategy.
- Implementation: Defined an abstract base class
-
Factory Pattern (
src/flashcard_quizzer/quiz_engine.py):- Implementation:
QuizModeFactory.create_mode(mode_name: str, cards: Sequence[Flashcard]) -> QuizModenormalizes the CLI mode string (case-insensitively and whitespace-trimmed) and instantiates the matchingQuizModestrategy, raising a descriptiveValueErrorif an unsupported mode is requested. - Rationale: Centralizes strategy creation and validation in one place, keeping
main.pyfree of conditionalif/elif/elseinstantiation branches.
- Implementation:
Code Structure and Organization¶
flashcard-quizzer/
├── .github/workflows/
│ ├── ci.yml # Automated lint, type-check, security & pytest pipeline
│ └── docs.yml # Automated Zensical build & GitHub Pages deployment
├── data/ # JSON flashcard decks (greek_mythology, earth, sample)
├── docs/ # Zensical documentation site, AI edit log, prompts & report
├── src/flashcard_quizzer/
│ ├── __init__.py # Package exports
│ ├── __main__.py # Module execution entry point (python -m flashcard_quizzer)
│ ├── main.py # CLI argument parsing and session/creation orchestration
│ ├── quiz_engine.py # Domain layer: Strategy (QuizMode) & Factory patterns
│ ├── ui.py # Presentation layer: Rich terminal rendering & input
│ └── utils/
│ ├── deck_creator.py # Interactive deck creation (overwrite/append/cancel)
│ ├── file_handler.py # Flashcard dataclass & dual-schema JSON loader
│ └── task_manager.py # Legacy starter CRUD utility
├── tests/ # 54 unit and integration tests (93% coverage)
├── pyproject.toml # Project metadata, hatchling build-system & tool configs
├── uv.lock # Deterministic dependency lockfile
└── zensical.toml # Zensical documentation site configuration
Technical Challenges and Solutions¶
Challenge 1¶
pytest Masking Broken Package Installation (ModuleNotFoundError)
- Problem: After Jules refactored the repository into
src/flashcard_quizzer/in PR#4, all unit tests passed inpytest, yet runninguv run flashcard-quizzeroruv run python src/flashcard_quizzer/main.pyfailed withModuleNotFoundError: No module named 'flashcard_quizzer'. - Solution: Configured
[build-system](requires = ["hatchling"],build-backend = "hatchling.build") inpyproject.toml, addedsrc/flashcard_quizzer/__main__.py, and ranuv syncsouvinstalled the package in editable mode and generated theflashcard-quizzerscript binary in.venv/bin/. - AI Involvement: I used Antigravity on the checked-out PR branch to inspect
uv syncwarnings and identify thatpythonpath = ["src"]in[tool.pytest.ini_options]had allowedpytestto importflashcard_quizzereven thoughuvtreated the project as an unpackaged virtual environment. - Lessons Learned: Passing unit tests do not guarantee valid Python packaging. Always test the installed CLI entry point directly (
uv run <script>).
Challenge 2¶
Hidden Transitive Runtime Dependency (rich)
- Problem: In PR
#6, Jules importedrichinsrc/flashcard_quizzer/ui.pyto render colored terminal output. Both local tests and GitHub Actions CI passed, but when reviewingpyproject.toml, I discovereddependencies = []was still empty. - Solution: Because
safety(a dev dependency) depended onrichtransitively,richwas present inuv.lockwhenever--devor--all-groupswas installed. In interaction 7, I instructed Jules to audit all imports acrosssrc/and explicitly add"rich>=13.0.0"to[project] dependenciesinpyproject.toml. - AI Involvement: Human code review caught the bug; Jules executed the dependency audit and lockfile update in PR
#8. - Lessons Learned: Dev dependencies can mask missing runtime dependencies in lockfiles. Always review
pyproject.tomlalongside code diffs when third-party imports are added.
Challenge 3¶
Empty Committed Files and Duplicate GitHub Actions Workflows in Documentation PR
- Problem: In PR
#9(interaction 8), Jules's first commit pushed 0-byte empty files fordocs/index.md,docs/getting-started.md,docs/architecture.md, anddocs/cli-reference.md. Additionally, after Jules fixed the empty files, the branch contained two competing GitHub Pages workflows:.github/workflows/docs.yml(generated byzensical newusingpip) and.github/workflows/deploy-docs.yml(created by Jules usinguv). - Solution: I caught the empty files during PR diff review and prompted Jules to push the missing content (
bc2e31b). Then, using Antigravity, I merged theconcurrency: group: "pages"config,astral-sh/setup-uv@v5caching,.python-version, anduv sync --dev/uv run zensical build --cleansteps fromdeploy-docs.ymlintodocs.yml, deleteddeploy-docs.yml, and updated the CLI examples to usedata/greek_mythology.json. - AI Involvement: Jules generated the documentation content after my PR review prompt; Antigravity consolidated the two GitHub Actions workflows cleanly.
- Lessons Learned: Never trust an AI agent's commit message or PR summary without inspecting the file diffs, and always reconcile AI-generated CI workflows with existing project workflows.
Code Quality Analysis¶
Metrics¶
- Lines of code: 965 lines of Python in
src/flashcard_quizzer/(387 executable statements) + 837 lines of Python intests/(1,802 total lines of Python) - Test coverage: 93% overall statement coverage across 54 unit and integration tests (
file_handler.py: 100%,ui.py: 100%,quiz_engine.py: 96%,main.py: 93%,deck_creator.py: 87%) - Number of functions/classes: 8 classes and 46 functions/methods in
src/flashcard_quizzer/ - Linting score: 0 errors / 100% pass rate across
flake8,black --check,isort --check,mypy(strict mode), andbandit -r src
Self-Assessment¶
- Code Readability: 5/5 — All modules follow PEP 8 strictly, functions are concise and focused on single responsibilities, identifiers are descriptive (
SequentialQuizMode,has_remaining_cards,missed_cards), and every class and function has a clear PEP 257 docstring. - Code Maintainability: 5/5 — The Strategy and Factory patterns cleanly isolate card-scheduling logic from terminal UI and file I/O, and the project is packaged with deterministic dependencies (
uv.lock) and automated CI (ci.yml). - Test Quality: 5/5 — The 54-test suite tests both happy paths and critical edge cases (malformed JSON, empty decks, division by zero on 0 answered cards, adaptive mode repeat bounds,
Ctrl+C/EOFErrorexits, and file overwrite/append flows) without relying on fragile sleep timers or external network calls. - Documentation: 5/5 — Includes a comprehensive
README.md, a deployed Zensical documentation site (index.md,getting-started.md,architecture.md,cli-reference.md), full prompt records (prompts.md), and a transparentai_edit_log.md.
Learning Outcomes¶
Technical Skills Developed¶
- Modern Python Packaging with
uvand Python 3.14: Gained hands-on experience configuringpyproject.tomlwith PEP 517 build backends (hatchling),[project.scripts]entry points,[dependency-groups], and deterministicuv.lockworkflows in CI. - Static Analysis & Security Toolchains: Integrated
black,isort,flake8,mypy,bandit, andsafetyinto a unified local and GitHub Actions workflow. - Documentation Engineering with Zensical: Learned how to initialize, configure (
zensical.toml), and deploy a static documentation site using Zensical and GitHub Pages (actions/deploy-pages).
AI Collaboration Skills¶
- Structured XML Prompt Engineering: Learned how organizing prompts with explicit
<role>,<context>,<requirements>,<testing_requirements>, and<constraints>tags dramatically reduces ambiguity and improves first-pass code quality. - Multi-Agent Workflow Orchestration: Discovered the complementary strengths of different AI paradigms—using an asynchronous PR agent (Jules) for well-scoped feature implementation and test generation, while using a local interactive agent (Antigravity) for rapid environment debugging and file consolidation.
- Pragmatic Task Selection (When Not to Prompt): Forcing myself to use prompts for nearly every change taught me a valuable lesson about developer ergonomics: AI agents excel at generating 200-line modules and test suites, but using a full PR agent cycle for a 1-line fix (like adding a missing dependency to
pyproject.toml) has high overhead compared to making the edit directly withuv add.
Software Engineering Insights¶
- Automated Linters Eliminate Hallucinations, Not Architectural Flaws: Because Jules ran
pytest,mypy,flake8, andblackbefore opening each PR, I experienced zero syntax errors or hallucinated standard-library methods. However, automated tools did not prevent architectural mistakes like missing[build-system]metadata, unpinned transitive dependencies, or duplicate CI workflows—proving that human architectural oversight remains indispensable. - Value of Design Patterns for Testability: Isolating
QuizModestrategies fromui.pymade it trivial to test complex adaptive re-queueing behavior deterministically in milliseconds without mocking terminal I/O.
Reflection¶
What Worked Well¶
- Strict Quality Gates in
pyproject.tomland CI: Havingpytest,mypy,flake8,black,isort, andbanditconfigured from Interaction 1 meant every subsequent AI PR had to pass a high technical bar before review. - Incremental, PR-Based Development: Breaking the application into 8 focused PRs made code reviews manageable and allowed me to checkout and test each vertical slice locally before building the next layer.
- Separation of Domain and Presentation Layers: The boundary between
quiz_engine.py,ui.py, andutils/file_handler.pykept the codebase clean and easy to reason about.
What Could Be Improved¶
- Establish
src/Packaging Before Feature Prompts: In hindsight, I should have configured thesrc/flashcard_quizzer/directory layout and[build-system](hatchling) in Interaction 1 rather than migrating files across the prompts in interaction 2 and 3. - Explicit Modern Python Typing Constraints: Adding a rule in
AGENTS.mdand prompt<constraints>requiring PEP 604 union syntax (X | None) and PEP 585 built-in generics (list,dict) would have prevented the AI from defaulting to legacytyping.Optionalandtyping.Listimports. - Hybrid Human + AI Editing Workflow: Rather than restricting myself to prompt-only edits for minor fixes, combining direct manual edits for trivial configuration tweaks with AI prompts for larger feature and test generation is much faster and less frustrating.
Future Enhancements¶
- Persistent Spaced-Repetition Metadata: Extend the JSON schema and
AdaptiveQuizModeto persist per-card mastery scores, ease factors, and next-review timestamps across sessions. - Fuzzy / Levenshtein Answer Matching: Allow configurable tolerance for minor spelling typos using Python's standard library difflib.SequenceMatcher.
- Session History & Export: Log completed study sessions to a local SQLite database or CSV/Markdown progress report to track accuracy trends over time.
Conclusion¶
Building Flashcard Quizzer demonstrated both the power and the boundaries of modern AI-assisted software development. By combining structured prompt engineering, rigorous automated tooling (uv, pytest, mypy, flake8, black, isort, bandit), and disciplined local code reviews of every pull request, I was able to transform a basic starter template into a well-architected, 93%-covered Python 3.14 CLI application with comprehensive documentation.
More importantly, this project reshaped how I view AI coding assistants: when paired with strong automated linters and type checkers, modern AI agents rarely hallucinate syntax or APIs, yet they still require an experienced engineer to guide package architecture, audit dependencies, verify real-world execution, and decide when a direct manual edit is wiser than a prompt. Moving forward, I will continue using strict static analysis gates, design patterns that isolate domain logic, and a pragmatic combination of autonomous PR agents and local interactive AI pair-programming.
Appendices¶
Appendix A: AI Interaction Log¶
The complete chronological record of all 8 AI interactions, including exact prompts, Jules commit messages, code review findings, and local fixes with Antigravity, is available in:
docs/ai_edit_log.md— Detailed AI Edit Logdocs/prompts.md— Full text of prompts interaction 1 through 8
Key highlights from docs/ai_edit_log.md:
- Interaction 1 (
d000c31, PR #2): Python 3.14 &uvenvironment setup and linter configuration. - Interaction 2 (
29b11f3, PR #3):Flashcarddataclass & dual-schema JSON loader (file_handler.py). - Interaction 3 (
c2a8e63, PR #4):src/flashcard_quizzer/migration, CI workflow, and Antigravity fix forhatchling[build-system]. - Interaction 4 (
26d4f53, PR #5): Strategy (QuizMode) and Factory (QuizModeFactory) implementation inquiz_engine.py. - Interaction 5 (
7feba0a, PR #6): Rich terminal UI (ui.py), CLI loop (main.py), and discovery of the undeclaredrichdependency. - Interaction 6 (
38220b1, PR #7): Interactive deck creator (deck_creator.py) with overwrite/append/cancel support. - Interaction 7 (
1bca64d, PR #8): Dependency audit, identifier refactoring, PEP 257 docstrings, and edge-case hardening. - Interaction 8 (
752916f, PR #9 & #10): Zensical documentation site, resolution of empty committed files, and GitHub Actions workflow consolidation.
Appendix B: Code Statistics¶
| Module | Statements | Missed | Coverage | Role |
|---|---|---|---|---|
src/flashcard_quizzer/__init__.py |
2 | 0 | 100% | Package exports |
src/flashcard_quizzer/__main__.py |
3 | 3 | 0% | python -m entry point |
src/flashcard_quizzer/main.py |
84 | 6 | 93% | CLI parser & session orchestration |
src/flashcard_quizzer/quiz_engine.py |
80 | 3 | 96% | Strategy & Factory quiz engine |
src/flashcard_quizzer/ui.py |
33 | 0 | 100% | Rich terminal presentation |
src/flashcard_quizzer/utils/__init__.py |
0 | 0 | 100% | Utils subpackage |
src/flashcard_quizzer/utils/deck_creator.py |
115 | 15 | 87% | Interactive deck builder |
src/flashcard_quizzer/utils/file_handler.py |
44 | 0 | 100% | JSON loader & Flashcard model |
src/flashcard_quizzer/utils/task_manager.py |
26 | 0 | 100% | Starter CRUD utility |
Total (54 passed in 0.31s) |
387 | 27 | 93% | Full Application |
Appendix C: Additional Resources¶
- uv Documentation (Astral) — Python package and project management
- Zensical Documentation — Static documentation site generator
- Rich Documentation — Terminal formatting and color output
- ArjanCodes YouTube videos