Building a REAL feature with Claude Code: every step explained
Matt Pocock · 2026-03-18 · 44м 16с · 171 852 просмотров · YouTube ↗
Топики: ai-loop-engineering
🎧 Аудио
📝 Summary
model=deepseek-v4-flash · prompt=summary-v7 · 13 351→2 641 tokens · 2026-07-20 11:52:08
🎯 Главная суть
Практическая демонстрация полного цикла разработки функции с помощью Claude Code: от сырой идеи до работающего кода, разбитого на AFK-агенты. Весь процесс строится вокруг принципа делегирования — человек фокусируется на архитектуре, языке терминов и QA, а LLM выполняет реализацию и исправление багов в фоне.
Проект и его стек
Course Video Manager — внутреннее приложение для управления созданием контента (видео, курсы, миниатюры, тексты). Насчитывает ~1200 коммитов и 637 закрытых issues. Стек: React Router, TypeScript, Node.js, Drizzle ORM, PostgreSQL, Vtest для тестов. Автор не деплоит приложение — запускает локально через start. Внутри есть концепция «призрачных» (ghost) и «реальных» (real) уроков и разделов: ghost существуют только в БД, real — ещё и на файловой системе.
Проблема, которую нужно решить
Текущий UX громоздок: чтобы создать реальный урок, нужно сначала создать ghost, потом материализовать его. Аналогично при удалении реального урока нельзя просто удалить — требуется сначала превратить в ghost. Кроме того, автор хочет иметь возможность создавать «призрачные» курсы (ghost courses) — без привязки к файловой системе, чтобы планировать курсы без немедленного коммита к структуре на диске.
«Grill Me»: как LLM вытягивает требования
Первым делом автор открывает VS Code и использует свой навык «Grill Me»: диктует сырые идеи в виде заметок, объясняя не только «что» сделать, но и «почему». Например, объясняет, зачем нужны ghost courses — чтобы планировать курсы, не привязываясь к файловой системе, инициализация репозиториев слишком медленная. LLM отвечает серией уточняющих вопросов, например: «Вы говорите ghost курс может содержать реальные уроки — что значит "реальный" без файловой системы?» Это вынуждает автора конкретизировать: добавление реального урока в ghost курс — триггер для выбора пути на диске и материализации всего курса.
Ubiquitous Language: общий словарь с LLM
Автор использует принципы Domain-Driven Design: ведёт документ «Ubiquitous Language», где зафиксированы термины (ghost lesson, materialize, материализационный каскад и т.д.). В ходе сессии LLM обновляет этот документ. Позднее, когда нужно обсудить баг, достаточно сказать «баг в материализационном каскаде» — и LLM понимает без дополнительных пояснений. В итоговый глоссарий добавились: ghost courses, materialize, materialization cascade, create on disk.
Side Question и Explore Phase
Во время Grill Me автор использует функцию side question (временный вопрос, не попадающий в историю): «Опиши, что делает CourseWriteService, какие у него возможности». Это позволяет уточнить, как устроена бизнес-логика, не прерывая основного диалога. Также используется Explore Phase — вызов суб-агента для чтения множества файлов; суб-агент возвращает только суммаризацию, экономя токены (~40k токенов контекста на весь сеанс). Автор отмечает, что explore бы хотелось сделать быстрее — это частый инструмент.
Архитектура CourseWriteService
CourseWriteService — центральный слой мутации данных: координация между БД, файловой системой, логикой перенумерации. Методы: add ghost lesson, materialize ghost, convert to ghost, delete lesson и т.д. Тесты для него собраны в одном файле course-write.e2e.test.ts — создают тестовую БД и временный git-репо. Код написан с использованием Effect (библиотека для TypeScript), что делает тестирование таким модулей удобным. В ходе Grill Me LLM ошибочно сообщила, что тестов нет — автор попросил «look harder» — и LLM их нашла.
Семь ключевых пунктов спецификации
После ~22 минут диалога достигнуто точное понимание объёма работ:
file_pathдля курсов становится nullable.- Создание ghost курса — только имя, без пути.
- UI ghost курсов — скрыть кнопки публикации и экспорта.
- Кнопка «создать реальный урок» доступна и в реальных, и в ghost курсах; в ghost курсах — с модалкой выбора пути.
- «Создать ghost урок» работает как сейчас.
- Прямое удаление реального урока — удаление с диска и из БД за один шаг.
- Превращение в ghost остаётся для реальных уроков, которые нужно оставить в плане, но убрать с диска.
Написание PRD и разбивка на issues
После Grill Me автор запускает навык «Write PRD»: LLM создаёт Product Requirements Document на основе всей сессии вопросов-ответов. В PRD включены модули (Ghost Course Schema, Materialization Cascade Service, API routes, UI), тестовые решения. Автор почти не проверяет PRD — доверяет LLM в суммаризации. Затем команда «PRD to issues» разбивает PRD на отдельные GitHub issues с блокирующими связями. Получилось 6 issues, автор вручную объединяет два мелких (UI-изменения) в один, оставляя 4 issues: Ghost Course Creation, UI Stuff, Direct Delete Action, Materialization Cascade.
AFK-агент (Ralph Loop/Sandcastle)
Автор использует самописную систему Sandcastle: Docker-контейнер, в котором Claude Code работает внутри, а изменения вытаскиваются как patch-коммиты и применяются локально. Запуск — pnpm ralph с max iterations 100. Агент последовательно обрабатывает issues, делает коммиты с подробными сообщениями. Пока агент работает, автор может заниматься другими идеями или QA. Это названо «дневная смена» (человек) и «ночная смена» (LLM). Через ~1.5 часа агент завершил 5 итераций и создал 6 коммитов.
QA-цикл и обратная связь
После выполнения автор просит LLM сгенерировать QA-план (пошаговый сценарий тестирования для каждой части) и сохранить его в GitHub issue. Затем вручную проходит план. Находит проблемы:
- В модалке добавления курса появились две вкладки (реальный/ghost), что не нужно — автор хочет всегда создавать ghost молча.
- При создании ghost курса страница не переходит на новый курс, ошибка React (minified).
- Нет состояния загрузки на кнопке.
- В контекстном меню ghost курса опции «создать ghost урок» и «создать реальный урок» выглядят неинтуитивно; автор решает заменить на один пункт с чекбоксом «создать на файловой системе».
- Баг: если репозиторий курса не git-репозиторий, система входит в нестабильное состояние — нужно откатывать создание директории при ошибке.
Каждую проблему автор отправляет через feedback-кнопку (создаёт GitHub issue). Запускает Ralph снова, который параллельно фиксит баги. К концу QA — 14 коммитов, почти все issues закрыты. Многие баги — это пограничные случаи, которые невозможно было предвидеть на этапе спецификации, что подтверждает необходимость итеративного QA.
Добавление подтверждения удаления
В процессе QA выяснилось, что при удалении реального урока нужна модалка подтверждения с деталями файлов. Автор создаёт issue на это, Ralph реализует. После финальной сборки проверяется: в меню добавления урока теперь не «ghost/real», а чекбокс «create on file system»; всё работает.
Итоговый рабочий процесс
Ключевые принципы, продемонстрированные в видео:
- Минимум ручного написания кода — автор почти не заглядывает в реализацию, сосредоточен на интерфейсах, тестах и результатах.
- «Спецификации → код» никогда не работает идеально — только через быстрый QA-цикл с обратной связью вылавливаются все edge cases.
- Возможность запускать AFK-агентов позволяет параллелить разработку и QA: пока агент чинит предыдущий баг, автор тестирует следующий функционал.
- Язык терминов (Ubiquitous Language) резко повышает эффективность коммуникации с LLM.
📜 Transcript
en · 9 670 слов · 114 сегментов · flagged: word_run (1 dropped, q=0.99)
Показать текст транскрипта
I've been posting a lot recently about things you should do with Claude Code and mostly I've been approaching it from the kind of philosophy angle. In other words, kind of everyone's thinking this is a new paradigm shift but actually just the stuff we've been doing for 20 years is really, really good with Claude Code. You get the most out of it when you treat it like someone you would delegate to in your team. In other words, you focus on the architecture, you focus on making sure that you've got good feedback loops, you focus on all the things we've been doing for the last 20 years, not planning too much up front or that stuff. And in all the comments people have been saying show us something practical. Show us you doing it. And so this is that video. I have no idea how long this video is going to be. I have no idea exactly what we're going to cover. I'm just going to go into actually doing some work with Claude Code in my work repo and I'm going to talk you through it. I'm going to show you all of the skills I'm using. I'm going to show you my process and we're just going to give you all the context you can on how these tools are actually used. And if you dig this then I'm running a cohort in a couple of weeks for Claude Code for Real Engineers. It is 40% off this week and it teaches you, either teaches you the skills that you need to do real engineering or it gives you the conversion course you need to take the existing skills you have and apply them to this new age. So if that sounds good to you then the link is below. Okay, let's start by understanding the project that I'm in and what point it is in its life cycle, all that stuff. This is my course video manager. It's about 1,200th commit. I've closed around, what, 637 issues on this project? And what I'm using this for is this is my main entry point for anything I do regarding creating content. In other words, I create videos on here, I organize my courses on here, I post videos on here, I do thumbnail editing on here, I do writing on here. It really is my one-stop shop for everything I need to do my job, and it's amazing. In terms of the code, it's a React router application which uses TypeScript and node and uses drizzle for the database ORM, uses Postgres as its database, does a lot of testing with Vtest. And the way I run this is I don't actually deploy it, I just basically run it locally. So I just tend to run start, I build it and I just run it with the start script. This is a confusing video for me to make because actually right now I'm recording the video that I'm making on the course video manager. There you go, this is me, this is the clips that are being detected. from the video that I'm recording and there you go, that's the previous clip, this is a confusing video to me to make, you know what I mean? This is just got loads of stuff here. This is what the main view looks like, we've got courses down the left hand side here, then we've got lessons and sections within those courses and the stuff I want to show you is inside the free videos over here. Inside the app I've got a concept of ghost lessons and real lessons which you can see on the filters here. These ghost lessons basically don't exist on disk and I need to right click the lesson and say create on disk disk for it to actually create the lesson in like a repo in the disk. And the logic for this is fairly complicated because I can turn this back into a ghost here so and then when I do I need to delete all of the files that are on the file system. convert it back to a ghost. The idea here is that it lets me plan and kind of create courses in the same UI, which ends up being really, really nice. But the thing I want to add are some enhancements here. So I want to be able to, when I create a lesson, I shouldn't just need to create a ghost. I should be able to add a real lesson instead of a ghost lesson. Same when I delete a real lesson, I should be able to just... delete it instead of needing to turn it into a ghost first. So this one here. Now this part of the codebase is really well tested and so this should be a relatively simple build. But there is another thing I want to do as well which is I want to when we go up to these courses up here and add a new course I want to be able to basically create a ghost course. A course that doesn't yet exist on the file system because currently every course needs an entry in the file system. So that is my idea. I've got a very loose set of requirements here and this is maybe how you enter most days as a developer. You have some small tweaks that you need to make to your application that are based on some vague ideas. Maybe those are ideas that you've come up with. Maybe those are ideas that have come from somewhere else. And the first thing you need to do is actually road test those ideas and harden those ideas. So the first thing we're going to do is open this up in VS Code and we're going to go into this and say grill me. And I'm going to use my dictation tool to dictate some stuff into here. The way that we handle ghost and real lessons is a little bit cumbersome in places. It's annoying that you have to create a ghost lesson. before you then create a real lesson. And it's also annoying that when you delete it, or delete a real lesson, you can't just delete the real lesson, you have to turn it into a ghost lesson first. Now at this point I'm thinking, do I also in this session want to tackle the other thing? Do I want to tackle the idea of ghost courses as well? My decision space for that is like, will this crowd out the... Grill Me session or will it all actually fairly seamlessly link together because the idea of this grill me session is I'm going to create a document out of this that I'm then going to use to Do future builds on basically so that's my decision here is do I want to create a PRD a product requirements document that has both of these things in or are these separate concerns that I need to separate into separate PRDs I'm going to say that this stuff actually sort of belongs together, but I'm just making that decision kind of arbitrarily really. I also want to extend the courses functionality so that it allows me to create a course without having a file path. This would be a kind of ghost course, a planned course that I haven't actually decided whether I'm going to build yet. Now this second section here, I haven't really explained the why. That's something that's on my mind now. I've sort of like, I've explained what I want the feature to do, but I haven't explained why I want the feature to exist. It's always really important when you enter these sessions that you explain the why to the LLM because Sure, if the LLM has the what, then it understands what you want to build, but if it doesn't know the why, then it can't suggest alternatives. The reason I want this is so that I can plan courses freely without needing to commit to an exact shape on the file system. Initiating new repositories is kind of cumbersome, and I sometimes just like to prefer, or I prefer building stuff in empty space instead of having to commit to an actual shape. Ghost courses would have slightly different constraints on them. Ghost courses would likely... have to... No, I guess ghost courses could have real lessons. It's just the moment that they have a real lesson inside them, then they become a real course. And you're then required to put it in the file system, I guess. Or I don't know, I'm not sure about that flow. Maybe we can work on that together. Notice how rough this is. This is just a super hashed out sort of idea that I'm kind of, you know, just spewing out the LLM and making it do all the work. If you've not seen my grill me skill, then head to my... I'll send... it in a link below, it's in my top 5 skills article. But you'll be able to see what it does here, it is really really cool. Ok, it's going to do an explore phase, I'm going to head and get a glass of water. And by the way, if you don't know what explore is, explore is basically invoking a sub-agent, so it's essentially just passing a message to another agent to say explore this codebase, that agent then explores it within its own context window and then it takes that context, it takes a sort of summary of that and it hands it back to the parent agent. So it allows you to get a lot of juice out of an explore phase because the explore can just go bam bam bam bam, read tons and tons of files and then the parent agent only gets a summary of it so it's very token efficient. But anyway, I need a glass of water. Okay, grill me has come back with a let me challenge your framing of the current pain points. It says that looking at the code delete lesson in the course write service already handles both ghost and real lessons directly. If it's real it deletes the directory and renumbers, if it's ghosts it just removes the db row. So you can delete a real lesson without converting to ghost first. Is there something in the UI that forces the convert to ghost then delete flow? Well yeah there is. Yeah in the UI there's no option to delete the lesson immediately and what I mean by deleting a real lesson is it would both delete the entry from the database and purge the on-disk representation. I'll ping this off while I explain something very cool that I've just recently started doing. I've been reading a book called Domain Driven Design and in this book they talk about how important it is that you maintain a sense of a sort of glossary of all of the terms that you use to describe the system. This ubiquitous language can then be used basically to bridge the gap between Devs and domain experts the people you're actually building it for so I love this because this is exactly what the LLM has to have with me I'm the domain expert and the LLM is the dev and we need some kind of shared language so that we can talk together precisely so for instance here we have a concept of a ghost lesson a lesson that exists on whoopsie a lesson that exists on the database Where is it? Yeah, a lesson that exists on the database but not yet on the file system. And so whenever the LLM is searching for stuff about ghost lessons it's going to come across this ubiquitous language file. Okay, so it's come back and it said, yep, the UI currently only exposes convert to ghost for real lessons, not a direct delete entirely option. That's a straightforward UI gap. Okay, that's useful. Let's quickly talk about... like the way it's using service here and what this actually means in my code base. I might actually try the new by the way side question thing here. This is really useful when you just want to ask a quick question and you don't want it to kind of enter into the chat history. Describe what's going on with the course write service. Describe the shape of it. Describe its capabilities. Let's see what it comes back with here. The reason I'm asking about this and want to show you what it is, is that... I'm really proud of the way that I've designed this repo. I mean, there's definitely stuff in there that definitely needs to be looked at, but... The way I've done these services I think is quite nice. The CourseWrite service is the main business logic layer for mutating course data, the right side of the system. It coordinates between the database, the file system and the path, numbering and renumbering logic. It's got a really clear interface here, like you can add ghost lessons, you can materialise a ghost, so convert a ghost lesson to a real lesson, convert it to ghost again, delete lesson, etc. So this is a big, big blob of functionality. And the tests for this CourseWrite service are all in one place. So course write service dot test dot ts. Oh no. Okay. Used to be there. Not anymore. Yeah, here they are. Course write E2E dot test dot ts. And this actually creates a test database. It sets up a temporary Git repo. And so it tests all of the synchronization between this. This repo is using effect and effect makes it so, so nice to create these kind of like units within your system that you can test really easily. Other languages have this as a first class thing, but effect sort of bolts that on top of TypeScript in a really beautiful way. I've made a video about effects before on this channel, just one video, and suffice to say I still love it and I use it for literally everything. Or at least every backend thing that I build is All in effect. Anyway, this huge service, the course write service, where is it? DB function service. Here it is, the course write service. It just lives in here. So it's a file with essentially a bunch of methods inside here. And these methods, like materialize ghost, like add ghost section, add ghost lesson, delete lesson, etc. This is what we're talking about when we talk about this service. So now I can exit out of this... by the way thing by just pressing space enter or escape and now I'm back here. So this is a great question. What flow do you actually want here? Are you saying you want to create a lesson action that immediately creates the directory on disk, skipping the ghost step entirely? Or is the pain more that the two-step process, create ghost and then materialize, feels like unnecessary ceremony when you already know you want a real lesson? Yes, exactly. The two-step process feels unnecessary. And it says, okay, so you want a single create lesson action that creates both the DB entity and the on disk directory in one shot when you already know that it's going to be real. Yep. Okay, and now it's gone to the bigger topic, ghost courses. Yeah, here we go. Then you said something interesting and immediately hedged on it. Ghost courses could have real lessons, and when they do, they become a real course and you're required to put it in the file system. Let me break this apart. First question. A ghost course has no file path, so it has no Git repo. Sections and lessons inside it will all be ghosts by necessity. There's nowhere to put them on disk. When you say a ghost course could have real lessons, what does real mean without a file system? Right, so here what it's doing is it's fleshing out my kind of woolly language a little bit and forcing me to... be a bit more specific. This is an easy question for me to answer. I don't want them to have some kind of intermediate status, like flesh, more fleshed out than a ghost. What a great phrase. But yeah, A is pretty obviously the thing here. Adding a real lesson to a ghost course is the trigger that forces you to assign a file path and materialise the course. Now, I'm already thinking from a UI point of view, what's going to be the easier thing to do? Or rather, the most obvious thing for the user, the cleanest UI. Either when we create a, like, real lesson in... a ghost course, we basically say no, you can't do that. You need to choose a thing in disk first and then you need to click away. Or we create a kind of integrated flow where you say, okay, you're writing this to disk. When you do that for the first time inside a ghost course, you need to choose a place that it's going to live. I think I'm like veering towards the second because it's not that complicated to do that in terms of UI. And I think it will be a nicer flow. So let's say that to it. Yeah, we're going to choose A here, and I think it makes sense that when you press Convert to, or Convert, Reveal in File System, or whatever the button is, whenever you make it... Notice here, this is where the ubiquitous language comes in really handy, because I was just sort of fluffing around there, but what I mean is that when I materialise a ghost lesson. When you materialise a ghost lesson... you should have a modal pop-up that forces you to materialize the course as well. Look how clean, like how much cleaner that language is because I've got a concept of materializing and the word agreed on between me and the LLM. Anyway, let's fire this off and see what happens. Right, yeah, we've got a concept of versions inside the application, so course versions. This is really important for me because I have a team of people who actually apply the course to the CMS and so I have a course and a changelog. For instance, here in my Claude code cohort, I've got several versions here. And like the sooner I approach the cohort, the more I'm sort of fixing bugs or like switching out videos, that kind of thing. The latest version is always the draft version. And then I can like publish that draft version and add a commit message to it. And it goes into the changelog. There you go. This is the UI for publishing the new version. And here's a preview of the changelog. So yeah, I'm pretty sure that the schema that we rely on needs the ghost course to still have versions. Yeah, I'm fairly sure that the schema that we need will require us to have versions because versions are needed in order to have sections and lessons and stuff inside them. However it makes zero sense to be able to publish a ghost version so we should just hide the bits of UI that and the actions in the menu that allow you to publish. So let's fire this off. I can see in my video editor that we're about 15 minutes in and just notice how long we're spending here on fleshing out these ideas. The more we do here, the less we're going to end up needing to do. when we actually guide the LLM. Okay, let me push on the file assignment moment. When you materialize a ghost course, because you're materializing a lesson inside of it, you need to give it a file path. Either the repo already exists, or the repo doesn't exist yet. When you assign a file path to a ghost course, should the app handle repo creation for you? No, it shouldn't handle repo creation. It's just pointing at an existing directory. That's an easy one to figure out there. Okay, this is such a lovely, smart question that it's given us here. It's laying out what you're asking for. Direct delete for real lessons. direct create for real lessons, ghost courses, and then materializing a lesson in ghost courses. Question, does direct create apply inside ghost courses too? That's so freaking smart. That's such a nice question. I can imagine. my old boss Willow Reagan asking that question, who is the guy I learned a lot of this lead developer stuff from. He had this incredible ability to just basically sit in a meeting and ask smart questions for hours and hours and hours. And this is exactly what the AI is doing here. It's asking really smart questions at the right point in the process. So if you're inside a ghost course and you hit create lesson, should that immediately trigger the assigned file path modal and create it on disk? Now, again, there's a trade off here. And instead of me thinking about this in my own head, which is not particularly fun viewing. Why don't we model what it might look like if I sort of work this through with the LLM. Can you give me the trade-offs of both of these approaches? Okay, so it's saying... On option A, create lesson inside a ghost course always creates a ghost lesson. Keeps the ghost course as a pure planning space. The assigned file path moment only happens when you explicitly choose to materialise, which is a clear deliberate action. This downside, if you've already decided this course is real and you're just adding lessons, you're back to the two-step annoyance you're trying to fix. Option B, create lesson inside a ghost course triggers the materialisation cascade. Yeah, it's saying option A is the obvious choice given your stated goal. The two-step annoyance you described is specifically about real courses where the file system exists and you want a real lesson. Yeah, I probably agree with two buttons, one to create a real lesson and one to create a ghost lesson. Could you just talk me through the UI a little bit and how this is going to look? Notice that I'm driving this a little bit more now, that's okay too. The grill me skill is nice and flexible so that you can drive sometimes and the AI can drive. This is nice too, it's actually checking the current UI for lesson creation first. This is part of the skill, is I get it to, instead of asking me questions about everything, if it's a question that can only be answered by, or can be answered by looking at the code, it should look at the code first. I do wish that explore was faster. Explore is the thing that's like you need it in every single session, sometimes multiple times a session. And I just wish it was quicker. Okay, so here's how the current UI works. You right click a section header, you add a lesson, right click an existing lesson, add lesson before, add lesson after, also creates a ghost. Inside a real course, you right click a section, you'd see add ghost lesson and add lesson. Inside a ghost course, right click the section, you'd only see add ghost lesson. Yeah, right, that's the question. Inside a ghost course, do you want the create real lesson button to exist at all? Now at this point, I don't know. Like I'm imagining this UI is going to get pretty crowded with this like, Because you're going to have multiple buttons there. You're going to have like, add a lesson before this one, and then do you make it ghost or real? I guess like, oh man, this is actually quite gnarly. I'm thinking, do I actually want a prototype of this so I can sort of see how it looks? Because there's multiple ways that we could fiddle with this and get it like nice. You could have it where you add a lesson, then it opens a modal, and you choose whether... it's a ghost lesson or a real lesson. I think probably what I'm going to do is I'm just going to build it one way and then maybe offer feedback afterwards. So let's say yes inside a ghost course let's have the create real lesson button. You know I could like stop this whole grilling session I could go into prototyping mode here where I just sort of prototype three different versions, five different versions of this just to see what the UI would look like. But I'm quite keen to get into implementation now. Okay, so create real lesson inside a ghost course triggers the full cascade. Modal asks for file path, course becomes real, section materializes, lesson gets created on disk, all in one flow, that's right. That modal is doing a lot of work. Correct, it needs to grab the lesson title and the course file path. Yeah, that's it. Correct, that's it, we just need the lesson title and the course file path. That's it. I can feel it, we're getting to the end of this grilling session. Oh, maybe not. It's asking about section behavior, sections inside ghost courses. And the state of a section is basically just derived from the lesson. If there are any ghost lessons inside a section, then it's going to be a ghost. Oh sorry, if there are any real lessons inside a section then it's a real section on disk. A section basically looks like this. It's just essentially a directory on disk. You can rename it and rename the directory or you can add things to it but that's basically it. Section logic should obey the current rules that we already have. There's nothing really new about sections. For instance, when we're inside a ghost course and we create a real lesson inside that section then we basically flow all the way up so it becomes a real section, becomes a real lesson and becomes a real course. Here we go, let's keep going. Let me switch to the database schema right now. Courses table has a file path as a required text field. For ghost courses this would need to be nullable. That is the only schema change I'm envisioning. Yeah, just going to say yes to this. It's going to grill me on a tricky edge case, I see. I have a real course with a file path. It has some real lessons and some ghost lessons. You delete all the real lessons or convert them all to ghost. the real course become a ghost course? Again, this is like a classic Willow Reagan question. It's fantastic. Really, really good. No, the real course does not become a ghost course. Once a course has a file path, it stays real forever because the thing that's on disk is not going anywhere. Yeah, I was thinking about that before and so I'm super glad that it answered it because now it's in the context and it's spelled out. Another edge case, you have a ghost course, you materialize a lesson, assign a file path, everything becomes real. Now the course has a file path but no real lessons. I feel like we just answered that, right? That's a weird one. I mean, I don't think Willow Reagan would ask that question. Yeah, let me move to the deletion side. You want a direct delete action for real lessons in the UI. Currently, the context menu for real lessons shows convert to ghost, but no delete. Yeah, this is a UI thing. Should real lessons have both options, convert to ghost and delete? I think we need both. Okay, we're getting there. We're getting there. I think I've covered the main branches. Let me lay out the full scope as I understand it, and you tell me if I'm missing anything. Courses file path becomes nullable. Ghost course creation, a new course creation flow that only asks for a name, no file path, correct. Ghost course UI, hide the publish and export actions, correct. Create a real lesson button, available in both real and ghost courses. In a real course, creates on disk immediately. In a ghost course, triggers modal asking for the file path first, then cascades materialization. That looks good. Create ghost lesson, works as today, available everywhere. Delete action for real lessons, purges from the disk and the database in one step. Yes. Delete action for ghost lessons already unchanged. And finally convert to ghost stays for real lessons that you want to keep planning but remove from the dist. Exactly. So we've got there. This was essentially the eight bullet points that we just spent 22 minutes of video just trying to get. This to me feels really, really familiar. This is how I'm working with AI a lot. I, you know... block out half an hour in my calendar to work out exactly the feature I want and from here it's pretty much all on rails. Like we have done the hard bit now, we have done the human in the loop bit. What I do next is I now, I've been trying this for a few days, I get it to update my ubiquitous language documents to basically just keep it up to date with any of the ideas that I've got in here. For instance we've got some new terminology now, we've got this idea of ghost courses, which is new. Let's see what edit it decided to make. In fact, I'm just going to accept it and review it afterwards. So there we go. We can see it's updated its idea of ghost entities here. It's got ghost lessons, ghost sections, and ghost courses. The verb materialize is now properly in here. The act of transitioning a ghost entity to a real entity by creating its on-disk representation. I like too that it has this create on disk and realize, like the aliases to avoid. That's really cool. And it's even got a concept for the materialization cascade. The chain reaction when materialising a lesson inside a ghost course. Assigns file path to course, materialises section, then materialises lesson. I freaking love this because later on I can say yeah there's a bug inside the materialisation cascade and it knows exactly what I'm talking about. So let's commit this. Update to the ubiquitous language document. Look at this, yeah, sometimes I am actually committing code myself. Okay, so it's gone back to the grilling. Yeah, it's now just sort of repeated the eight sections. The existing plan entity is a separate disconnected planning tool. Yeah, I did used to have a separate planning area, but then I sort of used this ghost concept to integrate it into my actual courses thing. I've got it inside the ubiquitous language inside here, down here. A plan, a plan section, and a plan lesson there. But this is basically... deprecated. I am going to remove this at some point. And so we can say plans are deprecated and ghost courses are the new way to do it. And in fact, I think I'm going to go ahead and say I'm satisfied. Let's. And I'll invoke my next skill, which is let's write a PRD. So if we think about the conversation that we've just had with the LLM, it's incredibly good fodder for just turning this into a sort of more. concise summary document of everything that we want to build. I freaking love question and answer because it co-locates the question with the answer. I know that sounds like such a sort of basic thing to say, but the way that these attention mechanisms work is that stuff that's close together tends to, like, it sort of shows up as a hotspot for the LLM in terms of its attention mechanism, just like it does for humans. And... What this ends up meaning is that this is such a rich document for it to pour through. Now it's doing another explore phase. We should probably do like a cursory check on our context window down here. We're currently only at like 40k tokens, which is pretty impressive given how long we've spent with the LLM working on this stuff. A lot of folks have asked me why my grill me skill doesn't use the ask user question tool. Like we weren't actually sort of like... using Claude's built-in mechanism for asking questions and the reason for that is that I don't really like that UI first of all and second like if you have a choice between calling a tool and not calling a tool then Like, not calling a tool is always going to be more token efficient. Because every time you call a tool, you need to wrap it in JSON, you need to do various things. Okie dokie, we can see here that... Okay, it's now sketching out the major modules before writing the PRD. I've talked before on this channel on how important I think modules are, and like, understanding the modules. in your codebase without necessarily needing to look inside them. Like the course write service that we looked at before, that's a big testable module that I was very careful in designing and designing the interface for. And I think this is so important that I've baked it into my writer PRD skill, so it sketches out the modules and surfaces them to you. I don't necessarily need to look inside these modules, I just want to know how they're changing. For instance here I can see something that's a bit dodgy, right? It wants it to make a new method on the course write service, materialize course and lesson. Reuses the existing materialized ghost and section materializing logic internally. I'm thinking about whether it could just instead use the existing materialized ghost and parse it like an additional parameter. But I think that would be a little bit dodgy API wise. So I think it is probably right. I think it should have a new method. Notice how I'm thinking about the interface more than I'm actually thinking about the implementation here. The implementation I don't really care about. But I want to make sure that this is testable and, you know, that the rest of the... kind of repo and any future AI agents can understand what it's doing. And again, notice how easy these functions are to name because we've got this concept of the ubiquitous language. So module one is the ghost course schema, db operations, that seems fine. The materialization cascade service, that seems fine. It's just a new method on the course write service. Here we've got two new API routes, which makes sense because we're doing direct real lesson creation, direct real lesson deletion. I could put them into like other API routes but because this is just an internal tool I don't really care about what my API looks like from the outside then I don't really care about how many API routes I have or what they look like it's just an implementation detail now it's talking about the Ghost course UI, new add ghost course option in the sidebar. Yep, that seems fine. A materialization modal, two lesson creation buttons in context menus, and a delete action. And then module six, the plans deprecation, hey? I mean, I do want to deprecate the plans at some point, but I don't think I want to deprecate plans now, certainly not as part of this PRD. And it's asking me a second question, which modules do you want tests for? I want tests in the places that we already have test harnesses. So for the course write service, definitely. And if we have some stuff for direct deletion, I think that would be as part of the course write service too, right? So let's see what it says here. Now this is funny. It's saying there's no existing test harness for course write service. The tests in the repo are mostly for pure functions and reducers. That is rubbish. So I'm going to do a Rafiki. I'm going to say look. harder. And there we go. There they are, a whole suite of them split by concern. Okay, and it's satisfied, it has everything it's needed, it's now going to write the PRD and submit it as a GitHub issue. This is what I do with my PRDs, this is what I do with all of my issues. I stick them in GitHub, because then when we go to actually implement this with an AFK agent, the AFK agent is going to pull down all of the GitHub issues, choose the one it's going to work on, and then just work on that one. Now the question becomes, am I going to review this PRD? And... No, I'm not going to. LLMs are really, really good at summarizing things. And so I know that this PRD will probably be a pretty good summarization of this conversation. So I'm just going to accept it on faith. And I'm then going to say PRD to... issues. So it's now time to break this PRD down into individual issues. The benefit of doing this is that this now, the PRD is already in its context from having written it, and so we get to just break the issues down straight from there. These all have blocking relationships in, so it's blocked by nothing. This one's blocked by number one. This one's also blocked by nothing. That's cool. Now let's just see how many there are here. Six does... feel about right I think and what I mean by that is this is going to get picked up by a Ralph loop and so it's going to just sort of work through each of these tasks sequentially and so I want to pick tasks that are not too big and not too small because if they're too small then we pay the cost of like having to kick up an entire agent just to do like number two here, hide the publish export UI on ghost courses. This is a tiny task and it can be melded in with something else. But maybe ghost course creation, maybe no, that seems like decently sized because it touches the UI, it touches the schema, touches the API. Two lesson creation buttons, create ghost lesson and create real lesson, this seems super small. And actually I think it has broken them. down a little bit too far, except maybe the materialization cascade, that needs to live in its own. think maybe we just merge two and three together and I think we'll be happy. So let's go down to the bottom. Let's say merge two and three together. Okay, now we've got four slices, the ghost course creation, the UI stuff, the direct delete action, and the materialization cascade. Good stuff. Let's go. Let's create the issues. Am I going to review these issues? Absolutely not. I understand, like, I've already sort of pre-reviewed them. It's just expanding out stuff that's in the PRD and then, you know, putting them in issues. this will be fine. I might just show you one of them just to sort of see what they look like. It links to the parent PRD, it says exactly what to build, it gives some acceptance criteria, says what it's blocked by, and it also says the user stories that are addressed in the parent PRD. If the parent PRD is the kind of destination, then these things are the journey to get there. And notice the PRD just has a bunch of user stories, some implementation decisions, and testing decisions as well. All of this stuff essentially just comes from the skills that I've put together. And I found that they're good enough to keep the LLM on Rails. I really like adding in the testing decisions because it means that it's more likely to follow TDD and do some kind of create some feedback loops as it's going, which is great. So, all right, we have now set everything up to make our AFK agent to give it the best chance possible to produce good work. Now, I've been like cooking my setup for this a little bit over the last 24 hours, and I've sort of built like a mini library to make it work better than I had it before. The provisional name for this is sandcastle and we have like a docker file here. It's going to spin up a docker container. It's going to mount the working directory inside this docker file and then it's going to any commits that are made then inside the docker file by Claude, which is going to run in there, it's then going to patch those out. So pull out those commits as patches and apply them to my local I found that this setup is just like super flexible. I have a Docker file and a prompt here and I can just run a Ralph loop again and again and again, passing in a bunch of issues and passing in like the last X number of commits. But you can check out the repo to sort of see the most up-to-date file on this. I'm going to run, where is it? I think it's pnpm-ralph. So I'll run pnpm-ralph. This is going to now spin up my AFK agent with a max number of iterations of 100. What it should do is it will run out of GitHub issues because it's going to close the GitHub issues as it creates commits for them. But at this point, I can essentially just walk away because, you know, we can see it's like doing things in the terminal. It's saying, looking at the issues, direct, delete, action, blah, blah, blah, blah. But I can now have a cup of tea or I can go make a coffee or something. or have my lunch. Or more likely, I can just open up another terminal and enter another grilling session. So for folks who say this approach seems really, really slow, what you need to understand is that it's slow because you're trying to extract ideas out of your human brain. And while this is happening, you've got AFK agents running in the background implementing your previous grilling sessions. This is why this is revolutionary, because like once we've completed thinking through the idea... our work is kind of done until we actually QA the outputs. My friend Jamin on Twitter called this the day shift and the night shift. Where I'm doing the day shift, I'm like, you know, thinking of ideas, I'm grilling with the LLM, I'm turning this into PRDs and turning those PRDs into issues, and then the LLM takes the night shift. Claude goes and actually implements this stuff AFK. So I'm going to take a little break, I might even go for a little walk, and I'm going to just wait and check back in with this once it's done. Okay, we are back. It is like an hour and a half later. I went for a walk. I went and had a cup of tea with my parents. Yeah, let's see what it's done. So we can see that the agent signaled completion after five iterations. So we essentially ran a cleared Claude Code agent five times in the repo, and it should have produced for us, yeah, I think six commits here. I think those commits, one of them might be one that I just haven't pushed up yet. Yeah, we can see the entire commit history here. It's been leaving really nice detailed commit messages for us. And it should be, yeah, I think this was the update that I pushed before, or haven't pushed so far. So at this point in the process, I need to kind of look back over these commits. I'm kind of tempting to look over the code, but I think I just want to review the implementation first. So I'm going to open up a new Claude session. come up with a skill for this yet, so I'm just going to sort of free blast it. So I'm going to say, take the last five commits and create a QA plan for me. Save that QA plan in a GitHub issue. The QA plan should give me a step-by-step guide on how to test every single part of the new implementation. This is something I've been sort of playing around with adding into my skills because I think it... does make sense almost in every single sort of user facing change. And while it's doing this I'm going to rebuild the application and rerun it locally so that we can see exactly what's happening with it. Okay, so it has created for us a QA plan. Beautiful. Now, I have to say there is nothing more boring than watching else or watching someone else do QA. So what I'm going to do is just walk through this myself and I will come back to you if I have any issues. Okay, the first one actually came super early on, which is pressing this add course modal. I can see now there's two tabs in here. I actually really hate this. Like, I just want... every time you add a new course it should be a ghost course and I don't really like the ghost course terminology appearing in the UI. So what I'm going to do is exit out of this and I've got a little feedback button here which you can see if I just remove myself there. This feedback button I can just describe my feedback in detail and then it will create a GitHub issue for me which can then be picked up by the Ralph loop. So I'm going to say when I open the add course modal I want the only option to be to add a ghost course. And I don't want the ghost course to appear in the UI. It should just look like we're creating a course and then it creates a ghost course. So I'll submit this and when I go to the GitHub issues we can see that we've now got a new issue saying hide ghost course option and add course modal, create ghost course silently. So I actually used Haiku to generate me a title here and then we've got the route it was submitted from and then what I said in the feedback button. This is how I do QA. This information is enough for Ralph to do a really nice job here. So I'm actually going to start my Ralph loop as I'm going here. I'm going to go back to the QA plan and I'm just going to add a comment to this and I'm going to say Ralph loops should not work on this one. This is a human in the loop task only. Or in fact, I'm just going to rename that first one to AFK, in fact. I've got something in my prompt that says if there's a human in the loop label on it, or it looks like it's for humans, don't work on it. And so let's go back in here. Let's run pnpm Ralph again, and it's going to actually work on that issue while we're QAing other stuff. So I'll go back to doing a bit of QA, and I'll see you in a second. Okay, a new thing is when I create a ghost course here, and I say new fun course, for instance. Then when I press create ghost course nothing appears to happen. But actually, as you can see in the top left, first of all, we get the weird minified react error. So I'll just copy and paste that for the feedback form later. It does actually create the course, but we don't go to the course and the modal doesn't close. So let's add this back into feedback. When I create a ghost course, it doesn't direct me to the new page and it shows this error. There's also no loading state present on the button, which looks confusing from the user's perspective. So let's submit that again. The agent that we kicked off to fix the previous bug. has actually already finished, so let's just kick it off again. And I'll keep QAing. This time I won't interrupt, I'll just kind of keep going through the issues and attempting to fix them with Ralph in the background. Okay, this one I did think was showing off here, or worth showing off rather. When I see a new ghost section inside a ghost course, and I right-click here, this create ghost lesson and create real lesson doesn't seem right to me. I feel like when I go into create ghost lesson here, or I just want an add lesson, kind of thing. We've already got a modal here. What we should do is just have a checkbox that says also create this on the file system. So this is something that came up in our early discussions about this feature that kind of like I couldn't get a sense for which way to go until I saw it in reality. So that's the way that some things go sometimes. We could have had an extra design phase or we could have had an extra prototype phase but you know I don't mind just jumping to code and fixing it there. So I will add some feedback for that but I'll spare you hearing my dictation. Alright then, I've walked through the QA plan and I have created seven issues here over the last, or six issues over the last eight minutes. We can see that while I've been QA'ing, Ralph has been going in the background and is fixing the issues. Most of these issues are bugs, but some of them are features that we just didn't think about. So for instance, when we're deleting real lessons, we want to add a confirmation modal to make sure that it doesn't just, you know, like... we don't accidentally click it and it deletes or whatever. There were certainly some showstopper bugs here, for instance. If it's not a git repo, then it gets into a super weird state. Yeah, if the course repo is not a git repository and anything about that fails, then we should walk back the creation of the directory inside it, since the directory and file system and the database will then be out of sync. This is something, again, we didn't think about in the grill me session at the start, so we're now having to find out about it in QA. It's this kind of stuff, this kind of stuff, that makes me think that, like, the specs to code approach, It's just never going to work. Because when you're in there, when you're in the QA loop, when you're iterating towards something, you are going to find little weird edge cases like this that is really hard to plan for before. Anyway, rant over. The point here is that I've now done an extra QA step. Ralph is now going to chew through these issues. I can probably actually close this QA plan. I might want to reopen it if I, like, um... sort of want to redo it, let's say, but the behavior has slightly changed. So I'm closing it to take it out of Ralph's, or rather the agent that I'm using's context, so it doesn't look at this as like the source of truth for what it's supposed to be building. Either way, I'm gonna go do something else, and then we'll see what happens to Ralph when it comes back. Okay, we're back and we've got iteration 8 which is very nearly complete. This looks like the final issue here where it's just adding the confirmation modal with file details when deleting real lessons. We can see we're up to 14 commits that have been added kind of in this entire feature build as we've been going along. And the issues here are very nearly closed. You can see I last opened this one 30 minutes ago so that's how long it's been running kind of. One thing I would like to add here is potentially parallelising these Ralph loops and sort of having like a team of agents working on it at once. But to be honest, it's quite nice having these gaps because it means that I get to do some deep focus on something, for instance, like grilling me on something, and then later I can come back and sort of... come back to code and do a big QA session, send it off again, you get the idea. Something that's crucial to the success of these Ralph loops is making sure it runs tests and types on every single commit. We can see that in most of these loops it's adding tests too, so it's updating the reducer test to cover the new action. Now it's committing and closing the issue, so we should be nearly there. Okay, and we are good to go. I'm going to rerun the build and let's do a little bit more QA. And I'll spare you the extra QA step here. but suffice to say what I would do from this point on is I would go back here and I would continue QAing it, find more bugs and at some point I would call this done and I would stop working on it. One thing that's great about having this really flexible backlog approach is that anytime I can just queue up a bunch of bug fixes and it will go in and fix them. Let's just check one thing which is we can see that the add lesson now it no longer says ghost lesson and real lesson and we can see here it's saying create on file system in a little checkbox. If I go foobar and I say create on file system then it's going to add the lesson and we should be able to see once that's done it's quite slow that isn't it i'm interested on why that's quite slow especially because this is all local It should now be complete. And it creates that foo bar as the second lesson. So there we go. We are, by my count, which is not entirely accurate, it'll be about 42 minutes into the video at this point. If you're here, thank you so much for watching all the way to the end. What I hope you take out of this video, I'm not sure to be honest, like, this is so much looser than the videos I usually put together, which are usually fairly tight, focused on a specific goal. I hope that you were able to pick up some vibes from me in terms of how much detail and how... depth I look at the stuff that I'm producing. And also notice how little I looked at the code really. Like what I'm doing here is I'm reviewing inputs and outputs. I'm interested in the code, absolutely. I'm interested in how the interfaces are changing. I'm interested in how the modules, what the modules are sort of looking like. And every so often I'll go and have a little poke around in the code just to make sure it's on the right track. But for me really what I'm doing is reviewing the outputs that come from AI, passing more information to it, and getting into a tight loop with it. And crucially because I'm able to run the run Claude AFK, I'm able to parallelize my own QA with the fixing of the bugs, you know, which is just amazing. I imagine though you have just dozens and dozens of questions. feel free to ping them into the comments below. Or if you want to learn this from the ground up instead of just diving through this messy video, then the cohort is the place. This one starts on March 30th, but if you're seeing this sometime in the future, then there'll probably be a place you can sign up for the next one too. This has been the last kind of two months of my life, really full-time working on this. And I'm so proud of how it's come together because everything that you see here in this sort of... big video, has come from me thinking about this stuff deeply, and the cohort is the kind of perfect encapsulation, the easiest way to learn this from the ground up. Anyway, thank you so, so much for watching, thank you for getting to this point, and I'll see you in the next one.
⚙️ Pipeline jobs
| Stage | Status | Att. | Updated | Error |
|---|---|---|---|---|
| download | done | 1/3 | 2026-07-20 11:51:07 | |
| transcribe | done | 1/3 | 2026-07-20 11:51:36 | |
| summarize | done | 1/3 | 2026-07-20 11:52:08 | |
| embed | done | 1/3 | 2026-07-20 11:52:10 |
📄 Описание YouTube
Показать
In this video, I walk through a real-world example of using Claude Code with my course video manager. I'll show you my entire workflow: from initial feature brainstorming through to autonomous implementation and QA testing. Claude Code for Real Engineers Cohort - 40% off this week: https://aihero.dev/s/BQGSo5 My Skills: https://aihero.dev/s/FpvIa6 The course-video-manager repo: https://github.com/mattpocock/course-video-manager Sign up for the AI Hero newsletter: https://aihero.dev/s/xP6m6E Follow Matt on Twitter https://twitter.com/mattpocockuk Join the Discord: https://aihero.dev/s/gmJkUT