Home

02 From Copy-Paste to Crafted Prompts: Structuring AI-Assisted Development

ai prompting software-engineering copilot claude-code ai-diary

1. Introduction

Welcome to Week 2. You are in Act I — Experience, the phase of the course where you use AI tools before understanding how they work under the hood. Last week you set up your environment, got a first look at the AI landscape, and were sent off with the Stack Overflow Challenge: recreate a chart of monthly Stack Overflow question counts with AI milestone markers, using Python and an AI coding assistant.

By now you have attempted that challenge. Some of you produced a working plot quickly. Others spent hours debugging hallucinated API endpoints. Most of you landed somewhere in between — a script that almost works, with a few stubborn issues remaining.

All of that is expected. This lecture is about processing that experience and extracting lessons from it.

Here is what we will cover today:

We are not teaching theory today. No transformers, no tokenization, no attention mechanisms. That comes in Act II. Today is about one core insight: the way you interact with an AI tool matters more than which tool you use.


2. Feedback: Your Stack Overflow Challenge Experience

Before diving into new content, we start with a structured feedback session. Please fill out this short quiz about your experience with the Stack Overflow Challenge:

Stack Overflow Challenge — First Attempt Feedback

What we are looking for:

We will discuss the aggregated results briefly in class. Pay attention to the patterns: you will likely find that your struggles were shared by many of your peers.


3. Three Approaches to the Stack Overflow Challenge

Let us revisit the challenge — not to solve it again (yet), but to examine how one might approach it. We will walk through three fundamentally different strategies. Each produces working code. The difference is in how they get there, what breaks along the way, and how easy the result is to maintain and debug.

3.1 The Challenge Recap

Here is the original task as it appeared on the lecture slide:

Your mission: Recreate the Stack Overflow plot using Python + AI tools.

  • Find the data programmatically (Stack Overflow Data Explorer, Stack Exchange API, …)
  • Reproduce the chart: monthly question counts + rolling average
  • Extend it: Overlay markers for key AI breakthroughs from the timeline

This task has several sub-problems that are easy to underestimate:

  1. Data acquisition — Where do you get monthly Stack Overflow question counts? The Stack Exchange API? A data dump? Web scraping?
  2. Data parsing — Monthly aggregation, date handling, missing data periods
  3. Visualization — A time series line plot with proper formatting and a rolling average
  4. Milestone overlay — Adding labeled markers at specific dates for the AI breakthroughs from the course timeline (Transformer 2017, ChatGPT 2022, GPT-4 2023, etc.)
  5. Integration — Making all parts work together in a single runnable script

Now let us see three very different ways to tackle this.


3.2 Approach 1: The Copy-Paste

3.2.1 What You Do

Nothing, really. You take the task description — or even a screenshot of the lecture slide — and paste it directly into GitHub Copilot Chat.

3.2.2 The Prompt

You open Copilot Chat and type something like:

Your mission: Recreate the Stack Overflow plot using Python + AI tools.

- Find the data programmatically (Stack Overflow Data Explorer, Stack Exchange API, ...)
- Reproduce the chart: monthly question counts + rolling average
- Extend it: Overlay markers for key AI breakthroughs from the timeline

Create a Python script that does all of this.

A note on prompt language. You will notice that all prompts in this lecture — and throughout this course — are written in English, even though the course is taught in German. This is a deliberate choice, and it is backed by research.

Current large language models are trained predominantly on English text. GPT-3’s training data was roughly 93% English; LLaMA’s was approximately 90% (Brown et al., 2020; Touvron et al., 2023). This imbalance has a direct consequence: models reason more accurately when prompted in English. Shi et al. (2022) demonstrated that providing chain-of-thought exemplars in English — even when the question is in another language — consistently outperforms native-language prompting across 10 languages. Etxaniz et al. (2023) confirmed this more broadly: translating prompts to English before inference improves results on reasoning tasks, even when the final output needs to be in a different language. Huang et al. (2023) formalized this as “Cross-Lingual-Thought Prompting” (XLT), showing significant gains from routing reasoning through English.

Does the model translate internally? Not literally. There is no hidden translation module. But research on the internal representations of multilingual models shows something interesting: the middle layers of the network — where most of the reasoning happens — operate in a representation space that is heavily English-aligned (Wendler et al., 2024). Non-English inputs are effectively mapped into this English-centric space, processed, and then mapped back to the target language in the final layers. The model does not translate your German prompt to English — but its strongest reasoning pathways were shaped by English data, and English prompts align with those pathways most directly.

The practical recommendation is simple: prompt in English, request output in whatever language you need. The model will respond in German if you ask it to — and it does so by generating German tokens directly in the output layer, not by producing an English draft and translating it. You lose nothing in the output language. You gain measurable quality in reasoning and code generation.

We will revisit this topic with more depth when we cover how tokenization and attention mechanisms work in Act II. For now, take it as an empirically supported best practice: write your prompts in English.

Or perhaps you take a screenshot of the slide and paste the image directly into the chat — no additional text. The AI’s vision capabilities can interpret the task from the image. This is already an interesting observation: you are using multimodal input (image-to-text) without necessarily being aware of it. We will return to how this works when we cover tokenization in Act II.

3.2.3 What Happens

The AI will likely generate a complete Python script in one go — perhaps 50 to 100 lines. It will pick a data source (often the Stack Exchange API), choose a plotting library (matplotlib or plotly), and include the milestone markers. It will look impressively complete.

But here is what commonly goes wrong:

Hallucinated API endpoints. The AI may invent a Stack Exchange API endpoint or response format that does not exist. The URL looks plausible, the parameter names look reasonable, but when you run the script you get a 404 or an unexpected JSON structure.

Hardcoded fake data. Some models, rather than writing code to fetch real data, will generate plausible-looking but fabricated numbers. The chart looks great — the data is just made up.

Monolithic structure. Everything is in one file, often in one function or no functions at all. When something breaks, you cannot tell whether the problem is in the data fetching, the parsing, or the plotting.

Missing dependencies. No requirements.txt, no setup instructions. You get ModuleNotFoundError on the first run.

Incomplete error handling. The script crashes on the first network error with an unhelpful traceback.

What follows is a back-and-forth loop: “This doesn’t work because…“, “Now I get this error…“, “The data looks wrong…“. Each fix introduces new issues. After 10-15 iterations, you have a script that works — but you are not entirely sure why it works, and you have lost track of what changed.

3.2.4 Why This Matters

This approach illustrates several important patterns:

The 80-20 problem. The AI gets you to “looks like it works” in minutes. The remaining 20% — the debugging, the data access issues, the subtle bugs — takes hours. The initial speed is seductive but misleading.

Hallucination surface area. A single, broad prompt gives the AI maximum freedom. With no constraints or structure, the model fills gaps by generating plausible-sounding content. The broader the prompt, the more room for hallucination.

No isolation of failure. When the monolithic script breaks, you cannot tell which part failed. Is it the API call? The date parsing? The plot formatting? Everything is tangled together, and each debugging prompt risks introducing new issues in other parts.

Did you actually understand the code? This is a question worth asking honestly. When the AI generates 80 lines of code in seconds, it is tempting to check whether it runs, see that it produces a plot, and move on. But do you understand what it does? Could you explain why it uses requests.get with those specific parameters? Could you fix the date parsing if it broke in a way the AI cannot help with?

This matters far beyond this exercise. Using code — or entire software artifacts like project structures, requirement documents, commit histories — that you do not fully understand makes that software much harder to maintain. This is true even when you have agentic AI tools that can help with future changes: the agent works better when you can guide it with understanding of the existing design. The point here is not that you must read every line — but that you should be aware that this is the crucial part of AI-assisted software development. The progression from Approach 1 to Approach 3 is also a progression in understanding: the more you structure the prompts, the more you engage with the design, and the more you comprehend what the AI produces.

We will return to this topic in depth when we cover context windows and how LLMs process information (Act II), and again when we discuss the broader challenge of AI systems and memory (Act III). For now, make it a habit: read the code the AI gives you.

A preview of later theory. In Act II, you will learn why this happens: how context windows work, why large undirected prompts lose focus, and the statistical mechanisms behind hallucinations. For now, just notice the pattern.


3.3 Approach 2: The Structured Breakdown

3.3.1 What You Do

Before touching the AI tool, you spend 5 to 10 minutes thinking about the problem. You identify distinct concerns:

  1. Data research — Where does the data come from? What are the options?
  2. Data access — A separate module for fetching and caching data
  3. Visualization — A separate module for plotting
  4. Milestone data — How to source the AI milestone dates?
  5. Integration — A main script that ties everything together

Then you write multiple, focused prompts instead of one big one.

An important note before we look at the prompts. The examples below reflect the thinking of someone who already has experience with APIs, data formats, and Python libraries. Your prompts will look different depending on your background — and that is completely fine. If you have never heard of the Stack Exchange Data Explorer, you would not list it as a data source option. If you are new to Python, you would not specify pandas DataFrame column names.

The point is not to copy these prompts. The point is the strategy: break the problem into parts, research first, implement second. A less experienced developer might write Prompt 1 as simply: “I need monthly Stack Overflow question counts from 2008 to 2024. How can I get this data?” — without listing specific sources. The AI will suggest options, and you learn about SEDE, the API, and data dumps through the AI’s response rather than from prior knowledge. This is equally valid. The research prompt works because it asks the AI to explore options, not because of the specific options you list.

3.3.2 Prompt 1 — Research

I need to get the monthly count of questions posted on Stack Overflow
from 2008 to 2024. What are my options for data sources?

Consider:
- Stack Exchange API
- Stack Exchange Data Explorer (SEDE)
- Data dumps
- Web scraping
- Pre-existing datasets

For each option, list pros, cons, and any rate limits or access
requirements.

This prompt does not ask for code. It asks for research. You are using the AI as an analyst before using it as a coder. This is itself a powerful prompting strategy.

The AI will typically provide a solid comparison. You can then make an informed decision about which data source to use. Perhaps the Stack Exchange Data Explorer (SEDE) offers a direct SQL interface that gives you exactly what you need without pagination headaches.

3.3.3 Prompt 2 — Data Access Module

After choosing a data source (say SEDE), you write a focused implementation prompt:

Write a Python module `data_fetcher.py` that fetches monthly Stack
Overflow question counts using the Stack Exchange Data Explorer.

Requirements:
- Return data as a pandas DataFrame with columns:
  year_month (datetime), question_count (int)
- Cache results locally to a CSV file to avoid repeated queries
- Handle network errors gracefully with retry logic
- Include a main block that demonstrates usage

Notice the specificity: exact column names, exact behavior for caching and errors, exact output format. The AI has much less room to hallucinate when the requirements are precise.

Again, this prompt reflects experience. Specifying pandas DataFrame with exact column names and types is something a developer familiar with the Python data ecosystem would do. If you are newer to Python, your prompt might be: “Write a Python script that downloads monthly Stack Overflow question counts and saves them to a CSV file.” That is a perfectly reasonable prompt — and the AI will likely choose pandas on its own anyway. The key principle is not which libraries you specify, but that you give the prompt a focused, single-responsibility task rather than asking it to do everything at once. In fact, being overly prescriptive can sometimes hurt: if you insist on pandas but the task would be simpler with plain CSV reading, you have constrained the AI away from a potentially better solution.

3.3.4 Prompt 3 — Visualization Module

Write a Python module `plotter.py` with a function
`plot_so_trends(df, milestones)` that:

- Takes a pandas DataFrame (year_month, question_count) and a list
  of milestones as (date, label) tuples
- Creates a time series line plot of monthly question counts
- Adds a 12-month rolling average line
- Overlays vertical markers with labels for each milestone
- Uses matplotlib with a clean, professional style
- Returns the figure object (don't call plt.show())

The same caveat applies here: specifying matplotlib, function signatures, and return behavior assumes familiarity with Python plotting conventions. A less specific prompt — “Write a Python script that creates a line chart of monthly question counts with markers for AI milestones” — would also work. The AI will make library and design choices for you. Whether that is a good thing depends on whether you have an opinion about those choices. As you gain experience, you will naturally become more specific — not because you must, but because you can.

3.3.5 Prompt 4 — Milestone Data

The task description said: “Overlay markers for key AI breakthroughs from the timeline.” The breakthroughs shown on the course timeline are:

These are the events you should mark on the plot — not a list you make up or ask the AI to guess. This is a case where you already have the data. The prompt might be:

I need a Python list of AI breakthrough milestones for my plot.
Here are the events with their dates:

- Transformer: June 2017
- ChatGPT: November 2022
- GPT-4: March 2023
- Cursor & AI IDEs: August 2023
- Claude Code: February 2025
- GitHub Copilot Agent Mode: February 2025

Format this as a list of tuples: (date_string, label), suitable
for passing to a plotting function that draws vertical markers.

Note: with only 6 milestones that you already know, you could also just type them directly into a Python file — no prompt needed. That is a perfectly valid engineering decision, and an important one. Not everything needs to be AI-generated. Knowing when not to use the AI is part of the skill.

3.3.6 Prompt 5 — Integration

Write a `main.py` that imports `data_fetcher` and `plotter`, fetches
the data, loads the milestones, and creates the visualization.

Include an `if __name__ == '__main__'` block that:
1. Fetches or loads cached data
2. Creates the plot
3. Saves it as 'so_trends.png'
4. Shows it in a window

3.3.7 What Is Different

Each module is self-contained and can be tested independently. If the data fetching fails, you know exactly where to look — and fixing it does not risk breaking the plotter. The AI gives better results per prompt because each prompt has a clear, focused scope.

You will still likely encounter some hallucinations, especially in the data access layer. But now they are isolated. A hallucinated API endpoint is a problem in data_fetcher.py, not in your entire project.

3.3.8 Key Takeaways

Separation of concerns pays off immediately. Even without knowing the formal term from software architecture, you can see that isolated modules are easier to debug and improve.

Research before implementation. Using the AI to explore options (Prompt 1) before asking it to write code is a strategy that consistently produces better results.

Prompting is design. The way you decompose your prompts mirrors how you would decompose software. This is not a coincidence — good prompting is a design activity.

Manual work is sometimes the right choice. Typing 6 milestones manually takes 2 minutes. Building a scraper to find them automatically takes 20 minutes and might hallucinate dates. Engineering judgment means choosing the right tool — including no tool.

The AI as collaborator, not magic box. You are directing the work. You decide the architecture, the module boundaries, the data flow. The AI executes the pieces you assign to it.

3.3.9 But Wait — Could This Be One Prompt?

If decomposition is the key insight, an obvious question arises: Can I tell the AI to decompose the problem itself, within a single prompt?

Yes. Consider something like this:

I need a Python project that visualizes monthly Stack Overflow
question counts (2008-2024) with AI milestone markers.

Before writing any code, first:
1. Research available data sources and recommend the best option
2. Design a modular architecture with separate concerns for data
   fetching, visualization, and milestone management
3. For each module, define the interface (inputs, outputs, behavior)

Then implement each module one at a time.

This is a single prompt, but it encodes the thinking process of Approach 2 as instructions. You are asking the AI to do the research, make the design decisions, and then implement — all in sequence, within one conversation turn.

Modern AI models can handle this reasonably well. The result will often be better than Approach 1, because you have given the AI a process to follow rather than a blank canvas. But there are trade-offs worth understanding:

What you gain: Convenience. One prompt instead of five. The AI handles the decomposition for you.

What you lose: Control. The AI decides the module boundaries, the data source, the library choices. If those decisions are wrong, you may not notice until much later — because you did not make them consciously.

The deeper issue: When you decompose the problem in Approach 2, you build the mental model of the solution. You understand why there are separate modules, what each one does, and how they connect. When the AI decomposes for you, it has the mental model — you do not. This matters when something breaks and you need to debug.

This pattern — encoding a multi-step process into a single prompt — is a preview of something we will explore in depth in Act III: agentic workflows. Tools like Claude Code take this idea further: you give a high-level instruction, and the agent autonomously plans, implements, tests, and iterates. The agent is Approach 2 (or even Approach 3), automated.

But here is the key point: you cannot write a good meta-prompt — or effectively supervise an AI agent — without first understanding how to decompose problems yourself. The manual decomposition in Approach 2 is not just a technique. It is a thinking skill that you need regardless of how much the tooling automates. Learn to walk before you run.


3.4 Before We Continue: The Problem with Chat History

Before looking at Approach 3, let us address something you have likely already experienced. In Lecture 1, we introduced the concept of an AI Diary — recording your prompts, the AI’s responses, and what you learned. Let us be honest about what probably happened:

The built-in chat history is better than nothing, but it has significant gaps:

3.5 Why Tracking Matters

The AI Diary is not busywork. It serves three concrete purposes:

Self-reflection. You are learning a new skill — AI-assisted development. Like any skill, improvement requires conscious reflection. What worked? What did not? What would you do differently? Without records, this reflection is based on vague memory.

Future coursework. In Act III, you will analyze your own prompting strategies. You will need data for that — prompt counts, hallucination patterns, iteration trends over weeks. The diary is your dataset.

Professional evidence. By the end of this course, your diary is a portfolio of how you work with AI tools. It shows an employer not just what you built, but how you built it — a much more valuable signal than code alone.

With this motivation in mind, Approach 3 will integrate a solution directly into its workflow — saving prompts as files in the project, committed alongside the code they produce. The full details of format and structure follow after the three approaches comparison.


3.6 Approach 3: The Professional

3.6.1 What You Do

Before any prompting, you decide on a professional development process:

  1. Set up a project with proper packaging — and make it a git repository from the start
  2. Commit each meaningful milestone separately
  3. Define requirements as a document before writing code
  4. Write tests before implementation
  5. Choose infrastructure tooling (uv for dependency management, gh for repository creation)
  6. Track every AI interaction in a diary/ folder — the AI saves a concise record of each prompt cycle alongside the code it produces

This approach embeds the Software Development Lifecycle (SDLC) directly into your prompting strategy. Crucially, both the git workflow and the diary are not afterthoughts — they begin with the very first prompt.

3.6.2 Prompt 0 — Project Setup and Repository

Create a Python project called so-challenge using uv (from Astral)
for dependency management. The project should separate data
collection (data_fetcher.py) from visualization (plotter.py), with
a separate module for milestone definitions (milestones.py).

Set up pytest for testing with corresponding test files for each
module. Manage all dependencies through pyproject.toml — I'll need
pandas, matplotlib, requests, and pytest.

Include a README.md with a brief project description.

Create a diary/ folder for tracking AI interactions. For every
prompt cycle in this project, save the interaction record to this
folder as a numbered markdown file. Use this format:

# NNN — Short Title

**Date**: YYYY-MM-DD
**Tool**: [tool name]
**Model**: [model name]
**Iterations**: [number]

## Prompt

**YYYY-MM-DD HH:MM**

[The full prompt text as given by the user.]

If there were follow-up prompts (corrections, clarifications),
add each as a separate entry with its own timestamp under the
same Prompt section.

Save this initial setup prompt as diary/001-project-setup.md.

Do NOT implement any logic yet — just create the project structure,
configuration, and empty module files with docstrings describing
their purpose.

Initialize a git repository and create an initial commit with this
scaffolding. Then create a GitHub repository using the gh CLI and
push the initial commit.

Notice several things about this prompt.

First, the diary format is specified explicitly — including the markdown structure, timestamp format, and how to handle multiple iterations. If you leave this out, the AI will invent a format, and it will likely be different each time. You must define the format you want, or you will not get it consistently. Within a single chat session, the AI will remember this format for subsequent prompts. But if you start a new session tomorrow, it will have forgotten.

This is exactly the situation where project instruction files become valuable. We will discuss this in detail below, but the core idea is simple: instead of specifying the diary format in every Prompt 0, you write it once in a file that the AI tool reads automatically at the start of every session.

Second, the “Do NOT implement any logic yet” constraint is critical. Without it, the AI will eagerly fill every module with implementation code, defeating the purpose of the multi-step strategy. Controlling what the AI does NOT do is just as important as telling it what to do.

Third, git is part of Prompt 0 — not a separate step at the end. The scaffolding gets its own commit before any code exists. This sets the pattern for the entire workflow: every meaningful change gets committed immediately, and every commit includes its corresponding diary entry.

Fourth, instead of pasting an ASCII directory tree, you describe what you want and why (separation of concerns, testing, dependency management), and let the AI figure out the conventional layout. This is closer to how you would brief a colleague. It also leaves room for the AI to suggest a layout you might not have considered — perhaps it creates a src/ layout, perhaps a flat one. Both are valid, and you can adjust.

As before, the level of detail reflects your experience. A senior developer might write a much terser prompt. A student encountering uv for the first time might need to be more explicit — or might ask the AI to explain the choices it made.

3.6.3 Prompt 1 — Requirements Document

Write a brief requirements specification for this project and save
it to docs/requirements.md.

Include:
- Functional requirements (data source, date range 2008-2024, plot
  type, milestone overlay)
- Non-functional requirements (performance: cache data locally,
  reliability: handle API errors with retries, usability: clear axis
  labels and legend)
- Acceptance criteria for each requirement

Save the diary entry for this interaction and commit everything
together with a meaningful commit message.

This prompt produces a document, not code — and commits it immediately along with its diary entry. Notice that we do not repeat the full diary format here. Prompt 0 already established what a diary entry should contain. The AI carries that context forward within the session. You now have a written specification that you (and the AI) can reference throughout development. The docs/ folder is a conventional home for project documentation, and having the requirements in version control means they evolve alongside the code.

3.6.4 Prompt 2 — Tests First (TDD-Style)

Write pytest tests for `data_fetcher.py`. The module should:
- Fetch monthly SO question counts (2008-2024)
- Return a pandas DataFrame with columns: year_month, question_count
- Cache results locally as CSV
- Handle network errors gracefully

Write the tests BEFORE the implementation. Use unittest.mock to
mock API/network calls in tests. Include tests for:
- Successful data fetch returns correct DataFrame shape
- Cached data is returned without network call
- Network error triggers retry logic

Save the diary entry and commit everything with a proper commit
message describing what was added.

You now have tests that define the expected behavior, committed to the repository alongside their diary entry. The implementation has not been written yet — these tests will fail. That is the point.

3.6.5 Prompt 3 — Implementation Against Tests

Now implement `data_fetcher.py` to pass the tests in
test_data_fetcher.py. The implementation should pass all tests.
Do not modify the tests.

Run the tests to confirm they pass. If they do, save the diary
entry and commit everything with a proper commit message that
describes what was implemented and why.

Since you are in the same chat session, the AI already has the test code in its context window — there is no need to paste it again. The file path is enough for it to know what to implement against. This is a practical advantage of the multi-prompt approach within a single session: each prompt builds on the shared context of everything that came before. The same applies to the diary convention — established once in Prompt 0, carried forward implicitly.

If you did start a fresh session (perhaps the next day), you might need to reference the test file explicitly and remind the AI about the diary convention. Tools like GitHub Copilot can read open files in your editor, and Claude Code can access any file in your project directly. Either way, the tests serve as a precise specification: the AI cannot invent an API endpoint if the tests mock a specific one.

Notice that the prompt says “a proper commit message” rather than dictating a specific message. The AI should derive the message from the actual changes it made — just as a developer would. In a real project, you might even have a commit message template or convention (e.g., conventional commits, a project-specific format). In later weeks, when we discuss project instruction files like CLAUDE.md, we will see how such conventions — and the diary convention — can be defined once and applied automatically. No need to mention them in any prompt at all.

You repeat the Prompt 2 → Prompt 3 pattern for plotter.py and milestones.py. Each cycle produces two commits: tests first, then implementation. Your git log grows into a clear record of the entire development process.

Or, if you are using an agentic tool like Claude Code, you can compress the cycle:

Implement the data_fetcher module with tests. Run the tests. If they
pass, save the diary entry and commit everything with a meaningful
message. Then move on to the plotter module. Follow the same pattern
for each module.

A reference repository demonstrating this full approach will be provided separately.

3.6.6 The Resulting Git History

When you are done, your git log might look like this:

* f4e2a1b Integrate main.py and update README
* c8d3b7e Implement milestones module
* 91a0f5c Add tests for milestones module
* 7b2e4d9 Implement plotter module
* 3f8c1a6 Add tests for plotter module
* a5d9e2f Implement data_fetcher module
* e1c7b3d Add tests for data_fetcher module
* 6d4f8a1 Add requirements specification
* 9c3e8f1 Initial project scaffolding

Each commit tells a story. Each commit corresponds to a prompt. And each commit includes a diary entry in the diary/ folder that records the full interaction. Run git show on any commit and you see both the code changes and the prompt that produced them. The git history combined with the diary files gives you a complete, searchable, diffable record of your entire development process.

3.6.7 What Is Different

Git and diary from the start. The repository and the diary/ folder exist before the first line of code. Every commit includes both the code changes and the diary entry that documents the interaction. This creates an automatic, exact correspondence between prompts and artifacts — the foundation of the AI Diary system we will formalize in the next section.

Tests as hallucination detectors. If you write tests first, hallucinated code fails immediately. There are no silent bugs — the tests catch them before you even look at the output.

Infrastructure matters. A uv project setup with proper packaging means anyone can reproduce your environment. uv sync and everything works. No “it works on my machine” problems.

SDLC embedded in prompting. Requirements, then tests, then implementation, then commit. This is a development lifecycle that you are encoding into your prompt sequence. The AI is not just writing code — it is participating in a structured process.

The AI can manage process, not just code. You can prompt for requirements documents, commit messages, project structures, README files. The AI assists with the entire workflow, not just the implementation.

As with Approach 2, the same experience caveat applies. Knowing about uv, pytest, TDD, and feature branches reflects a developer who has already internalized professional workflows. If these concepts are new to you, your version of Approach 3 might start simpler — perhaps just “set up a project folder with separate files, create a git repo, and write a test before the implementation.” The core principle is the same: let engineering process guide your prompting.

And just as we discussed in Approach 2 — yes, this entire workflow can be compressed into a single prompt or delegated to an AI agent. The Claude Code example above is already doing exactly that. We will explore this extensively in Act III. But the insight remains: you can only write effective agent instructions if you understand the underlying process yourself.

3.6.8 The Problem of Repeating Yourself — and Project Instruction Files

You may have noticed two annoyances in this workflow. First, Prompt 0 contains a long diary format specification. Second, every subsequent prompt includes “save the diary entry and commit.” You have to repeat this because the AI does not remember project conventions across sessions. Within a single chat session, the context is preserved. But the next time you open your editor, start a new session, or switch tools — all of that context is gone.

This is not just a diary problem. Think about all the conventions you might want to encode: commit message format, testing requirements, coding standards, which libraries to prefer, how to handle errors. Specifying all of this in every first prompt of every session is tedious and error-prone.

Project instruction files solve this. They are simple text files that the AI tool reads automatically at the start of every session, before you type your first prompt. The tool treats them as persistent context — as if you had typed those instructions yourself.

Claude Code uses a file called CLAUDE.md in the project root. When you start a Claude Code session in a directory that contains a CLAUDE.md, the tool reads it and follows the instructions. For our SO challenge, a CLAUDE.md might look like this:

# Project Instructions

## AI Diary
For every prompt cycle, save a diary entry to diary/ as a numbered
markdown file. Use this format:

# NNN — Short Title

**Date**: YYYY-MM-DD
**Tool**: [tool name]
**Model**: [model name]
**Iterations**: [number]

## Prompt

**YYYY-MM-DD HH:MM**

[Full prompt text with timestamp for each iteration.]

Always commit the diary entry together with the code changes.

## Conventions
- Run tests before committing implementation code
- Use meaningful commit messages derived from the changes
- Do not modify existing tests when implementing against them

With this file in place, you would not need the diary format in Prompt 0 at all. Every session, every prompt, every tool invocation would automatically follow these conventions.

GitHub Copilot has an equivalent: .github/copilot-instructions.md. Same concept — a markdown file in the repository that Copilot reads to understand project-specific conventions. Cursor uses .cursorrules. The file names differ, but the idea is identical across all major AI coding tools: encode your project conventions once, in a file that lives in the repository, and the tool applies them automatically.

We will not set up these files yet — that comes in Act III when we work with agentic workflows in depth. But now you understand why they exist: they solve the exact repetition problem you just experienced. The structured thinking you practiced in Approach 3 — defining conventions, specifying formats, encoding process — is precisely what goes into these instruction files. You are already learning the skill; the tooling just automates the delivery.

3.6.9 Connection to Later Content

This approach naturally leads into topics we will cover in later weeks:


3.7 Comparing the Three Approaches

The table below summarizes the key differences. A word on the time estimates: these are rough and highly dependent on your experience. A developer who already knows uv, pytest, and git branching will set up Approach 3 in 15 minutes. A student encountering these tools for the first time might need an hour — and that is fine, because the setup time includes learning that pays off on every future task. Similarly, “time to actually works” for Approach 1 might be 30 minutes for someone who is a strong debugger, or an entire afternoon for someone less familiar with Python. The relative pattern (more upfront investment leads to less total debugging) holds across experience levels, but the absolute numbers will vary.

Aspect Approach 1 (Copy-Paste) Approach 2 (Structured) Approach 3 (Professional)
Setup time None Minutes of thinking Significant (tooling + structure)
Number of prompts 1 (+ many fix-ups) 4-6 focused 8-12 systematic
Files produced 1 monolithic script 3-4 modules Full project with tests
Testability None Modules testable individually Tests from the start
Debugging ease Hard — everything tangled Medium — errors localized Easy — tests pinpoint failures
Hallucination risk High — no guardrails Medium — focused scope Low — tests catch them
Reproducibility Low Medium High (uv + git)
Time to “looks done” Fast Medium Slower
Time to “actually works” Slow (debugging) Medium Fast (tests confirm)

3.8 Key Messages

None of these approaches is “wrong.” They represent a progression. Approach 1 is fine for a quick experiment or throwaway script. Approach 2 is appropriate for most tasks. Approach 3 is what you reach for when quality, maintainability, and reproducibility matter. This course will help you move naturally along this spectrum.

This is not about prompting tricks. It is about applying software engineering thinking to AI-assisted development. The prompting strategy follows from the engineering thinking — not the other way around.

The AI does not know what “good” looks like for your project. You need to encode your quality standards into the prompt. Structure, requirements, tests — these are the mechanisms through which you communicate your expectations.

A preview of what is coming. In a few weeks, you will understand why these approaches produce such different results. It has to do with how attention mechanisms work, how context windows process information, and the statistical nature of token generation. For now, notice the pattern: more structure in the prompt leads to better output.


8. The AI Diary: Format and Getting Started

We discussed earlier why chat history is insufficient and why tracking your AI interactions matters. Approach 3 already showed the solution in action: a diary/ folder created in Prompt 0, with every prompt cycle saving a diary entry alongside the code changes it produced.

Now let us formalize the details: what a diary entry looks like, how your project is structured, and how to get started.

8.1 What a Diary Entry Looks Like

# 004 — Data Fetcher Implementation

**Date**: 2026-03-23
**Tool**: Claude Code
**Model**: Claude Opus 4.6
**Iterations**: 2

## Prompt

**2026-03-23 10:25**

Now implement `data_fetcher.py` to pass the tests in
test_data_fetcher.py. The implementation should pass all tests.
Do not modify the tests.

Run the tests to confirm they pass. If they do, save the diary
entry and commit everything with a proper commit message that
describes what was implemented and why.

**2026-03-23 10:32**

The test `test_retries_on_network_error` is failing —
mock_get.call_count is 1 instead of 3. The retry logic calls a
helper function internally so the mock only sees the outer call.
Move the retry loop to call requests.get directly in each attempt
so the mock captures all 3 tries.

This example shows a 2-iteration entry. The first prompt asked for the implementation. It produced code, but one test failed. The follow-up prompt — timestamped 7 minutes later — describes the problem and asks for a fix. Both prompts are recorded as they happened. No summaries, no after-the-fact annotations. The diary is a raw record of the conversation.

For single-iteration entries (where the AI gets it right on the first try), there is simply one timestamped prompt. The format stays the same — just shorter.

8.2 The Resulting Project Structure

so-challenge/
├── src/
│   └── so_challenge/
│       ├── data_fetcher.py
│       ├── plotter.py
│       └── milestones.py
├── tests/
│   └── ...
├── docs/
│   └── requirements.md
├── diary/
│   ├── 001-project-setup.md
│   ├── 002-data-fetcher-tests.md
│   ├── 003-data-fetcher-impl.md
│   ├── 004-plotter-tests.md
│   ├── 005-plotter-impl.md
│   ├── week-02-reflection.md
│   └── ...
└── pyproject.toml

Each numbered diary entry corresponds to a prompt cycle. Because each diary entry was committed together with the code it produced, git show on any commit reveals both the changes and the interaction that caused them. This is the exact correspondence between prompts and artifacts that chat history fundamentally cannot provide.

Notice that most of the bookkeeping happened automatically. The AI knows which model it is, what prompt it received, how many iterations you went through. It writes the diary entry as part of its task. The only thing you add manually — if you choose to — is a brief observation in the “What I Used” or “Observations” section. The overhead is minimal because the AI does the work.

A word of honesty about commit granularity. The one-commit-per-prompt strategy we use here is a learning tool, not a universal best practice. By committing after every AI interaction, you create a fine-grained record that lets you study your own prompting behavior — which prompts worked, which led to dead ends, how many iterations a task required. That traceability is the entire point for this course.

But recognize what this implies: you are imposing a commit strategy on your project that is driven by your learning goals, not by the project’s conventions. In a real team, you might be working on a feature branch where the convention is to squash all commits into one clean merge commit. Or the team might enforce conventional commit messages (feat:, fix:, refactor:) where “diary entry for prompt 3” does not fit. Your diary commits and the project’s git workflow may conflict.

For now, in this course, the learning value takes priority — commit after every meaningful AI interaction. But as you gain experience, you will find your own position on a spectrum: from strict (commit after every single prompt, including trivial ones) to relaxed (work across many prompts, commit at meaningful milestones, record interactions in a separate diary file). In Lecture 3 we will revisit this tension when we encounter real project conventions that our diary strategy must coexist with. The important principle is not the exact commit frequency — it is that the correspondence between your prompts and their artifacts exists in some reviewable form.

8.3 Why Version-Control Your Prompts?

You might find it odd to commit prompt files to a repository. In a production project at a company, you probably would not do this. But this is not a production project — this is a course about building AI competence.

Prompts are engineering artifacts. They are the instructions that produced your code. In this course, learning to write good prompts is as important as learning to write good code. If you would not throw away your source code, why throw away the instructions that created it?

Version-controlling your prompts lets you:

8.4 A Note on Lighter Approaches

Not everyone will use Approach 3 from day one. If you are still working in a simpler style (closer to Approach 1 or 2), you can still keep a diary — just save your prompts manually to the diary/ folder after each session. The key minimum: for each AI interaction, record the prompt, the tool/model, and one observation. The format matters less than the habit.

8.5 Getting Started Right Now

If you are in the lecture room, take 5 minutes now:

  1. Create a diary/ folder in your challenge repository
  2. Write your first diary entry — a brief retrospective on your first Stack Overflow Challenge attempt. What prompts did you use? What worked, what did not?
  3. If you remember any hallucinations or “aha moments” — write them down now, before they fade

This first entry does not need to be polished. It just needs to exist.


10. Expanding Your Toolbox: GitHub Copilot and Claude Code in VS Code

In this course, we use two AI coding assistants, both through their VS Code extensions:

Both live inside VS Code. Both accept natural-language prompts. In their respective Agent modes, both can read and write files across your project, run terminal commands, execute tests, make git commits, and even create GitHub repositories via gh CLI. The capabilities have converged significantly — both tools are full agentic coding assistants when used in Agent mode. The differences lie in their underlying models, ecosystem integration, and how they handle context.

10.1 How They Compare

Aspect GitHub Copilot Claude Code
Interface in VS Code Chat panel + inline completions Terminal panel (VS Code extension) + standalone CLI
File access (Agent mode) Reads and writes any file in workspace Reads and writes any file in project
Run terminal commands Yes — shell commands, tests, builds Yes — shell commands, tests, builds
Git operations Yes — via terminal (git, gh CLI) Yes — via terminal (git, gh CLI)
Iterative test-fix loops Yes — runs tests, reads output, fixes, re-runs Yes — runs tests, reads output, fixes, re-runs
Models available GPT-4o, Claude Sonnet, Gemini (user-selectable) Claude Opus, Claude Sonnet (Anthropic models only)
Context window Varies by model (typically 128K–200K tokens) Up to 1M tokens (Opus)
Custom instructions .github/copilot-instructions.md CLAUDE.md
Pricing for students Free (GitHub Education) Free tier available (limited daily usage)

As you can see, the core capabilities are nearly identical. Both tools can do everything Approach 3 requires: create project scaffolding, write files, run tests, make commits, push to GitHub. The real differences are in the models (Copilot offers multi-vendor choice, Claude Code has access to Opus with its larger context window) and ecosystem integration (Copilot is more tightly integrated with GitHub’s issue and PR workflows).

In practice, you will develop a feel for which tool you prefer for which tasks. Some people prefer Copilot’s inline completions for small edits and Claude Code for larger multi-file tasks. Others use one tool for everything. Both are valid — and documenting your tool preferences in the AI Diary is itself a valuable learning exercise.

10.2 Interaction Modes: Ask, Agent/Edit, and Plan

When you open either tool in VS Code, you will notice a mode selector at the top of the chat. Both tools offer three modes with similar names but slightly different behavior:

Mode GitHub Copilot Claude Code
Ask Ask — answers questions, explains code, suggests changes but does not edit files directly Ask — same concept: answers questions without making changes. Prompts before every file edit or command
Agent / Edit Agent — can edit files, run terminal commands, and make multi-file changes. You review and accept each change Edit automatically — edits files without asking for each one, but still prompts before running shell commands
Plan Plan (via /plan) — outlines a multi-step approach before executing. You approve the plan, then it executes Plan — creates a detailed implementation plan. No code changes until you approve. Then switches to execution

The terminology differs slightly — Copilot calls its active mode “Agent,” while Claude Code calls it “Edit automatically” — but the underlying idea is the same: how much autonomy do you give the AI?

We will explore what drives these differences — context windows, tool use, and agentic architectures — in Act II and Act III. For now, start by experimenting with all three modes and notice how they feel different.

10.3 Configuring Permissions: Teaching the AI What It May Do

When you first use Claude Code in “Edit automatically” mode, you will notice it still asks for permission before running certain commands — git operations, package installations, test runners. This is a safety feature: the AI wants confirmation before taking actions that affect your system.

For repeated workflows (like our Approach 3 pattern of test → implement → commit → push), these prompts become tedious. Both tools allow you to configure permission rules so trusted operations run without asking.

Claude Code uses a JSON settings file. You have two levels:

User-level (~/.claude/settings.json) — applies to all your projects:

{
  "permissions": {
    "allow": [
      "Edit",
      "Write",
      "Bash(git:*)",
      "Bash(gh:*)",
      "Bash(uv:*)",
      "Bash(npm:*)"
    ]
  }
}

This says: allow file edits, file writes, and any command starting with git, gh, uv, or npm — without prompting. The * is a wildcard, so Bash(git:*) matches git add, git commit, git push, and so on.

Project-level (.claude/settings.json in your repo) — applies to everyone working on this project. This is where team-wide conventions go. It can also be used to deny dangerous operations:

{
  "permissions": {
    "deny": ["Bash(rm -rf:*)"]
  }
}

There is also .claude/settings.local.json (gitignored) for personal overrides within a project.

GitHub Copilot handles this differently. In VS Code, Copilot’s Agent mode uses the VS Code trust model — when you open a workspace, VS Code asks whether you trust the folder. Within a trusted workspace, Copilot Agent can run terminal commands. There is no equivalent of Claude Code’s granular per-command allowlist. Instead, Copilot shows a confirmation dialog for each terminal command, and you click “Allow” or “Always Allow” for that specific command pattern.

For both tools: start with the default (ask for everything), and add permissions as you discover which commands you run repeatedly. This is a good practice — you learn what the tool does before giving it a longer leash.

10.4 Project Instruction Files Revisited

We discussed CLAUDE.md and .github/copilot-instructions.md earlier in the context of the AI Diary. These files serve a broader purpose: any convention you want the AI to follow automatically goes here.

For Claude Code, CLAUDE.md is read at the start of every session. For Copilot, .github/copilot-instructions.md serves the same role. The settings files above control permissions (what the tool may do). The instruction files control behavior (how the tool should do it).

We will set these up properly in Act III. For now, just know they exist — and that the repetition you experienced in Approach 3 (“save the diary entry and commit”) is exactly what they eliminate.

10.5 Setting Up Claude Code in VS Code

  1. Install the extension: Open VS Code, go to the Extensions panel, search for “Claude Code” and install it.

  2. Create an account: If you do not have an Anthropic account, create one. The free tier provides a limited number of messages per day — enough for experimentation and coursework.

  3. Try a simple task: Open the Claude Code panel and give it a small, well-defined task. For example:

Create a Python script that prints "Hello from Claude Code" and
saves the current date and time to a file called 'timestamp.txt'.

Watch how it works: it will read your project, create the file, and potentially run it to verify — all autonomously.

  1. Compare the experience: Try the same task in Copilot Agent mode. Both tools can create the file and run it — notice the differences in interface (chat panel vs. terminal panel), how they show proposed changes, and how they handle the execution step.

There is no student discount for Claude Code, but the free tier provides meaningful value for the tasks in this course. If you hit the daily limit, switch to Copilot for the remainder of that day — and note the switch in your diary.

10.6 When to Use Which

Both tools can handle almost any task. Over time you will develop your own preferences. Here are some starting observations — not rules:

The best way to learn which tool fits which task is to try both and compare. Document what you find in your AI Diary — these observations are exactly the kind of learning the diary is designed to capture.


11. The Stack Overflow Challenge — Round 2

You have now seen three fundamentally different approaches to the same challenge. You have a structured AI Diary format. You have a second AI tool available. It is time to try again.

11.1 Your Task

The challenge is the same as in Lecture 1:

Your mission: Recreate the Stack Overflow plot using Python + AI tools.

  • Find the data programmatically (Stack Overflow Data Explorer, Stack Exchange API, …)
  • Reproduce the chart: monthly question counts + rolling average
  • Extend it: Overlay markers for key AI breakthroughs from the timeline

11.2 What Is Different This Time

The task is identical. Your approach should not be.

This time, use Approach 3 — the professional SDLC workflow with integrated diary. Specifically:

  1. Set up a proper project with uv, separate modules, and a diary/ folder — exactly as shown in Prompt 0 of Approach 3.

  2. Follow the prompt sequence: requirements first, then tests, then implementation, then integration. Commit after each step, with a diary entry alongside each commit.

  3. You may use GitHub Copilot, Claude Code, or both. If you use both, note in your diary entries which tool you used for which prompt cycle.

  4. Focus on process, not just the result. A working plot with no commit history and no diary is worth less than a partially working plot with a clean git log and complete diary entries. We are assessing how you work, not just what you produce.

11.3 Reference Repository

A complete example of this workflow is available at: github.com/l3gj0n/so-challenge

Browse its git log to see the expected commit structure. Look at the diary/ folder to see what diary entries look like — including one with a multi-iteration correction (004-data-fetcher-impl.md). Your repository should follow the same pattern, though your code, prompts, and diary entries will of course be different.

11.4 Deliverables

11.5 Timeline

Start during the lecture session if time permits. Complete as take-home work by the start of Lecture 3.


12. Post-Challenge Feedback

After completing the second attempt at the Stack Overflow Challenge, you will fill out a second feedback quiz. This one is shorter and focused on comparing your two attempts.

We will compare the aggregated results from both feedback rounds to see how the class evolved between attempt 1 and attempt 2. Key questions include: Did your approach change? Did your iteration count go down? What was the single most important thing you learned?

The results will be discussed at the start of Lecture 3.

© 2026 Dominik Müller   •  Powered by Soopr   •  Theme  Moonwalk