Case Study 1: A Non-Programmer Learns to Read Python

Background

Maria Gonzalez is a marketing manager at a mid-sized e-commerce company. She has never written a line of code in her life. Her technical background consists of advanced spreadsheet skills, some experience writing SQL queries for marketing reports, and a natural curiosity about how things work. When her company began exploring AI coding tools to automate marketing workflows, Maria saw an opportunity. She did not want to become a software engineer. She wanted to understand enough Python to use AI assistants for building small automation tools and to evaluate the code those tools produced.

This case study follows Maria through her first two weeks of learning to read Python for the purpose of vibe coding. It illustrates the key insight from this chapter: you do not need to write Python fluently to be an effective vibe coder. You need to read it well enough to have informed conversations about code.

The Starting Point

Maria began by asking Claude to write a simple script that would read a CSV file of customer emails, check which ones had not opened a newsletter in the last 30 days, and generate a re-engagement list. The AI produced the following code:

"""Generate a re-engagement email list from customer data."""

import csv
from datetime import datetime, timedelta
from pathlib import Path
from dataclasses import dataclass


@dataclass
class Customer:
    """Represents a customer record."""
    email: str
    name: str
    last_open: datetime | None
    signup_date: datetime


def parse_date(date_str: str) -> datetime | None:
    """Parse a date string, returning None for empty or invalid values."""
    if not date_str or date_str.strip() == "":
        return None
    try:
        return datetime.strptime(date_str.strip(), "%Y-%m-%d")
    except ValueError:
        return None


def load_customers(filepath: Path) -> list[Customer]:
    """Load customer data from a CSV file."""
    customers = []
    with open(filepath, "r", newline="") as f:
        reader = csv.DictReader(f)
        for row in reader:
            customer = Customer(
                email=row["email"].strip().lower(),
                name=row["name"].strip(),
                last_open=parse_date(row.get("last_open", "")),
                signup_date=parse_date(row["signup_date"]) or datetime.now(),
            )
            customers.append(customer)
    return customers


def find_inactive_customers(
    customers: list[Customer],
    days_threshold: int = 30,
) -> list[Customer]:
    """Find customers who have not opened an email in the given number of days."""
    cutoff = datetime.now() - timedelta(days=days_threshold)
    inactive = []
    for customer in customers:
        if customer.last_open is None or customer.last_open < cutoff:
            inactive.append(customer)
    return inactive


def write_reengagement_list(
    customers: list[Customer],
    output_path: Path,
) -> None:
    """Write the re-engagement list to a CSV file."""
    with open(output_path, "w", newline="") as f:
        writer = csv.writer(f)
        writer.writerow(["email", "name", "last_open", "days_inactive"])
        for customer in customers:
            if customer.last_open:
                days = (datetime.now() - customer.last_open).days
                last_open_str = customer.last_open.strftime("%Y-%m-%d")
            else:
                days = "never"
                last_open_str = "never"
            writer.writerow([customer.email, customer.name, last_open_str, days])


if __name__ == "__main__":
    input_file = Path("customers.csv")
    output_file = Path("reengagement_list.csv")

    customers = load_customers(input_file)
    inactive = find_inactive_customers(customers, days_threshold=30)

    print(f"Total customers: {len(customers)}")
    print(f"Inactive customers: {len(inactive)}")

    write_reengagement_list(inactive, output_file)
    print(f"Re-engagement list written to {output_file}")

Maria stared at this for about ten minutes, feeling overwhelmed. But she committed to a systematic approach rather than panicking.

Week 1: The Top-Down Reading Strategy

Maria followed the top-down reading strategy described in Section 5.14 of this chapter. Instead of trying to understand every line, she started with the big picture.

Step 1: Reading the Imports

She looked at the first few lines:

import csv
from datetime import datetime, timedelta
from pathlib import Path
from dataclasses import dataclass

Maria reasoned: "The program uses CSV files, dates, file paths, and something called a dataclass. All of these are standard Python -- no third-party packages to install." This was her first insight. The imports told her what the program depended on, and since everything came from Python's standard library, there were no installation steps required.

Step 2: Understanding the Data Structure

Next, she looked at the Customer dataclass:

@dataclass
class Customer:
    email: str
    name: str
    last_open: datetime | None
    signup_date: datetime

Even without knowing Python, Maria could read this. "A Customer has an email, a name, a last-open date that might be missing, and a signup date." The | None told her that last_open was optional. She recognized this pattern from database schemas she had worked with -- nullable fields.

Step 3: Scanning the Function Names

Maria then read only the def lines:

  • parse_date -- converts date strings to date objects
  • load_customers -- reads the CSV file
  • find_inactive_customers -- finds the ones who have not opened emails
  • write_reengagement_list -- saves the result

Without reading the function bodies, she had a complete understanding of the program's workflow. "It reads customer data, finds the inactive ones, and writes a new file." This was exactly what she had asked for.

Step 4: Reading the Entry Point

The if __name__ == "__main__": block confirmed the workflow:

customers = load_customers(input_file)
inactive = find_inactive_customers(customers, days_threshold=30)
write_reengagement_list(inactive, output_file)

Load, filter, write. Three steps, three function calls. Maria realized the program was well-organized.

Week 1: First Modifications Through Conversation

With this structural understanding, Maria had her first real vibe coding conversation. She asked Claude: "Can you modify this so the days_threshold is configurable from the command line instead of hardcoded to 30?"

The AI updated the main block:

import sys

if __name__ == "__main__":
    days = int(sys.argv[1]) if len(sys.argv) > 1 else 30
    input_file = Path("customers.csv")
    output_file = Path("reengagement_list.csv")

    customers = load_customers(input_file)
    inactive = find_inactive_customers(customers, days_threshold=days)
    ...

Maria could read this change: "If a number is provided on the command line, use it. Otherwise, default to 30." She recognized the if/else pattern from the chapter. She also noticed the int() conversion and wondered: "What happens if someone types a word instead of a number?" She asked the AI to add error handling, demonstrating that her code-reading ability was already leading to better code.

Week 2: Deeper Reading Skills

By the second week, Maria was spending less time on structure and more time on details. She began to notice specific Python patterns.

Recognizing Error Handling

In the parse_date function:

try:
    return datetime.strptime(date_str.strip(), "%Y-%m-%d")
except ValueError:
    return None

Maria understood: "If the date is in the wrong format, instead of crashing, the program returns None. That is why the Customer class allows last_open to be None." She saw how the error handling and the data model were connected.

Understanding File Operations

with open(filepath, "r", newline="") as f:
    reader = csv.DictReader(f)
    for row in reader:
        ...

Maria learned that with open(...) was the safe way to handle files. She recognized from the chapter that this pattern guaranteed the file would be closed properly. When she later saw AI-generated code that used f = open(...) without with, she knew to ask for a correction.

Spotting a Real Issue

During her review, Maria noticed something:

signup_date=parse_date(row["signup_date"]) or datetime.now(),

She reasoned: "If the signup date is missing or invalid, it defaults to right now. But that is wrong -- if someone signed up years ago and the date field is just empty, we should not pretend they signed up today." She asked the AI about this, and it agreed the logic was flawed. They changed it to raise an error for missing signup dates, since that field should always be present in the data.

This was a pivotal moment. Maria, with no programming experience, caught a logic bug in AI-generated code through careful reading and domain knowledge. She knew her data -- she knew signup dates were always present in the source system -- and that knowledge, combined with her growing ability to read Python, made her an effective code reviewer.

The Patterns Maria Learned to Recognize

By the end of two weeks, Maria had a personal checklist of patterns she could identify on sight:

  1. Dataclasses: She recognized @dataclass as "this defines a data structure" and immediately looked at the fields to understand what data the program worked with.

  2. Type hints: She read str | None as "this could be text or empty" and list[Customer] as "a list of customer objects."

  3. Context managers: She knew with open(...) as f: meant safe file handling.

  4. List comprehensions: She learned to read [x for x in items if condition] as "filter items, keeping only the ones where condition is true."

  5. f-strings: She recognized f"Total: {len(data)}" as inserting a calculated value into a message.

  6. The if __name__ == "__main__": block: She knew this was the starting point of the program.

  7. Error handling: She recognized try/except as "try this, and if it fails, do that instead."

Key Lessons and Takeaways

Maria's journey illustrates several important principles for non-programmers approaching vibe coding:

Domain knowledge is your superpower. Maria caught the signup date bug not because she understood Python better than the AI, but because she understood her business data. Technical knowledge and domain knowledge are complementary -- the AI provided technical skills, and Maria provided contextual judgment.

Reading top-down is more efficient than bottom-up. By starting with imports, class definitions, and function names, Maria could understand the overall structure before diving into implementation details. This is the opposite of how code is typically taught (starting with syntax) but perfectly suited for code review.

The vocabulary builds quickly. After just two weeks of daily practice, Maria recognized enough Python patterns to hold meaningful conversations with AI assistants about code quality, error handling, and design decisions.

You do not need to understand every line. Even after two weeks, Maria could not write a datetime.strptime() call from memory. But she could read it and understand what it did. That was enough for effective vibe coding.

Questions are your best tool. Maria's most effective technique was asking the AI "What happens if...?" questions. "What happens if the CSV file is missing?" "What happens if a date is in the wrong format?" "What happens if there are duplicate emails?" These questions, informed by her growing ability to read the code, consistently uncovered edge cases and improvements.

What Maria Built Next

Over the following month, Maria used vibe coding to build:

  • A weekly report generator that pulled data from the company's marketing API and formatted it into HTML emails
  • A data validation script that checked new customer uploads for formatting issues before import
  • A simple web dashboard using Flask that displayed key marketing metrics

None of these projects required Maria to write Python from scratch. Each one was built through conversation with AI assistants, with Maria reading and reviewing the generated code, asking questions, and requesting modifications. Her Python reading skills, built on the foundation described in this chapter, made each project possible.

Maria's story demonstrates the core premise of this textbook: vibe coding is accessible to anyone willing to learn to read code and think critically about what AI assistants produce. You do not need years of programming experience. You need curiosity, domain knowledge, and the reading skills this chapter provides.