Welcome

--:-- recording — let's go

Week 8

Decision records and persistence

what changed this week?

Claude Sonnet 5.5: claims up to 30% less per task through fewer tokens and tool calls

Local models on strproxy: free for you (no quota)

Course plugin update (0.14.26): run claude plugin update comp4020@comp4020, then /comp4020:doctor

local models on the course proxy

  • models: soco-fast (quicker) and soco-smart (more capable), on School GPU servers
  • cost: free, and outside your weekly budget
  • limits: weaker than Claude on hard agentic work, and slow when busy

in a session: /model soco-fast, and /model claude-sonnet-5 to switch back (it’s saved as your default, so do switch back)

from launch: ANTHROPIC_MODEL=soco-fast (LLM access)

today’s beats

the final project brief

decision records

persistence: restarts, schemas and migrations

make a multi-user, real-time website that’s good

— the final project brief

the final project

fixed: multi-user, real-time and persists (as per the brief)

yours: everything else, including the stack and (especially!) what “good” means

the repo: a placeholder, a Fly app and your own spec/

one repo: from C8 in week 9 to submission on Monday 9 November

more open than A1 and A2

A1 and A2 built constraints into the provocation; this one has none

the spec doesn’t mention accounts, feeds, rooms or scores: those are other developers’ answers to questions you now answer yourself

small is fine: an app for twelve people, one street, one book club or one afternoon

the crit agents build it too, so we’ll see the median answer

“what should it do?”

“what would make it good?”

the word “good”

what it does: you can’t say what good means for an app without saying who it’s for and what it does

how well: an app can have every feature on the list and still not be worth using

the median: ask an agent for “a multi-user, real-time website” and you get the most common one; deciding what good means is up to you

the harness: you write good down in README.md (the argument), CLAUDE.md (the rules) and spec/ (the checks), and the project is marked on whether the three are consistent

“is it any good?”

a friend asks “what’d you make?”, and you say “a site where my book club argues about the chapter we’re up to”

then they ask “is it any good?”

your answer is about those people and that use: they actually come back, nobody gets a spoiler, and last week’s argument is still there

that answer is the “good” we mean, not a universal theory of the good

assessment in the age of Claude

what value do you add beyond pasting the brief straight into Claude Code?

good, crit by crit

C8: a first answer to “is it any good?” in README.md, rough and meant to change

C9: one decision about several people at once, made using that definition

C10: logs of what people actually do, as evidence for or against it

submission: the definition, the rules and the checks are consistent, and the app meets them

stack constraints

one machine: a small Fly machine, with one volume for anything that persists (what’s fixed is set in fly.toml and the Dockerfile)

persists: across restarts and redeploys (later today)

real-time: a change reaches every open session within about a second (next week)

your README: whatever you promise there, the stack has to make possible

choosing a stack

what good needs: start from what you promised in the README

what you can read: you review and debug the agent’s code, so pick code you can follow

what’s in the training data: models write better code for widely used, well-documented stacks

how a change reaches everyone: some frameworks build real-time in (Phoenix LiveView, next week); others leave it to you

it’s a first choice: if it stops fitting, switch, and write a record saying why

ask me about the final project

decision records

an architecture decision record

a short document that captures and explains a single decision relevant to a product or ecosystem

(Martin Fowler, 2026)

the format is Michael Nygard’s (2011): title, status, context, decision, consequences

words; what do they even mean?

one, in full

# 0001. A git-based CMS for non-technical editors

Status: deprecated (the theme dropped the CMS)
Context: staff who don't use git need to edit pages;
  content is MDX in the repo, on self-hosted GitLab
Options: Sveltia (in the browser, commits to GitLab);
  TinaCMS (GitHub only, needs a server);
  Payload (a database, replacing the MDX files)
Decision: Sveltia; content stays as files in git
Consequences: editors need GitLab accounts; the CMS
  schema is a second copy, kept in step by hand

(from a Backlog.md task for the theme this site is built on)

record keeping

one file each, numbered: doc/adr/0001-htmx-for-active-web-pages.md

status: proposed, then accepted

a changed mind is a new record that supersedes the old one; an accepted record is never edited

in the repo it applies to, so it’s versioned with the code it’s about

(Fowler; adr.github.io collects templates and tools)

with an agent

lay out three options for where this app stores X, and what each costs. Don’t pick one yet

then in CLAUDE.md, so the agent reads the records in every new session:

Decisions live in doc/adr/, one numbered file each.
Read them before changing the stack, storage or data
model. To change one, write a record superseding it.

choosing a stack, with an agent

read my README, then quiz me one question at a time about what this app needs: what counts as a person, what has to persist, and how a change reaches everyone else’s open session. Give each option’s pros and cons. Don’t recommend a stack until I’ve answered

then, once you’ve chosen:

write the decision record for the stack I picked, with the options we ruled out and why

record-worthy decisions

decisions that are expensive to reverse, or that someone will ask about later

in the final project: the stack, what counts as a person, what persists, how a change reaches every open session

not every library pick/UI tweak

further reading

Nygard (2011): the original post and format

Fowler (2026): status, superseding and where records live

adr.github.io: templates (MADR, Y-statements) and tooling

joelparkerhenderson/architecture-decision-record: many worked examples

Ben uses… Backlog.md

Backlog.md: tasks as markdown files in backlog/, committed with the code

yaml frontmatter + free-form prose

the built-in backlog decision create writes a standalone record for a decision too big for its task

(the task tracking topic compares it with the alternatives)

Web

persistence, in Django

the worked instance today is Django: python’s most famous web framework

next week’s is Phoenix LiveView (similar, but in Elixir): the ideas carry across stacks, and your final project’s stack is your call

schema constraints

class Room(models.Model):
    name = models.CharField(max_length=40, unique=True)
class Message(models.Model):
    room = models.ForeignKey(Room, on_delete=models.CASCADE)

unique=True: no two rooms share a name; no null=True: every message has a room; ForeignKey: its room exists

IntegrityError: UNIQUE constraint failed: chat_room.name
IntegrityError: FOREIGN KEY constraint failed

the database raises these, whatever code the agent wrote

a migration is a recorded step

add a topic field to Message, run makemigrations, and Django writes 0002_message_topic.py:

dependencies = [('chat', '0001_initial')]
operations = [migrations.AddField(model_name='message',
    name='topic',
    field=models.CharField(default='general', max_length=40))]

the guestbook had the same migration in week 7, generated by Drizzle:

ALTER TABLE `messages` ADD `topic` text DEFAULT 'general' NOT NULL;

migrations on Fly

at boot: with SQLite on a volume, the server runs its migrations as it starts, since the volume is only on that machine

not release_command: Fly runs that on a temporary machine without your volume

the same everywhere: Rails (Active Record), Phoenix (Ecto) and Drizzle all use a schema and a migration history (persistence)

further reading

The Twelve-Factor App: processes and backing services: why the server keeps no state, and the database lives outside the code

Fowler and Sadalage, “Evolutionary Database Design”: migrations as small, versioned steps committed with the code

Django’s migrations guide: the worked instance, including what to do when two branches both add one

C8: it’s alive

ship the first working version of your final project, and a first go at saying what good means for it

live at its *.fly.dev URL, where a stranger can do the core thing and find their trace still there when they come back

a first README.md, published at /readme/, with what you read to get there

the repo goes public at the cutoff and stays public

week 9: Monday groups

Monday 5 October is a public holiday, so neither Monday crit runs in its usual slot

Shítāo: Tuesday 6 October, 14:00–15:30, usual room

Bādà: Wednesday 7 October, 15:30–17:00, Room 3.05

your C8 cutoff moves with your session (crits)

deadline radar

this week: deploy the placeholder by hand with your Fly token, before you’ve chosen anything else

your week 9 session: C8 live two hours before it, with README.md at /readme/ and reflections/crit-8.md in the repo

noon Monday 9 November: the final project is due

12–2pm Wednesday 11 November: the final project showcase in the Birch building (optional)

tl;dr

final project: you decide what makes your app good, and what it does follows

a decision record: one expensive choice and its options, in the repo for later sessions

only the database on the volume survives a restart; migrations change its schema one recorded step at a time

ask your agent

ask until you can explain these back:

  • the stack: three stacks that fit my README’s idea of good, and what each costs on one small Fly machine
  • a record: a decision record for the one I pick
  • the schema: what does my schema make impossible, and what should it?
  • migrations: how does my framework run migrations on deploy, and where does that happen on Fly?

See you in the studio