{ "cells": [ { "cell_type": "raw", "id": "bb8491c0", "metadata": {}, "source": [ "---\n", "title: \"Notebook 8: Software Engineering โ€” SDLC, Git & AI-Assisted Development\"\n", "subtitle: \"COMP 1150 โ€” Computer Science Concepts\"\n", "author: \"Brendan Shea, PhD\"\n", "date: last-modified\n", "---" ] }, { "cell_type": "markdown", "id": "cd2a5292", "metadata": {}, "source": [ "\n", "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/brendanpshea/computing_concepts_python/blob/main/v2/notebooks/COMP1150_NB08_SoftwareEngineering.ipynb)\n", "[Download .ipynb](https://raw.githubusercontent.com/brendanpshea/computing_concepts_python/main/v2/notebooks/COMP1150_NB08_SoftwareEngineering.ipynb) ยท [View on GitHub](https://github.com/brendanpshea/computing_concepts_python/blob/main/v2/notebooks/COMP1150_NB08_SoftwareEngineering.ipynb)\n" ] }, { "cell_type": "markdown", "id": "adaafbbf", "metadata": {}, "source": [ "๐Ÿ“บ **Lecture video:** *(link coming soon)*" ] }, { "cell_type": "markdown", "id": "cadad58e", "metadata": {}, "source": [ "## Learning Outcomes\n", "\n", "By the end of this notebook, you will be able to:\n", "\n", "- **Describe** the stages of the **software development life cycle (SDLC)** and explain why teams plan before they type.\n", "- **Write** a clear **requirement** and **user story** for a feature.\n", "- **Use** **git** to track changes, read a project's history, and recover an earlier version of your work โ€” and **explain** how git is really used day-to-day through GitHub and your code editor.\n", "- **Write** simple **tests** with `assert` to check that code is *correct*, not just that it *runs*.\n", "- **Review** AI-generated code critically โ€” finding bugs and judgment problems *before* they ship.\n", "- **Explain** how software is actually built in 2026, when AI writes much of the code, and **discuss** who is responsible for it.\n", "\n", "*Maps to course LOs: 5, 13*" ] }, { "cell_type": "markdown", "id": "f81e8e7c", "metadata": {}, "source": [ "## The Problem: A Wrong Dose Ships to Emerald City Hospital\n", "\n", "It is Dorothy's first week as a junior developer at **Emerald City Hospital**. The hospital runs its own software, **MedTrack**, which records the medications each patient receives.\n", "\n", "On Tuesday, a nurse reports that MedTrack logged a dose of **5.0 mg** for a patient who should have received **0.5 mg**. Nobody typed the wrong number โ€” the *program* did it. The code \"worked\": it ran without crashing, it printed a number, it saved to the file. It was just **wrong**.\n", "\n", "How does a single bad line of code make it all the way to a real patient? And what do professional teams do to stop it? That question is what this whole notebook is about.\n", "\n", "> Writing code that *runs* is only a small part of software engineering. The hard part is making sure it is **correct**, that you can **undo** mistakes, and that a **human stays responsible** โ€” especially now that AI writes so much of the code." ] }, { "cell_type": "markdown", "id": "5a5709a9", "metadata": {}, "source": [ "### The Roadmap\n", "\n", "This notebook has four parts. Together they tell the story of how a feature goes from idea to something safe enough to ship.\n", "\n", "- **The SDLC** โ€” the stages every project moves through, the two rhythms teams use to move through them (**waterfall vs. agile**), and why we don't just start typing.\n", "- **Git & GitHub** โ€” how teams track every change, work in parallel, and recover from mistakes.\n", "- **Testing** โ€” how we check that code is *correct*, not just that it runs.\n", "- **AI-Assisted Development** โ€” how code actually gets written in 2026, and why everything before it matters *more*, not less, when AI writes the first draft.\n", "\n", "Our running example is **MedTrack**, the patient-medication tracker." ] }, { "cell_type": "markdown", "id": "831de03a", "metadata": {}, "source": [ "## The Software Development Life Cycle\n", "\n", "The Scarecrow wants a brain โ€” and on a software team, \"using your brain\" mostly means **thinking before you build**. Rushing straight to code is how a hospital ends up with a 5.0 mg bug.\n", "\n", "Professionals describe the journey of any software project as the **software development life cycle (SDLC)** โ€” a sequence of stages a project moves through:\n", "\n", "1. **Requirements** โ€” figure out *what* the software must do, and for whom.\n", "2. **Design** โ€” decide *how* it will work: the structure, the pieces, the data.\n", "3. **Build** โ€” actually write the code (this is the part beginners think is the *whole* job).\n", "4. **Test** โ€” check that it does what the requirements said, *correctly*.\n", "5. **Deploy** โ€” release it so real people (nurses, patients) can use it.\n", "6. **Maintain** โ€” fix bugs, add features, adapt as needs change.\n", "\n", "The 5.0 mg dose bug is a **requirements-and-test failure** wearing a costume: nobody clearly wrote down the rules for a valid dose, and nobody tested for a bad one." ] }, { "cell_type": "markdown", "id": "bbc12697", "metadata": {}, "source": [ "### Picture It: The Cycle\n", "\n", "It's called a *life cycle*, not a *life line*, because software is never really \"done.\" Maintenance feeds new requirements, and the loop starts again. A bug caught at **Requirements** costs a sentence to fix; the *same* bug caught at **Deploy** โ€” in a patient's chart โ€” can cost far more." ] }, { "cell_type": "code", "execution_count": 1, "id": "83cbe72b", "metadata": { "execution": { "iopub.execute_input": "2026-06-10T12:51:35.309750Z", "iopub.status.busy": "2026-06-10T12:51:35.309369Z", "iopub.status.idle": "2026-06-10T12:51:36.256673Z", "shell.execute_reply": "2026-06-10T12:51:36.255442Z" } }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "Requirements\n", "\n", "Requirements\n", "\n", "\n", "\n", "Design\n", "\n", "Design\n", "\n", "\n", "\n", "Requirements->Design\n", "\n", "\n", "\n", "\n", "\n", "Build\n", "\n", "Build\n", "\n", "\n", "\n", "Design->Build\n", "\n", "\n", "\n", "\n", "\n", "Test\n", "\n", "Test\n", "\n", "\n", "\n", "Build->Test\n", "\n", "\n", "\n", "\n", "\n", "Deploy\n", "\n", "Deploy\n", "\n", "\n", "\n", "Test->Deploy\n", "\n", "\n", "\n", "\n", "\n", "Maintain\n", "\n", "Maintain\n", "\n", "\n", "\n", "Deploy->Maintain\n", "\n", "\n", "\n", "\n", "\n", "Maintain->Requirements\n", "\n", "\n", "  new needs &\n", "  bug reports\n", "\n", "\n", "\n" ], "text/plain": [ "" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "#| echo: false\n", "#@title ๐Ÿ”„ The SDLC cycle โ€” diagram code (click to show)\n", "from graphviz import Digraph\n", "\n", "g = Digraph()\n", "g.attr(rankdir=\"LR\")\n", "g.attr(\"node\", shape=\"box\", style=\"filled,rounded\", fillcolor=\"#e3f2fd\",\n", " fontname=\"Helvetica\")\n", "stages = [\"Requirements\", \"Design\", \"Build\", \"Test\", \"Deploy\"]\n", "for s in stages:\n", " g.node(s, s)\n", "for a, b in zip(stages, stages[1:]):\n", " g.edge(a, b)\n", "g.node(\"Maintain\", \"Maintain\", fillcolor=\"#fff3e0\")\n", "g.edge(\"Deploy\", \"Maintain\")\n", "g.edge(\"Maintain\", \"Requirements\", label=\" new needs &\\n bug reports\",\n", " constraint=\"false\", style=\"dashed\")\n", "g" ] }, { "cell_type": "markdown", "id": "8b2912a2", "metadata": {}, "source": [ "### Waterfall vs. Agile: Two Ways Around the Loop\n", "\n", "Knowing the *stages* doesn't settle the biggest practical question: how should a team move *through* them? Two classic answers dominate software history.\n", "\n", "**Waterfall** takes the list literally โ€” finish each stage completely before starting the next. Gather *all* the requirements and sign them off; design *everything*; then build; then test; then deploy. One long pass, like water flowing down a series of falls, with no swimming back up. It feels wonderfully organized. Its famous weakness: you find out whether you built the *right thing* only at the very end, when changing course is most expensive. If the nurses first see MedTrack a year in and say \"that's not how we record doses at all,\" that's a year of work aimed at the wrong target.\n", "\n", "**Agile** runs the same stages as *many small loops* instead of one big pass. The team builds a thin slice of working software in a short cycle called a **sprint** (commonly one to four weeks), puts it in front of real users, and lets what they learn *change the requirements* before the next loop. Working software early and often; course corrections while they're still cheap. A bare-bones dose-entry screen that nurses try in week two could surface the 0.5-vs-5.0 confusion before it ever reached a patient.\n", "\n", "Neither approach is simply \"right\":\n", "\n", "- **Waterfall still fits** when the requirements truly are fixed and changing the product later is enormously costly โ€” firmware burned into a heart monitor, or software a regulator must approve as one complete, documented package.\n", "- **Agile fits** the far more common case: nobody fully knows the requirements until users touch the software.\n", "- **Most real teams run something agile-ish** โ€” short cycles and feedback at the core, plus as much up-front planning and documentation as the project's risk demands. Hospital software like MedTrack needs both: sprints *and* a paper trail.\n", "\n", "Keep this idea in your pocket โ€” a tight loop of *build a little, check it, adjust*. It comes back twice in this notebook: as the test-fix-retest rhythm of the testing section, and again at high speed when an AI writes the first draft." ] }, { "cell_type": "markdown", "id": "72c38921", "metadata": {}, "source": [ "### Picture It: One Pass vs. Many Loops\n", "\n", "The same stages, two rhythms. Waterfall crosses the river in one giant leap; agile crosses on stepping stones, checking its footing at each one." ] }, { "cell_type": "code", "execution_count": 2, "id": "70735e77", "metadata": { "execution": { "iopub.execute_input": "2026-06-10T12:51:36.261562Z", "iopub.status.busy": "2026-06-10T12:51:36.261040Z", "iopub.status.idle": "2026-06-10T12:51:37.243090Z", "shell.execute_reply": "2026-06-10T12:51:37.241229Z" } }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "cluster_a\n", "\n", "Agile: many small loops (sprints)\n", "\n", "\n", "cluster_w\n", "\n", "Waterfall: one long pass\n", "\n", "\n", "\n", "s1\n", "\n", "Sprint 1\n", "plan ยท build ยท test ยท show\n", "\n", "\n", "\n", "s2\n", "\n", "Sprint 2\n", "plan ยท build ยท test ยท show\n", "\n", "\n", "\n", "s1->s2\n", "\n", "\n", "  feedback\n", "\n", "\n", "\n", "s3\n", "\n", "Sprint 3\n", "plan ยท build ยท test ยท show\n", "\n", "\n", "\n", "s2->s3\n", "\n", "\n", "  feedback\n", "\n", "\n", "\n", "w_Requirements\n", "\n", "Requirements\n", "\n", "\n", "\n", "w_Design\n", "\n", "Design\n", "\n", "\n", "\n", "w_Requirements->w_Design\n", "\n", "\n", "\n", "\n", "\n", "w_Build\n", "\n", "Build\n", "\n", "\n", "\n", "w_Design->w_Build\n", "\n", "\n", "\n", "\n", "\n", "w_Test\n", "\n", "Test\n", "\n", "\n", "\n", "w_Build->w_Test\n", "\n", "\n", "\n", "\n", "\n", "w_Deploy\n", "\n", "Deploy\n", "\n", "\n", "\n", "w_Test->w_Deploy\n", "\n", "\n", "\n", "\n", "\n" ], "text/plain": [ "" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "#| echo: false\n", "#@title ๐ŸŒŠ Waterfall vs. agile โ€” diagram code (click to show)\n", "from graphviz import Digraph\n", "\n", "g = Digraph()\n", "g.attr(rankdir=\"LR\", fontname=\"Helvetica\")\n", "g.attr(\"node\", shape=\"box\", style=\"filled,rounded\", fontname=\"Helvetica\")\n", "\n", "with g.subgraph(name=\"cluster_a\") as a:\n", " a.attr(label=\"Agile: many small loops (sprints)\", fontname=\"Helvetica\",\n", " style=\"rounded\", color=\"#a5d6a7\")\n", " a.attr(\"node\", fillcolor=\"#c8e6c9\")\n", " for n in [\"s1\", \"s2\", \"s3\"]:\n", " a.node(n, \"Sprint \" + n[1] + \"\\nplan ยท build ยท test ยท show\")\n", " a.edge(\"s1\", \"s2\", label=\" feedback\")\n", " a.edge(\"s2\", \"s3\", label=\" feedback\")\n", "\n", "with g.subgraph(name=\"cluster_w\") as w:\n", " w.attr(label=\"Waterfall: one long pass\", fontname=\"Helvetica\",\n", " style=\"rounded\", color=\"#90caf9\")\n", " w.attr(\"node\", fillcolor=\"#e3f2fd\")\n", " stages = [\"Requirements\", \"Design\", \"Build\", \"Test\", \"Deploy\"]\n", " for s in stages:\n", " w.node(\"w_\" + s, s)\n", " for a, b in zip(stages, stages[1:]):\n", " w.edge(\"w_\" + a, \"w_\" + b)\n", "\n", "g" ] }, { "cell_type": "markdown", "id": "1b2f4d44", "metadata": {}, "source": [ "### Requirements: Turning a Wish Into a Spec\n", "\n", "\"Track medications\" is a *wish*, not a plan. A **requirement** is a precise, testable statement of what the software must do. Teams often write requirements as **user stories**, which keep the focus on *who* needs *what* and *why*.\n", "\n", "A user story follows a simple shape โ€” fill in the three blanks:\n", "\n", "```\n", "As a ,\n", "I want ,\n", "so that .\n", "```\n", "\n", "Compare a vague wish with a precise requirement for MedTrack:\n", "\n", "- โŒ **Vague:** \"The app should handle doses.\"\n", "- โœ… **Precise:** \"A recorded dose must be a positive number between 0.01 and 1000 mg. If it falls outside that range, the app must reject it and show an error instead of saving it.\"\n", "\n", "That second version is something you can actually *build* and *test*. The vague one is how 5.0 mg slips through." ] }, { "cell_type": "markdown", "id": "8de5d6d8", "metadata": {}, "source": [ "### โœ๏ธ Your Turn โ€” Write a Requirement\n", "\n", "Pick one feature MedTrack might need (for example: searching for a patient, recording who gave a dose, or warning about a drug interaction).\n", "\n", "In the cell below, fill in:\n", "\n", "1. **One user story** using the `As a... I want... so that...` shape.\n", "2. **One precise, testable requirement** for that feature โ€” specific enough that someone could later check whether the code obeys it.\n", "\n", "There's no autograder here; just edit the text." ] }, { "cell_type": "markdown", "id": "63db5b75", "metadata": {}, "source": [ "> **โœ๏ธ Write your answer here.** Double-click this cell to edit it, fill in the blanks, then press **Shift+Enter** to render it. (This is writing, not code โ€” no `print` needed.)\n", ">\n", "> **User story**\n", "> As a \\_\\_\\_,\n", "> I want \\_\\_\\_,\n", "> so that \\_\\_\\_.\n", ">\n", "> **Precise, testable requirement**\n", "> \\_\\_\\_" ] }, { "cell_type": "markdown", "id": "1323d606", "metadata": {}, "source": [ "### ๐Ÿ’ญ Think About It โ€” Skipping the Plan\n", "\n", "Beginners (and rushed teams) love to skip straight to the **Build** stage โ€” it feels productive to be typing code.\n", "\n", "1. Why might jumping straight to writing code *feel* faster but actually end up *slower* for a real project?\n", "2. Think of a non-software project you've done where poor planning caused problems later (an essay, a road trip, an event). What did the \"requirements\" stage look like โ€” or what happened because there wasn't one?\n", "3. In a hospital, who *besides programmers* should have a say in the requirements for MedTrack? Why does that matter?\n", "4. MedTrack is hospital software: mistakes can reach patients, and regulators may demand documented evidence before anything ships. Make the best case you can for running this project **waterfall**, then the best case for **agile**. Which would you actually choose โ€” and what would you borrow from the other approach?" ] }, { "cell_type": "markdown", "id": "0b5e0dcc", "metadata": {}, "source": [ "## The Cowardly Lion's Problem\n", "\n", "The Cowardly Lion is afraid to touch MedTrack's code. What if he changes something and breaks the whole thing? What if he can't get back to the version that worked?\n", "\n", "This fear is completely reasonable โ€” and **version control** is the cure. A version-control tool records the full history of a project: every change, who made it, when, and why. If today's edit breaks something, you can look back, compare, and return to any earlier version. The Lion can finally experiment without dread.\n", "\n", "The standard tool for this, used almost everywhere, is **git**." ] }, { "cell_type": "markdown", "id": "37290177", "metadata": {}, "source": [ "## What Version Control Is\n", "\n", "A **version-control system** records the full history of a project: every change, who made it, when, and *why*. Think of it as an unlimited, organized \"undo\" that works across the whole project and never forgets.\n", "\n", "The core idea is the **commit** โ€” a saved **snapshot** of the project at one moment, with a short message describing the change (\"Reject doses outside the safe range\"). String the commits together and you get the project's **history**: a timeline you can read, compare, and **return to**. Made a mess today? You can look back at yesterday's snapshot and restore it. That's what finally lets the Cowardly Lion experiment without fear.\n", "\n", "The standard tool for this โ€” used almost everywhere โ€” is **git**. The most popular place to store git projects online, so a whole team can share them, is **GitHub**." ] }, { "cell_type": "markdown", "id": "0bd29206", "metadata": {}, "source": [ "### Picture It: The Three Areas\n", "\n", "When you save work with git, a change moves through three places: the **working directory** (the files you edit), the **staging area** (the changes you've chosen to include next), and the **repository** (the permanent history of snapshots). The diagram below shows that one-way flow." ] }, { "cell_type": "code", "execution_count": 3, "id": "f9c05be4", "metadata": { "execution": { "iopub.execute_input": "2026-06-10T12:51:37.247388Z", "iopub.status.busy": "2026-06-10T12:51:37.246997Z", "iopub.status.idle": "2026-06-10T12:51:38.151404Z", "shell.execute_reply": "2026-06-10T12:51:38.149872Z" } }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "W\n", "\n", "Working directory\n", "(your files)\n", "\n", "\n", "\n", "S\n", "\n", "Staging area\n", "(chosen changes)\n", "\n", "\n", "\n", "W->S\n", "\n", "\n", "  git add\n", "\n", "\n", "\n", "R\n", "\n", "Repository\n", "(saved history)\n", "\n", "\n", "\n", "S->R\n", "\n", "\n", "  git commit\n", "\n", "\n", "\n" ], "text/plain": [ "" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "#| echo: false\n", "#@title ๐Ÿ—‚๏ธ Git's three areas โ€” diagram code (click to show)\n", "from graphviz import Digraph\n", "\n", "g = Digraph()\n", "g.attr(rankdir=\"LR\")\n", "g.attr(\"node\", shape=\"box\", style=\"filled\", fillcolor=\"#e8f5e9\",\n", " fontname=\"Helvetica\")\n", "g.node(\"W\", \"Working directory\\n(your files)\")\n", "g.node(\"S\", \"Staging area\\n(chosen changes)\")\n", "g.node(\"R\", \"Repository\\n(saved history)\")\n", "g.edge(\"W\", \"S\", label=\" git add\")\n", "g.edge(\"S\", \"R\", label=\" git commit\")\n", "g" ] }, { "cell_type": "markdown", "id": "4d1452d5", "metadata": {}, "source": [ "### The Commands Under the Hood\n", "\n", "You won't run these in this notebook โ€” in a moment you'll use GitHub's website instead, which is friendlier. But it's worth seeing the actual commands once, because every button you'll click is secretly running one of them. The basic rhythm for saving work is three commands:\n", "\n", "```\n", "git init # start tracking the current folder as a repo\n", "git add # stage the changes you want in the next snapshot\n", "git commit -m \"\" # save the snapshot, with a note explaining it\n", "```\n", "\n", "To look back, you'd use `git log` to read the history and `git show` (or `git checkout`) to view or restore an earlier snapshot. The message on each commit matters: *\"fixed the dose validation bug\"* tells a future teammate (or future you) **why** the change happened โ€” *\"stuff\"* tells them nothing.\n", "\n", "So when GitHub later shows you a \"commit\" or a list of past versions, it's not magic โ€” it's exactly these commands wearing a nicer outfit." ] }, { "cell_type": "markdown", "id": "576ec28a", "metadata": {}, "source": [ "### Recovering an Earlier Version\n", "\n", "Here's the payoff that cures the Lion's fear. Because *every* commit is kept, you can always look at โ€” or restore โ€” any earlier version of any file, even one from weeks ago. If today's change breaks MedTrack, you don't panic: you open the history, find the last snapshot that worked, and bring it back. Nothing good is ever truly lost once it's been committed." ] }, { "cell_type": "markdown", "id": "94b0ce20", "metadata": {}, "source": [ "### How You'll *Really* Use Git: GitHub & Your Editor\n", "\n", "Here's the honest truth about working life: **most developers rarely type those commands by hand.** Git almost always runs underneath friendlier tools:\n", "\n", "- **GitHub** (and GitLab, Bitbucket) โ€” websites that store the shared, official copy of a project online so a whole team can collaborate. This is also where **code review** happens โ€” and it's what you'll set up in this notebook's exercise.\n", "- **Your code editor / IDE** โ€” editors like **VS Code** have a built-in \"Source Control\" panel with buttons for the common actions, so you click instead of type.\n", "- **GitHub Desktop** โ€” a standalone app that wraps git in a simple window.\n", "\n", "You learned the commands above for the same reason you learn arithmetic before trusting a calculator: when something goes wrong (and with git, eventually it will), the people who understand what's *actually happening* are the ones who can fix it." ] }, { "cell_type": "markdown", "id": "52e24e79", "metadata": {}, "source": [ "### Branches, Pull Requests & Code Review\n", "\n", "Teams add three more ideas on top of commits. You don't need to run them now, but you'll meet them on any real project:\n", "\n", "- **Branch** โ€” a separate, safe copy of the project's timeline where you build a feature *without* touching the working version everyone depends on. The Lion can experiment on a branch and break nothing real.\n", "- **Pull request (PR)** โ€” when the feature is ready, you open a pull request: a formal \"please review and merge my changes\" proposal.\n", "- **Code review** โ€” before the PR is merged, a teammate reads it. Glinda, the senior engineer, checks Dorothy's code for bugs and problems *before* it reaches a patient. Only after approval does it **merge** into the main project.\n", "\n", "This review step is one of the most important safety nets in all of software โ€” and when we get to AI-assisted development we'll see it's *exactly* what we need when an AI writes the code." ] }, { "cell_type": "code", "execution_count": 4, "id": "a9effbcd", "metadata": { "execution": { "iopub.execute_input": "2026-06-10T12:51:38.154742Z", "iopub.status.busy": "2026-06-10T12:51:38.154433Z", "iopub.status.idle": "2026-06-10T12:51:39.162491Z", "shell.execute_reply": "2026-06-10T12:51:39.159775Z" } }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "m1\n", "\n", "\n", "\n", "\n", "m2\n", "\n", "\n", "\n", "\n", "m1->m2\n", "\n", "\n", "\n", "\n", "\n", "m3\n", "\n", "\n", "\n", "\n", "m2->m3\n", "\n", "\n", "\n", "\n", "\n", "f1\n", "\n", "\n", "\n", "\n", "m2->f1\n", "\n", "\n", "  branch\n", "\n", "\n", "\n", "m4\n", "\n", "\n", "\n", "\n", "m3->m4\n", "\n", "\n", "\n", "\n", "\n", "f2\n", "\n", "\n", "\n", "\n", "f1->f2\n", "\n", "\n", "\n", "\n", "\n", "f2->m4\n", "\n", "\n", "  PR -> review -> merge\n", "\n", "\n", "\n", "mainlbl\n", "\n", "main (shipping)\n", "\n", "\n", "\n", "\n", "featlbl\n", "\n", "feature\n", "\n", "\n", "\n", "\n" ], "text/plain": [ "" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "#| echo: false\n", "#@title ๐ŸŒฟ Branch, pull request & merge โ€” diagram code (click to show)\n", "from graphviz import Digraph\n", "\n", "g = Digraph()\n", "g.attr(rankdir=\"LR\")\n", "g.attr(\"node\", shape=\"circle\", width=\"0.25\", label=\"\", style=\"filled\",\n", " fontname=\"Helvetica\")\n", "\n", "# main line (trusted, shipping version)\n", "g.attr(\"node\", fillcolor=\"#bbdefb\")\n", "for n in [\"m1\", \"m2\", \"m3\", \"m4\"]:\n", " g.node(n)\n", "g.edge(\"m1\", \"m2\"); g.edge(\"m2\", \"m3\"); g.edge(\"m3\", \"m4\")\n", "\n", "# feature branch\n", "g.attr(\"node\", fillcolor=\"#c8e6c9\")\n", "for n in [\"f1\", \"f2\"]:\n", " g.node(n)\n", "g.edge(\"m2\", \"f1\", label=\" branch\")\n", "g.edge(\"f1\", \"f2\")\n", "g.edge(\"f2\", \"m4\", label=\" PR -> review -> merge\", style=\"dashed\")\n", "\n", "g.node(\"mainlbl\", \"main (shipping)\", shape=\"plaintext\", fillcolor=\"white\")\n", "g.node(\"featlbl\", \"feature\", shape=\"plaintext\", fillcolor=\"white\")\n", "g.edge(\"mainlbl\", \"m1\", style=\"invis\")\n", "g.edge(\"featlbl\", \"f1\", style=\"invis\")\n", "g" ] }, { "cell_type": "markdown", "id": "b4866a3e", "metadata": {}, "source": [ "## โœ๏ธ Your Turn โ€” Set Up GitHub and Save This Notebook\n", "\n", "This is the hands-on part โ€” and you'll do it **in your browser**, not in a code cell, because that's how version control really works. You'll come away with a real GitHub account and a class repository holding your own copy of this notebook.\n", "\n", "> ๐Ÿ’ก GitHub's and Colab's screens change their wording from time to time, so the exact button labels may look a little different from what's written here โ€” the steps and the order stay the same.\n", "\n", "**1. Create a free GitHub account.**\n", "Go to **[github.com](https://github.com)** and sign up. You'll need an email address and a username (pick something you wouldn't mind a future employer seeing). It's free. As a student, you can also unlock extra free tools later through the **[GitHub Student Developer Pack](https://education.github.com/pack)**.\n", "\n", "**2. Create a repository for this class.**\n", "Once signed in, click the **+** in the top-right corner and choose **New repository**. Name it something like `comp1150`, set it to **Private** (this is your personal class space), check **Add a README file**, and click **Create repository**. You now have a repo โ€” a home for this course's work.\n", "\n", "**3. Save *this* notebook into your repo.**\n", "You're reading this in Google Colab, which can save straight to GitHub. In the Colab menu choose **File โ†’ Save a copy in GitHub**. The first time, it will ask permission to connect to your GitHub account โ€” approve it. Then pick your `comp1150` repository, keep the default filename, and click **OK**. Colab just made your first **commit** for you.\n", "\n", "> ๐Ÿ’ก *Not in Colab?* Plain Jupyter has no \"Save a copy in GitHub\" menu. No problem: download the notebook (**File โ†’ Download**), then in your repo on github.com click **Add file โ†’ Upload files** and upload it. Different door, same result โ€” a commit.\n", "\n", "**4. See your history grow.**\n", "Back on GitHub, open your `comp1150` repo and click your saved notebook. Now make a tiny change here in Colab โ€” for example, type your name in the cell below โ€” and do **File โ†’ Save a copy in GitHub** again. Refresh the repo page and click the **commits** link (it shows a clock/history icon). You should see **two** commits: proof that GitHub kept *both* versions and you could return to either one. That history is the whole point." ] }, { "cell_type": "markdown", "id": "62c2ed00", "metadata": {}, "source": [ "> **โœ๏ธ Your name here:** \\_\\_\\_ *(Double-click to edit, then re-save the notebook to GitHub in step 4.)*" ] }, { "cell_type": "markdown", "id": "028b38d5", "metadata": {}, "source": [ "### ๐Ÿ’ญ Think About It โ€” The Audit Trail\n", "\n", "Because of git, Emerald City Hospital can see *exactly* who changed each line of MedTrack, when, and why.\n", "\n", "1. Why does a complete, permanent history of changes matter *more* for hospital software than for, say, a hobby video game?\n", "2. If the 5.0 mg dose bug reached a patient, how could the project's git history help the team figure out what went wrong โ€” and who needs to be part of the fix?\n", "3. Knowing that every change is permanently recorded with your name on it, how might that change the way a developer works? Is that pressure a good thing, a bad thing, or both?" ] }, { "cell_type": "markdown", "id": "7ea29fae", "metadata": {}, "source": [ "## The Tin Woodman's Standard\n", "\n", "The Tin Woodman wants to do things *right*. For software, \"right\" has a specific meaning that trips up almost every beginner:\n", "\n", "> **Code that runs is not the same as code that is correct.**\n", "\n", "The original 5.0 mg bug ran perfectly. It crashed nothing. It just produced the *wrong answer*. The only way to catch that kind of problem on purpose โ€” before a patient does โ€” is to **test**: feed the code known inputs and check that it produces the answers you expect." ] }, { "cell_type": "markdown", "id": "6c6abc85", "metadata": {}, "source": [ "### The Syntax of `assert`\n", "\n", "The simplest testing tool in Python is the `assert` statement. You give it something that should be **True**. If it is, nothing happens and the program moves on. If it is **False**, Python stops immediately and raises an `AssertionError` โ€” a loud, deliberate alarm.\n", "\n", "```\n", "assert \n", "assert , \"\"\n", "```\n", "\n", "That optional message after the comma is what gets shown when the check fails, so make it describe what went wrong." ] }, { "cell_type": "markdown", "id": "92c2442e", "metadata": {}, "source": [ "### Hands-On: Testing a Simple Function\n", "\n", "Here's a small MedTrack helper that converts a dose in grams to milligrams. Let's write `assert` checks that pin down what \"correct\" means for it." ] }, { "cell_type": "code", "execution_count": 5, "id": "7c2a252e", "metadata": { "execution": { "iopub.execute_input": "2026-06-10T12:51:39.168547Z", "iopub.status.busy": "2026-06-10T12:51:39.167582Z", "iopub.status.idle": "2026-06-10T12:51:39.177148Z", "shell.execute_reply": "2026-06-10T12:51:39.174681Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "All checks passed โœ…\n" ] } ], "source": [ "def grams_to_mg(grams):\n", " \"\"\"Convert a dose in grams to milligrams.\"\"\"\n", " return grams * 1000\n", "\n", "# Each assert states a fact that MUST be true if the function is correct.\n", "assert grams_to_mg(1) == 1000, \"1 g should be 1000 mg\"\n", "assert grams_to_mg(0.5) == 500, \"0.5 g should be 500 mg\"\n", "assert grams_to_mg(0) == 0, \"0 g should be 0 mg\"\n", "\n", "print(\"All checks passed โœ…\")" ] }, { "cell_type": "markdown", "id": "f4829ccb", "metadata": {}, "source": [ "### A Bug Hides in Plain Sight\n", "\n", "Now meet a function that **runs fine** but is **wrong**. It's supposed to round a dose to one decimal place. Read it, then run the tests below it." ] }, { "cell_type": "code", "execution_count": 6, "id": "46505886", "metadata": { "execution": { "iopub.execute_input": "2026-06-10T12:51:39.181760Z", "iopub.status.busy": "2026-06-10T12:51:39.181361Z", "iopub.status.idle": "2026-06-10T12:51:39.186296Z", "shell.execute_reply": "2026-06-10T12:51:39.185214Z" } }, "outputs": [], "source": [ "def round_dose(mg):\n", " \"\"\"Round a dose to 1 decimal place (e.g., 0.46 -> 0.5).\"\"\"\n", " return round(mg) # <-- bug: forgot the number of decimal places!\n" ] }, { "cell_type": "code", "execution_count": 7, "id": "64dcb001", "metadata": { "execution": { "iopub.execute_input": "2026-06-10T12:51:39.190433Z", "iopub.status.busy": "2026-06-10T12:51:39.189808Z", "iopub.status.idle": "2026-06-10T12:51:39.381252Z", "shell.execute_reply": "2026-06-10T12:51:39.379712Z" } }, "outputs": [ { "ename": "AssertionError", "evalue": "0.46 mg should round to 0.5, got 0", "output_type": "error", "traceback": [ "\u001b[31m---------------------------------------------------------------------------\u001b[39m", "\u001b[31mAssertionError\u001b[39m Traceback (most recent call last)", "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[7]\u001b[39m\u001b[32m, line 2\u001b[39m\n\u001b[32m 1\u001b[39m \u001b[38;5;66;03m# These tests describe what \"correct\" means. Run this cell โ€” and watch the test catch the bug.\u001b[39;00m\n\u001b[32m----> \u001b[39m\u001b[32m2\u001b[39m \u001b[38;5;28;01massert\u001b[39;00m round_dose(\u001b[32m0.46\u001b[39m) == \u001b[32m0.5\u001b[39m, \u001b[33m\"0.46 mg should round to 0.5, got \"\u001b[39m + str(round_dose(\u001b[32m0.46\u001b[39m))\n\u001b[32m 3\u001b[39m print(\u001b[33m\"All checks passed โœ…\"\u001b[39m)\n", "\u001b[31mAssertionError\u001b[39m: 0.46 mg should round to 0.5, got 0" ] } ], "source": [ "# These tests describe what \"correct\" means. Run this cell โ€” and watch the test catch the bug.\n", "assert round_dose(0.46) == 0.5, \"0.46 mg should round to 0.5, got \" + str(round_dose(0.46))\n", "print(\"All checks passed โœ…\")" ] }, { "cell_type": "markdown", "id": "97b7b71f", "metadata": {}, "source": [ "### Understanding the Failure\n", "\n", "Run the cell above and you'll get an **`AssertionError`** showing the message we wrote. That failure is a *success* for us โ€” the test caught a real bug before any patient did. Notice what happened:\n", "\n", "- `round(0.46)` returns `0` (rounds to the nearest *whole* number).\n", "- We wanted `round(0.46, 1)` โ€” round to *one decimal place* โ€” which gives `0.5`.\n", "\n", "This is exactly the flavor of mistake behind the opening 5.0 mg disaster: code that runs, looks reasonable, and is quietly wrong. A test turned a silent error into a loud one." ] }, { "cell_type": "markdown", "id": "b0d51c49", "metadata": {}, "source": [ "### โœ๏ธ Your Turn โ€” Catch and Fix the Bug\n", "\n", "Below is the buggy `round_dose` again.\n", "\n", "1. **First**, add one or two more `assert` checks describing correct behavior (for example, `round_dose(1.24)` should be `1.2`).\n", "2. **Then** fix the function body so all your checks pass." ] }, { "cell_type": "code", "execution_count": 8, "id": "769f98a3", "metadata": { "execution": { "iopub.execute_input": "2026-06-10T12:51:39.384856Z", "iopub.status.busy": "2026-06-10T12:51:39.384413Z", "iopub.status.idle": "2026-06-10T12:51:39.391079Z", "shell.execute_reply": "2026-06-10T12:51:39.389787Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "If you see this with no error, your checks passed โœ…\n" ] } ], "source": [ "def round_dose(mg):\n", " \"\"\"Round a dose to 1 decimal place.\"\"\"\n", " return round(mg) # TODO: fix this line\n", "\n", "# TODO: add a couple of assert checks for correct behavior, e.g.:\n", "# assert round_dose(1.24) == 1.2, \"1.24 should round to 1.2\"\n", "\n", "\n", "print(\"If you see this with no error, your checks passed โœ…\")" ] }, { "cell_type": "markdown", "id": "9019c060", "metadata": {}, "source": [ "### Picture It: The Testing Mindset\n", "\n", "Professionals often write the **test first** โ€” stating what \"correct\" means โ€” and only then write or fix the code until the test passes. Once a test exists, it keeps guarding that behavior forever: if a future change re-breaks it, the test fails again instantly, catching a **regression** (an old bug coming back)." ] }, { "cell_type": "code", "execution_count": 9, "id": "3f689333", "metadata": { "execution": { "iopub.execute_input": "2026-06-10T12:51:39.395301Z", "iopub.status.busy": "2026-06-10T12:51:39.394836Z", "iopub.status.idle": "2026-06-10T12:51:40.258974Z", "shell.execute_reply": "2026-06-10T12:51:40.257605Z" } }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "R\n", "\n", "Requirement\n", "'doses round to 0.1 mg'\n", "\n", "\n", "\n", "T\n", "\n", "Write a test\n", "'round_dose(0.46) == 0.5'\n", "\n", "\n", "\n", "R->T\n", "\n", "\n", "\n", "\n", "\n", "C\n", "\n", "Write / fix code\n", "\n", "\n", "\n", "T->C\n", "\n", "\n", "\n", "\n", "\n", "P\n", "\n", "Test passes โœ…\n", "\n", "\n", "\n", "C->P\n", "\n", "\n", "\n", "\n", "\n", "P->C\n", "\n", "\n", "  regression!\n", "  (test fails again)\n", "\n", "\n", "\n" ], "text/plain": [ "" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "#| echo: false\n", "#@title โœ… The test-first loop โ€” diagram code (click to show)\n", "from graphviz import Digraph\n", "\n", "g = Digraph()\n", "g.attr(rankdir=\"LR\")\n", "g.attr(\"node\", shape=\"box\", style=\"filled,rounded\", fillcolor=\"#f3e5f5\",\n", " fontname=\"Helvetica\")\n", "g.node(\"R\", \"Requirement\\n'doses round to 0.1 mg'\")\n", "g.node(\"T\", \"Write a test\\n'round_dose(0.46) == 0.5'\")\n", "g.node(\"C\", \"Write / fix code\")\n", "g.node(\"P\", \"Test passes โœ…\", fillcolor=\"#c8e6c9\")\n", "g.edge(\"R\", \"T\"); g.edge(\"T\", \"C\"); g.edge(\"C\", \"P\")\n", "g.edge(\"P\", \"C\", label=\" regression!\\n (test fails again)\",\n", " style=\"dashed\", constraint=\"false\")\n", "g" ] }, { "cell_type": "markdown", "id": "5238fcdc", "metadata": {}, "source": [ "### ๐Ÿ’ญ Think About It โ€” \"It Ran Without Errors\"\n", "\n", "1. In your own words, what's the difference between code that *runs* and code that is *correct*? Give an everyday (non-coding) example of something that \"worked\" but was still wrong.\n", "2. You can never test *every* possible input. So how does a team decide which tests are worth writing? What kinds of inputs are most likely to hide bugs?\n", "3. For software that affects people's health, is \"we tested it and it passed\" ever *enough* to be sure it's safe? What else might matter?" ] }, { "cell_type": "markdown", "id": "b092aa85", "metadata": {}, "source": [ "## How Code Really Gets Written in 2026\n", "\n", "You already know an AI can write code โ€” you've been doing it all semester. So this section isn't about *that*. It's about the harder, more valuable skill: how to **engineer** with an AI rather than just prompt it. Asking an assistant for a single function is easy. Building something real โ€” something you'd trust at Emerald City Hospital โ€” takes discipline the AI can't supply for you.\n", "\n", "That discipline is the great-and-powerful **Wizard of Oz** lesson. The Wizard is dazzling and confident โ€” and when Toto pulls back the curtain, *just a man pulling levers*. An AI assistant produces text that *reads* like expert code. Whether it is actually correct, safe, and appropriate is a separate question โ€” and answering it is now **your** job. The modern loop is:\n", "\n", "1. **You decompose and spec** โ€” decide what pieces should exist and describe them clearly.\n", "2. **The AI drafts** โ€” fast, fluent, often *mostly* right.\n", "3. **You review, test, and own it** โ€” because plausible is not the same as correct, and your name is on it.\n", "\n", "The punchline of this whole notebook: **AI writing the code makes planning, version control, and testing more important, not less.** The human stopped being the typist and became the one who is *responsible*." ] }, { "cell_type": "markdown", "id": "d492436c", "metadata": {}, "source": [ "### Decomposition โ€” You Draw the Boxes\n", "\n", "Here's the part an AI *won't* do for you. The hard part of a real program isn't writing a function โ€” it's deciding **what functions should exist** and how they fit together. The AI is brilliant at filling in a box you've defined. Drawing the boxes is the engineer's job.\n", "\n", "One decomposition decision matters more than any other for *quality*: **separate the pure logic from the input/output.**\n", "\n", "- **Pure logic** โ€” the rules. Given some inputs, it computes an answer and returns it (no printing, no asking). A function like \"given the secret code and a guess, how many symbols are correct?\" is pure logic.\n", "- **Input / output (I/O)** โ€” the parts that talk to the human: `input()` to read a guess, `print()` to show feedback, the loop that runs the game.\n", "\n", "Why split them? Because **you can only write `assert` tests against pure logic.** You can't easily test code that stops to ask a human a question, but you *can* test a function that takes inputs and returns an answer. So designing your program with a testable logic core isn't just tidy โ€” it's what makes the rest of this notebook's discipline *possible*." ] }, { "cell_type": "code", "execution_count": 10, "id": "4ff78308", "metadata": { "execution": { "iopub.execute_input": "2026-06-10T12:51:40.263404Z", "iopub.status.busy": "2026-06-10T12:51:40.262782Z", "iopub.status.idle": "2026-06-10T12:51:41.008406Z", "shell.execute_reply": "2026-06-10T12:51:41.006945Z" } }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "spec\n", "\n", "Your spec\n", "(what to build)\n", "\n", "\n", "\n", "logic\n", "\n", "Pure logic\n", "(rules in, answer out)\n", "\n", "\n", "\n", "spec->logic\n", "\n", "\n", "\n", "\n", "\n", "io\n", "\n", "Input / output\n", "(input, print, game loop)\n", "\n", "\n", "\n", "spec->io\n", "\n", "\n", "\n", "\n", "\n", "logic->io\n", "\n", "\n", "  used by\n", "\n", "\n", "\n", "tests\n", "\n", "assert tests\n", "\n", "\n", "\n", "tests->logic\n", "\n", "\n", "  can test\n", "\n", "\n", "\n", "tests->io\n", "\n", "\n", "  hard to test\n", "\n", "\n", "\n" ], "text/plain": [ "" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "#| echo: false\n", "#@title ๐Ÿงฉ Pure logic vs. I/O โ€” diagram code (click to show)\n", "from graphviz import Digraph\n", "\n", "g = Digraph()\n", "g.attr(rankdir=\"LR\")\n", "g.attr(\"node\", shape=\"box\", style=\"filled,rounded\", fontname=\"Helvetica\")\n", "g.node(\"spec\", \"Your spec\\n(what to build)\", fillcolor=\"#fff3e0\")\n", "g.node(\"logic\", \"Pure logic\\n(rules in, answer out)\", fillcolor=\"#c8e6c9\")\n", "g.node(\"io\", \"Input / output\\n(input, print, game loop)\", fillcolor=\"#e3f2fd\")\n", "g.node(\"tests\", \"assert tests\", fillcolor=\"#f3e5f5\")\n", "g.edge(\"spec\", \"logic\")\n", "g.edge(\"spec\", \"io\")\n", "g.edge(\"logic\", \"io\", label=\" used by\")\n", "g.edge(\"tests\", \"logic\", label=\" can test\", style=\"dashed\")\n", "g.edge(\"tests\", \"io\", label=\" hard to test\", style=\"dashed\", color=\"gray\")\n", "g" ] }, { "cell_type": "markdown", "id": "2d30374c", "metadata": {}, "source": [ "### The Spec Is the Real Source Code\n", "\n", "When you build with AI, something quietly flips. In the old world, the **code** was the thing you wrote and treasured. In the AI world, the code is *downstream* โ€” the AI can regenerate it any time. What you actually author, refine, and keep is the **spec**: the clear description of what the program must do.\n", "\n", "This is why everything from the start of the notebook pays off here. A vague request gets vague, buggy code; a precise spec โ€” inputs, rules, edge cases, constraints โ€” gets code worth keeping. Your prompt to an AI *is* a spec. Writing a good one is the same skill as writing a good requirement, just aimed at a machine." ] }, { "cell_type": "markdown", "id": "b8b4dc0f", "metadata": {}, "source": [ "### Iterative Development: The First Draft Is Rarely Right\n", "\n", "Beginners treat the AI's first answer as *the* answer. Professionals treat it as a **first draft**. Real AI-assisted development is a conversation: you read the draft, find what's wrong or missing, and ask for a fix โ€” then check *that* too. The skill is steering: knowing what to ask for next, and knowing when to stop steering and just fix the two lines yourself.\n", "\n", "And every loop needs a way to tell whether the new draft is *actually* better. That's what your tests are for. Without them, \"fixing\" one thing while the AI silently breaks another is just spinning in circles.\n", "\n", "Notice what this loop *is*: the **agile** cycle from the start of this notebook, shrunk from weeks to minutes. Every prompt-review-fix round is a tiny sprint โ€” build a little, check it, adjust โ€” with you playing the customer who gives the feedback." ] }, { "cell_type": "markdown", "id": "32e2b29f", "metadata": {}, "source": [ "### Reading AI Code Critically\n", "\n", "An AI assistant is built to produce **plausible** text โ€” output that *reads* like a confident expert wrote it. But **plausible is not the same as correct.** AI-generated code can:\n", "\n", "- contain subtle logic bugs (an off-by-one, a wrong rounding) that look perfectly normal;\n", "- call functions or libraries that don't actually exist (a \"hallucination\");\n", "- ignore a rule you forgot to state;\n", "- do something *technically* working but *inappropriate* โ€” like leaking private patient data.\n", "\n", "So you read every line and ask: *Does this match the spec? What happens with bad input? Is anything here unsafe?* This is the same **code review** skill from before โ€” now aimed at a machine's output." ] }, { "cell_type": "markdown", "id": "a59a85f9", "metadata": {}, "source": [ "### ๐Ÿ” The Curtain Exercise โ€” Review the Wizard's Code\n", "\n", "The Wizard (our AI assistant) was asked to write a function that records a patient's dose. Here's what it produced. It's clean, it's confident, and it **runs**. Your job is to be **Glinda**: review it *before* it ships and find what's wrong.\n", "\n", "Read the function carefully first. There are (at least) **two** problems hiding in it โ€” one a *bug*, one a *judgment/privacy* issue." ] }, { "cell_type": "code", "execution_count": 11, "id": "802ea868", "metadata": { "execution": { "iopub.execute_input": "2026-06-10T12:51:41.012430Z", "iopub.status.busy": "2026-06-10T12:51:41.011956Z", "iopub.status.idle": "2026-06-10T12:51:41.025322Z", "shell.execute_reply": "2026-06-10T12:51:41.023308Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Dose out of range\n", "Dose recorded for Aunt Em\n" ] } ], "source": [ "# --- Code drafted by the AI assistant (the \"Wizard\"). Review before shipping! ---\n", "\n", "def record_dose(patient_name, mg):\n", " \"\"\"Record a patient's medication dose.\"\"\"\n", " # Reject doses outside the safe range.\n", " if mg < 0.01 or mg > 1000:\n", " print(\"Dose out of range\")\n", "\n", " # Save a log line so we have a record.\n", " with open(\"public_dose_log.txt\", \"a\") as f:\n", " f.write(patient_name + \" received \" + str(mg) + \" mg\\n\")\n", "\n", " return \"Dose recorded for \" + patient_name\n", "\n", "# It runs without crashing -- which is exactly the trap.\n", "print(record_dose(\"Aunt Em\", 5000))" ] }, { "cell_type": "markdown", "id": "a9610453", "metadata": {}, "source": [ "### Understanding What We Caught\n", "\n", "Run the cell above and watch the trap spring: `record_dose(\"Aunt Em\", 5000)` is **way** over the safe limit, yet the function happily returns `\"Dose recorded for Aunt Em\"`. Here are the two problems:\n", "\n", "1. **A correctness bug.** When the dose is out of range, the code only `print`s a warning โ€” then *keeps going* and records the dose anyway. It should **stop** (e.g., `raise ValueError`) so a bad dose is never saved. A **test** like `assert`-checking that 5000 mg is rejected would have caught this instantly.\n", "\n", "2. **A privacy / judgment problem.** It writes the **patient's name** into a file literally named `public_dose_log.txt`. Even if every line *worked*, dumping identifiable patient data into a shared/public file is a serious violation. **No test would catch this** โ€” it takes a *human* who understands the hospital's rules and values.\n", "\n", "That second point is the heart of the notebook: some problems are caught by tests, but others can only be caught by a responsible person who knows the context. The Wizard can draft; only Glinda can approve." ] }, { "cell_type": "markdown", "id": "7b5af9dd", "metadata": {}, "source": [ "### ๐Ÿ’ญ Think About It โ€” Who Is Responsible?\n", "\n", "1. The Wizard's function had a dangerous bug *and* a privacy violation, but it was an AI that \"wrote\" it. If that code reached a patient, **who is responsible** โ€” the AI, the developer who accepted it, the reviewer, the hospital? Defend your answer.\n", "2. What does it mean to **\"own\" code you didn't personally type**? Is accepting AI-written code more like (a) hiring a contractor, (b) copying a classmate's homework, or (c) using a calculator? Argue for the best analogy.\n", "3. Should an AI assistant be allowed to write code that controls **medication dosing** at all? If yes, what safeguards would you require first? If no, where exactly is the line?" ] }, { "cell_type": "markdown", "id": "17d5d483", "metadata": {}, "source": [ "### ๐Ÿ’ญ Think About It โ€” Using AI in *This* Course\n", "\n", "You have access to the same kind of AI assistant the pros use. That raises a real question about your own learning.\n", "\n", "1. Where's the line between **learning *with* AI** (it helps you understand) and **outsourcing your learning** (it does the thinking so you don't have to)? Give a concrete example of each.\n", "2. If an AI can write the code for an assignment in seconds, what is the *actual skill* this course is trying to build in you? Has this notebook changed your answer?\n", "3. Imagine you're hiring a junior developer in 2026. Knowing AI writes much of the code, what would you most want to test that a candidate can do? How might you find out?" ] }, { "cell_type": "markdown", "id": "ab9448b1", "metadata": {}, "source": [ "## โœ๏ธ Capstone โ€” Crack the Emerald Gate Code\n", "\n", "Time to run the *whole* engineering loop yourself, on something bigger than a single function: you'll build a small game with an AI assistant. The point isn't a polished game โ€” it's experiencing **why** specs, tests, and version control exist by actually needing them.\n", "\n", "Unlike earlier notebooks, **you write every prompt.** The notebook tells you *what* to build and *what good looks like*; composing the prompts to Gemini, Claude, or ChatGPT is your job (and you're free to re-theme the game however you like)." ] }, { "cell_type": "markdown", "id": "fc09dd84", "metadata": {}, "source": [ "### First: What Game Are We Building?\n", "\n", "The **Gatekeeper of the Emerald City** picks a secret code of **4 colored gems**, chosen from 6 colors (repeats allowed). On each turn the player guesses 4 gems, and the game gives two clues:\n", "\n", "- how many gems are the **right color in the right position**, and\n", "- how many are the **right color but in the wrong position**.\n", "\n", "The player uses those clues to deduce the code, and wins by cracking it within **10 guesses**. Here's a short sample so the idea is unmistakable:\n", "\n", "```\n", "Secret (hidden): ๐ŸŸฅ ๐ŸŸฉ ๐ŸŸฉ ๐ŸŸฆ\n", "\n", "Guess 1: ๐ŸŸฅ ๐ŸŸฆ ๐ŸŸจ ๐ŸŸฉ -> 1 right spot, 2 right color / wrong spot\n", "Guess 2: ๐ŸŸฅ ๐ŸŸฉ ๐ŸŸฆ ๐ŸŸฉ -> 3 right spot, 1 right color / wrong spot\n", "Guess 3: ๐ŸŸฅ ๐ŸŸฉ ๐ŸŸฉ ๐ŸŸฆ -> 4 right spot -- cracked it! ๐ŸŽ‰\n", "```\n", "\n", "That's the entire game. (This is the classic game *Mastermind*; the colored-gem version just gives it an Emerald City coat of paint.)" ] }, { "cell_type": "markdown", "id": "a6bfbb7d", "metadata": {}, "source": [ "### Step 1 โ€” Build the Whole Game *(prompt #1)*\n", "\n", "Write **one** prompt that asks your AI assistant to build the complete game described above, and paste its code into the cell below, then play it.\n", "\n", "Your prompt should make the spec precise โ€” the things *you* now know to include:\n", "- the rules exactly as above (4 gems, 6 colors, repeats allowed, two clue numbers, 10 guesses, win/lose);\n", "- **one specific instruction that pays off in Step 2:** ask for the scoring to live in its own function, e.g. `score_guess(secret, guess)` that *returns* the two clue numbers (right-spot, wrong-spot) โ€” separate from the `input`/`print` game loop. (Remember: pure logic you can test; I/O you can't.)\n", "- **two notebook-survival instructions**, because this code will live in a notebook cell, not a `.py` file:\n", " - the player **types letters** for the colors (e.g. `R G B Y P O`) โ€” emoji are fine to *display*, but nobody can type ๐ŸŸฅ into an input box;\n", " - the game loop lives in a function `play()` that is **not called automatically** โ€” tell the AI: no `play()` call and no `if __name__ == \"__main__\":` block at the bottom. (In a notebook that block runs anyway, and the game would demand input every single time you re-ran the cell โ€” including later, when all you want is to re-define `score_guess` for testing.) You'll start the game yourself in the next cell." ] }, { "cell_type": "code", "execution_count": 12, "id": "e7f10ac9", "metadata": { "execution": { "iopub.execute_input": "2026-06-10T12:51:41.030436Z", "iopub.status.busy": "2026-06-10T12:51:41.029964Z", "iopub.status.idle": "2026-06-10T12:51:41.034817Z", "shell.execute_reply": "2026-06-10T12:51:41.033435Z" } }, "outputs": [], "source": [ "# Paste the AI's game code here โ€” function definitions only, with NOTHING at the\n", "# bottom that calls play() automatically. Run this cell once to define the game.\n" ] }, { "cell_type": "code", "execution_count": 13, "id": "be5fcc76", "metadata": { "execution": { "iopub.execute_input": "2026-06-10T12:51:41.039007Z", "iopub.status.busy": "2026-06-10T12:51:41.038447Z", "iopub.status.idle": "2026-06-10T12:51:41.043698Z", "shell.execute_reply": "2026-06-10T12:51:41.041974Z" } }, "outputs": [], "source": [ "# Now play! Remove the # from the line below and run this cell. (Keeping play()\n", "# in its own cell means re-running the definitions above never traps you in the game.)\n", "# play()" ] }, { "cell_type": "markdown", "id": "738142df", "metadata": {}, "source": [ "### Step 2 โ€” Add Tests *(prompt #2)*\n", "\n", "Don't trust the draft โ€” pin it down. Write a prompt asking the AI to produce `assert` tests for the `score_guess` function. Paste them below and run them.\n", "\n", "Make sure your prompt demands the case that trips up almost every first draft: **repeated colors.** For example, if the secret is `๐ŸŸฅ๐ŸŸฅ๐ŸŸฉ๐ŸŸฆ` and the guess is `๐ŸŸฅ๐ŸŸฆ๐ŸŸฅ๐ŸŸฅ`, a naive scorer double-counts the extra red gems and reports the wrong-spot number incorrectly. A good test catches that. If a test fails, you've found a real bug โ€” fix it (or re-prompt) until they pass.\n", "\n", "One more notebook-survival instruction for your prompt: ask for the tests **wrapped in a function** โ€” `run_tests()` โ€” that runs every `assert` and finishes with a line like `print(\"All tests passed โœ…\")`. That way Step 4 can re-run the whole suite with a single call." ] }, { "cell_type": "code", "execution_count": 14, "id": "60c2427b", "metadata": { "execution": { "iopub.execute_input": "2026-06-10T12:51:41.047941Z", "iopub.status.busy": "2026-06-10T12:51:41.047351Z", "iopub.status.idle": "2026-06-10T12:51:41.053477Z", "shell.execute_reply": "2026-06-10T12:51:41.052159Z" } }, "outputs": [], "source": [ "# Paste the AI's tests here: a run_tests() function full of asserts.\n", "# Then remove the # below and run the cell โ€” a failure means a real bug. Fix it!\n", "# run_tests()" ] }, { "cell_type": "markdown", "id": "5d2ef4c6", "metadata": {}, "source": [ "### Step 3 โ€” Extend the Game *(prompt #3)*\n", "\n", "Now grow it. Write a prompt asking the AI to add **one** new feature to your game. Pick one (or invent your own):\n", "\n", "- a **hint** the player can request (reveals one gem);\n", "- **difficulty levels** that change the code length or number of colors;\n", "- also report the **total number of correct-color gems** each turn.\n", "\n", "Paste the AI's new, extended version of the game below." ] }, { "cell_type": "code", "execution_count": 15, "id": "d1237119", "metadata": { "execution": { "iopub.execute_input": "2026-06-10T12:51:41.059322Z", "iopub.status.busy": "2026-06-10T12:51:41.058441Z", "iopub.status.idle": "2026-06-10T12:51:41.066036Z", "shell.execute_reply": "2026-06-10T12:51:41.063690Z" } }, "outputs": [], "source": [ "# Paste the AI's extended version of the game here.\n" ] }, { "cell_type": "markdown", "id": "2178c56a", "metadata": {}, "source": [ "### Step 4 โ€” Regression Test\n", "\n", "Here's the payoff. Re-run your Step 2 tests against the **extended** code from Step 3.\n", "\n", "When an AI rewrites a program to add a feature, it often quietly breaks something that used to work โ€” that's a **regression**. Your tests are exactly what catch it. First **re-run the Step 3 cell**, so the *extended* `score_guess` is the one currently defined โ€” then run the cell below: the same `run_tests()` from Step 2, now aimed at the new code.\n", "\n", "- If the tests still pass: great โ€” your change was safe.\n", "- If a test now **fails**: the AI broke your scoring while adding the feature. *That's the lesson.* Diagnose it, fix it (or re-prompt), and get back to green before you'd ever ship." ] }, { "cell_type": "code", "execution_count": 16, "id": "e08fe991", "metadata": { "execution": { "iopub.execute_input": "2026-06-10T12:51:41.070380Z", "iopub.status.busy": "2026-06-10T12:51:41.069971Z", "iopub.status.idle": "2026-06-10T12:51:41.076094Z", "shell.execute_reply": "2026-06-10T12:51:41.074585Z" } }, "outputs": [], "source": [ "# 1. Re-run the Step 3 cell, so the EXTENDED score_guess is the one defined.\n", "# 2. Remove the # below and run โ€” same tests, new code. That's a regression test.\n", "# run_tests()" ] }, { "cell_type": "markdown", "id": "915a3546", "metadata": {}, "source": [ "### Ship It โ€” Save to GitHub\n", "\n", "You took a real program through the full loop: spec, AI draft, tests, an extension, a regression caught and fixed. Now save your work the way you set up earlier: **File -> Save a copy in GitHub -> your `comp1150` repo.** That's a real commit, in your real history โ€” with a game in it you can actually play. *(Plain Jupyter: download the notebook, then **Add file โ†’ Upload files** in your repo on github.com โ€” same commit, different door.)*" ] }, { "cell_type": "markdown", "id": "5076d85a", "metadata": {}, "source": [ "## Key Terms\n", "\n", "- **Software development life cycle (SDLC)** โ€” the stages a software project moves through: requirements, design, build, test, deploy, maintain.\n", "- **Waterfall** โ€” running the SDLC stages as one long pass: finish each stage completely before the next; feedback arrives only at the end.\n", "- **Agile** โ€” running the SDLC as many short loops, shipping a small working slice each time and letting feedback change the requirements.\n", "- **Sprint** โ€” one short agile cycle (commonly 1โ€“4 weeks) that ends with working software and user feedback.\n", "- **Requirement** โ€” a precise, testable statement of what software must do.\n", "- **User story** โ€” a requirement written from a user's view: \"As a โ€ฆ, I want โ€ฆ, so that โ€ฆ.\"\n", "- **Version control** โ€” a system that records the full history of changes to a project.\n", "- **Git** โ€” the most widely used version-control tool.\n", "- **Repository (repo)** โ€” a project tracked by git, including its complete history.\n", "- **Commit** โ€” a saved snapshot of the project, with a message describing the change.\n", "- **Staging area** โ€” the holding zone (`git add`) for changes you've chosen to include in the next commit.\n", "- **GitHub** โ€” a website that hosts the shared copy of a git project and is where teams collaborate and review code.\n", "- **Branch** โ€” a separate timeline for building a change without affecting the main version.\n", "- **Pull request (PR)** โ€” a proposal to merge a branch's changes, used to request review.\n", "- **Code review** โ€” a teammate reading proposed changes before they're merged.\n", "- **Test** โ€” code that checks other code produces the expected result.\n", "- **Assertion (`assert`)** โ€” a statement that raises an error if a condition that should be true is false.\n", "- **Decomposition** โ€” breaking a program into well-defined pieces (deciding *what functions should exist*); the engineer's job, not the AI's.\n", "- **Pure logic vs. I/O** โ€” separating the rules (inputs in, answer out โ€” testable) from the parts that talk to a human (`input`/`print` โ€” hard to test).\n", "- **Spec** โ€” the precise description of what to build; in AI-assisted work it is the real artifact you author and keep, while the code is downstream.\n", "- **Iterative development** โ€” treating the AI's output as a *first draft* and improving it through a review-and-refine loop.\n", "- **Regression** โ€” working code that breaks because of a later change (such as adding a feature); tests are what catch it.\n", "- **Prompt** โ€” the instructions given to an AI assistant; effectively a spec.\n", "- **Hallucination** โ€” confident AI output that is wrong or invented (e.g., a nonexistent function).\n", "- **Accountability** โ€” the principle that a responsible human owns the code, even when an AI drafted it." ] }, { "cell_type": "markdown", "id": "58375759", "metadata": {}, "source": [ "## Summary\n", "\n", "- **The SDLC.** Software moves through stages โ€” requirements, design, build, test, deploy, maintain. Catching a problem early (in a *requirement*) is far cheaper than catching it late (in a *patient's chart*). Clear requirements and user stories are how vague wishes become buildable, testable plans. Teams move through the stages either as one long pass (**waterfall**) or as many short, feedback-driven loops called sprints (**agile**) โ€” and most modern teams work in short loops.\n", "- **Git & GitHub.** Version control records every change, so teams can collaborate, work in parallel on branches, recover earlier versions, and keep an audit trail. You type git commands to understand them, but day-to-day they live inside GitHub and your editor. Pull requests and code review are the safety net before code ships.\n", "- **Testing.** Code that *runs* is not code that is *correct*. Tests (`assert`) turn silent wrong answers into loud failures, and guard against regressions forever after.\n", "- **AI-Assisted Development.** In 2026, AI drafts much of the code, but it produces *plausible* output, not guaranteed-correct output. The human's job is the engineering *around* it: **decomposing** the problem and writing a clear **spec**, then **iterating** on the draft and holding it to **tests** you own. Some flaws a test catches; others โ€” like a privacy violation โ€” only a responsible human can.\n", "\n", "The throughline: **the more code AI writes, the more the human disciplines of this notebook โ€” planning, version control, testing, and judgment โ€” matter.**" ] }, { "cell_type": "markdown", "id": "55fe9f70", "metadata": {}, "source": [ "## What's Next\n", "\n", "**Notebook 9 โ€” Databases: Relational & Non-Relational.** MedTrack has to *store* all those patient records somewhere safe and searchable. Next we'll see how real applications keep data organized, find it fast, and keep it consistent โ€” the foundation under almost every program that matters." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.13.9" } }, "nbformat": 4, "nbformat_minor": 5 }