🎯 Hands-on Portfolio Deliverable: Retrofit an untyped, untested Python codebase with full type annotations and comprehensive pytest fixtures using AI assistance, transforming legacy code into maintainable production software.
The brief
Use your own untyped Python codebase or download the untyped starter project. Add tests and types in tandem. The starter README explains setup; your own project may use a different layout.
Why both at once
Adding types reveals what tests should cover (type signatures define contracts). Adding tests reveals what types ARE (the actual behavior). They reinforce each other.
Starting point
A codebase with:
- Functions without type hints.
- Few or no tests.
- Behavior knowable only by reading code.
Choose a manageable scope. The supplied starter is intentionally small; for your own codebase, focus on a manageable set of public functions.
Step 1: Audit (10 min)
Install the project dependencies plus pyright, pytest, and pytest-cov in a virtual environment. Run pyright src/ (or your project's source path). If tests exist, run pytest --cov=src --cov-report=term; if no tests or coverage data exist, record “no baseline tests; coverage not measured.” Record the actual baseline rather than assuming a particular error count.
Step 2: Plan (10 min)
Discuss with AI:
"I have this untyped codebase [paste/share files]. I want to add type hints AND tests. What order should I work in? Start with public APIs or internal functions?"
AI suggests starting with public APIs (most-used; types matter most). Then tests for each.
Order:
- Top-level functions (public API).
- Add types + minimal docstrings.
- Add tests for each.
- Move to internal functions.
- Iterate.
Step 3: Type the public API (15 min)
Pick one public function. Look at it; understand what it does.
"Add type hints to this function. Don't change behavior. If types are unclear from code, ask me."
AI adds:
def fetch_user(user_id: int) -> dict[str, Any] | None:
...
If you don't know what it returns, AI asks. You answer. AI updates types.
Step 4: Test the function (15 min)
"Generate pytest tests for fetch_user. Based on the type signature and the implementation, test happy path, missing user (returns None), and any other behavior you observe."
AI generates tests. Run them; they pass against current code.
Now you have:
- A typed signature (contract).
- Tests verifying behavior.
- Documentation (implicit in types + tests).
Step 5: Repeat for each public function
Same pattern. Type. Test. Verify.
Step 6: Identify the gaps
After typing public APIs, you'll notice:
- Internal functions still untyped.
- Some return types are
Any(didn't know better). - Edge cases not tested.
Iterate:
- Refine
Anyto specific types. - Add tests for edge cases AI mentioned.
- Add types to internal functions.
Step 7: Type-check + test
pyright src/ # errors should drop
pytest --cov # coverage should climb
Target after this build: pyright basic mode passes; coverage > 70%.
For strict mode + 90% coverage: takes more iteration.
Step 8: Document findings
# Retrofit Reflection
## Codebase stats
Before:
- Type errors (pyright basic): 247
- Test coverage: 12%
After:
- Type errors: 0
- Test coverage: 73%
## What AI helped with most
- Generating tests from function signatures.
- Inferring types from usage patterns.
## What I had to do manually
- Confirming ambiguous return types.
- Edge cases AI didn't think of.
## Surprises
- Found 2 actual bugs while adding types (functions returned wrong types).
- Found 3 untested branches.
Deliverables
- Codebase with type hints on public APIs.
- Tests covering happy path + key edge cases.
- Pyright passes basic mode.
- Coverage > 70%.
- Reflection document.
What you'll learn
- Types reveal contracts; tests verify them; they reinforce.
- AI accelerates both — read the function; types + tests follow.
- Untyped code often has hidden bugs that types reveal.
- AI-suggested tests miss edges — add them yourself.
- Incremental approach works (don't try perfect; aim for better).
Stretch
- Pyright strict mode passes.
- Coverage > 90% on critical modules.
- Add tests for any bugs found during the retrofit.
📊 100-Point Evaluation Rubric
| Evaluation Dimension | Weight | Target Standard (Pass ≥ 80%) |
|---|---|---|
| 1. Type Annotation Completeness | 25 pts | Public APIs and core internal functions annotated with modern Python type hints (`int |
| 2. Test Suite & Coverage | 25 pts | Pytest test suite reaches > 70% line coverage, covering normal execution flows, null checks, and error responses. |
| 3. Edge Cases & Bug Discovery | 25 pts | Identified and tested meaningful edge cases or contracts; add regression tests for bugs if found. If none are found, document the checks performed. |
| 4. Documentation & Retrofit Reflection | 25 pts | Documented before/after audit metrics (pyright error count, coverage %) and analysis of AI prompt patterns for test/type generation in RETROFIT.md. |
📤 Submission Instructions & Guidelines
- Deliverable Formats:
- Option A (Recommended): Upload a single
.ziparchive containing your typed Python files, pytest test suite, andRETROFIT.md. - Option B: Upload your
RETROFIT.mdand paste your GitHub repository link in the Submission Notes box below.
- Option A (Recommended): Upload a single
- Pre-Submission Verification:
- Run
pyright src/and ensure 0 type errors in basic mode. - Run
pytest --cov=srcand verify coverage exceeds 70%.
- Run
- Submit Your Work:
- Use the file dropzone below to upload your
.zipfile. - Click Submit Assignment to complete this build.
- Use the file dropzone below to upload your