Welcome#
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) andsoco-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)
make a multi-user, real-time website that’s good
— the final project brief
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 failedthe 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
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?