Case Study 2: From Sticky Notes to Software
Overview
Builder: Priya, a high school math teacher with zero programming experience Goal: Replace a paper-based student grade tracking system with a digital tool AI Tool: GitHub Copilot Chat in VS Code Session Duration: Approximately two hours (with breaks to review) Lines of Code Produced: ~400 Prior Python Experience: None. Has read the Python basics in Chapter 5 and completed the environment setup in Chapter 4.
The Problem
Priya teaches three sections of Algebra II with 30 students each. She tracks grades using a notebook with sticky notes marking students who need extra help. At the end of each grading period, she spends an entire weekend manually calculating averages, determining letter grades, and writing report comments. She has heard about vibe coding from a colleague and wants to try building a grade tracker.
Priya has no programming experience beyond reading Chapter 5 of this textbook. She understands variables, functions, lists, and dictionaries conceptually, but she has never written a program. This case study shows that the vibe coding workflow works even for complete beginners --- though it requires patience and willingness to ask the AI to explain what it produces.
Starting the Session
Priya opens VS Code with GitHub Copilot Chat and starts with an honest prompt about her situation:
I am a high school math teacher with no programming experience. I want
to build a simple command-line tool in Python to track student grades.
I need to:
1. Add students by name
2. Record grades for assignments (each assignment has a score and a
maximum possible score)
3. Calculate each student's average
4. See which students are below a C (below 73%)
5. Print a simple report card for each student
6. Save everything to a file
Please keep the code as simple as possible and explain anything complex.
I have read about Python basics but have never written a program.
The AI responds with a plan that uses two dataclasses (Student and GradeEntry), a GradeTracker class, and an argparse CLI. Priya notices the AI mentions "dataclass" and "argparse" --- terms she remembers from Chapter 5 --- but she wants to make sure she understands the plan before proceeding:
Can you explain in plain English what a dataclass is and why we need two
of them? I want to understand the design before we write code.
The AI explains: "A dataclass is like a form template. The Student dataclass is a form for recording a student's name and ID. The GradeEntry dataclass is a form for recording one grade --- which student it belongs to, which assignment, the score, and the maximum possible score. We need two because students and grades are different types of information that relate to each other."
This explanation helps Priya connect the concept to something familiar. In her paper gradebook, she has a roster page (students) and grade columns (grade entries). The dataclasses mirror this structure.
Building the Data Model
Priya proceeds with the data model:
Create the Student and GradeEntry dataclasses. Keep them simple.
A Student has an id, name, and optional student ID string.
A GradeEntry has an id, student_id (linking to which student),
assignment_name, score, max_score, and a category (like "homework",
"test", or "quiz"). Add a property that calculates the percentage.
The AI produces both dataclasses. Priya notices the @property decorator on the percentage method and asks about it:
What does @property do? I see you wrote it above the percentage method.
The AI explains that @property lets you access grade.percentage as if it were a regular attribute, without needing parentheses, even though it is calculated on the fly. Priya appreciates this --- it means she can use grade.percentage naturally without remembering to write grade.percentage().
This is a hallmark of successful vibe coding for beginners: asking the AI to explain what it generates. Priya does not need to memorize every Python feature, but she does need to understand enough to evaluate whether the code makes sense.
The Grade Tracker Class
For the main functionality, Priya asks for features one at a time:
Create a GradeTracker class with methods to:
1. Add a student
2. Add a grade for a student (check that the student exists first)
3. Get all grades for one student
4. Calculate a student's overall average as a percentage
The AI produces a clean implementation. Priya tests it by mentally tracing through the code: "If I add a student with ID 1, then add a grade with student_id 1, score 45, max_score 50... the average should be 90%." She asks the AI to verify:
If a student has these grades:
Homework 1: 45/50
Quiz 1: 18/20
Midterm: 82/100
What should their average be? Can you trace through the calculation?
The AI walks through the calculation: total_score = 45 + 18 + 82 = 145, total_max = 50 + 20 + 100 = 170, average = (145/170) x 100 = 85.3%. Priya verifies this matches what her manual calculation would produce. It does.
Letter Grades and Report Cards
Priya adds the grading scale:
Add a method that converts a percentage to a letter grade using this scale:
93+ = A, 90-92 = A-, 87-89 = B+, 83-86 = B, 80-82 = B-,
77-79 = C+, 73-76 = C, 70-72 = C-, 67-69 = D+, 63-66 = D,
60-62 = D-, below 60 = F
Also add a method to generate a text report card for a student showing
all their grades, their average by category, and their overall grade.
The AI implements the grade scale as a list of (threshold, letter) tuples and iterates through it to find the matching grade. The report card method produces formatted output:
==================================================
REPORT CARD: Alice Johnson
Student ID: STU001
==================================================
Grades:
------------------------------------------------
Assignment Score Pct Category
------------------------------------------------
Homework 1 45/50 90.0% homework
Quiz 1 18/20 90.0% quiz
Midterm 82/100 82.0% test
Homework 2 48/50 96.0% homework
Category Averages:
------------------------------
homework 93.0% (A)
quiz 90.0% (A-)
test 82.0% (B-)
Overall Average: 85.3% (B)
==================================================
Priya is thrilled. This is exactly the report she spends hours producing manually, and the program generates it instantly.
Identifying Struggling Students
The feature Priya is most excited about replaces her sticky note system:
Add a method called students_needing_help that finds all students whose
average is below a given threshold (default 70%). Return their names,
averages, and letter grades, sorted from lowest to highest average.
The AI produces:
def students_needing_help(self, threshold: float = 70.0) -> list[dict]:
results = []
for student in self.students:
avg = self.get_student_average(student.id)
if avg is not None and avg < threshold:
results.append({
"name": student.name,
"student_id": student.student_id,
"average": avg,
"letter_grade": self.get_letter_grade(avg),
})
return sorted(results, key=lambda x: x["average"])
Priya tests it mentally: "If Bob has a 65% average and Carol has a 95%, only Bob should appear." She asks the AI to add a display function that shows the results clearly, and also to add a class average calculation:
Also add a method that calculates the class average --- the average
of all student averages. And a display method that shows a class roster
with each student's name, average, and letter grade.
Persistence and File Handling
Priya follows the same pattern from Chapter 6 for persistence:
Add save and load methods that store everything in a JSON file. I need
to save both the student list and all the grades. Handle the case where
the file does not exist yet.
The AI structures the JSON with two top-level keys: "students" and "grades". Priya asks about this:
Why do we have two separate lists in the JSON file instead of putting
grades inside each student?
The AI explains that keeping them separate (a "flat" structure) is simpler for loading and saving, and avoids deeply nested data. It also makes it easier to add a grade without loading and modifying a specific student object. Priya accepts this --- it mirrors how her paper gradebook works, with a separate roster page and grade pages.
The Moment of Truth
Priya runs the program with real data from her current gradebook:
$ python grades.py add-student "Alice Johnson" --id STU001
Added student: Alice Johnson (STU001)
$ python grades.py add-grade 1 "Midterm Exam" 82 100 --category test
Added grade for Alice Johnson: Midterm Exam (82.0/100.0)
$ python grades.py report 1
[Full report card displays]
$ python grades.py roster
[Class roster with averages displays]
$ python grades.py help-needed --threshold 75
Students below 75%:
Bob Smith (STU002): 65.0% (D)
Everything works. Priya enters her entire class's data over the next hour and generates report cards for all 90 students. A process that previously took a weekend now takes seconds.
Challenges Priya Encountered
The session was not without difficulties:
-
Understanding error messages. When Priya accidentally typed
python grades.py add-grade 1 "Quiz" 18 20 --categorywithout providing a category value, Python produced anargparseerror. She pasted the error into the AI and asked what it meant. The AI explained and suggested the fix. -
Forgetting to save. Priya initially did not realize she needed to call
_save()after modifications. The AI had implemented auto-saving inadd_student()andadd_grade(), but when she manually modified a student object, the changes were lost. The AI explained the concept of persisting state to disk. -
ID confusion. Priya was confused by the difference between the internal numeric
idand the school'sstudent_idstring. The AI added a comment explaining that the internal ID is auto-generated and used by the program, while the student ID is an optional human-readable identifier. -
Letter grade boundary cases. Priya discovered that a student with exactly 93.0% was getting an A, but she wanted 92.5% rounded up to also be an A. She asked the AI to use rounding, and they discussed the implications of rounding for letter grade boundaries.
What Priya Learned
Priya's takeaways from the session:
-
You do not need to understand every line. Priya could not write the
sorted(results, key=lambda x: x["average"])line from memory, but she understood what it does: sort the list by the average field. That level of understanding is sufficient for vibe coding. -
Ask the AI to explain. When Priya did not understand
@propertyordefault_factory, she asked. The AI's explanations connected unfamiliar syntax to familiar concepts. -
Test with real data early. Entering real student grades early in the process revealed the rounding issue, which was easy to fix before the code grew more complex.
-
Error messages are informative, not scary. Priya's instinct was to panic when she saw a traceback. By the end of the session, she had learned to read the last line first (which usually describes the actual problem) and paste the error into the AI for help.
-
Small, incremental prompts work best. Priya built each feature with a separate prompt, testing after each one. This made it easy to identify problems and kept the conversation focused.
The Bigger Picture
Priya's case demonstrates that vibe coding is accessible to people with no programming background. She built a genuinely useful application in two hours, and she understands how it works at a conceptual level. She cannot modify the Python code directly (yet), but she can describe changes to the AI and evaluate whether the modifications are correct.
This is the promise of vibe coding: it lowers the barrier to creating software without eliminating the need for critical thinking. Priya still had to: - Define her requirements clearly - Evaluate the AI's output for correctness - Think about edge cases (rounding, missing data) - Test with real data
These are software engineering skills, and they matter regardless of whether you write the code by hand or generate it with AI.
The complete code for the student grade tracker is available in code/case-study-code.py.