TL;DR: одним промптом я реализовал IndexedDB с помощью Claude Code и Ralph loop: получилось пройти 95% целевого подмножества Web Platform Tests и 77,4% более строгого набора тестов.

Когда я узнал, что два простых браузерных движка были написаны с помощью вайб-кодинга, я не особенно удивился. Браузерный движок — хорошо изученная задача с несколькими независимыми реализациями, а их кодовые базы наверняка давно попали в обучающие данные LLM.

Удивило меня другое: похоже, ни один из проектов по-настоящему не использовал Web Platform Tests (WPT). А ведь это бесчисленные человеко-часы экспертизы, сжатые в точное описание того, как должен работать браузер, вплоть до самых странных пограничных случаев. Второй проект частично использует WPT, но, судя по всему, это не основная стратегия тестирования.

LLM отлично работают, когда им дают четкую спецификацию или PRD и приемочные тесты. Именно это сообщество веб-стандартов кропотливо строило последние несколько десятилетий: сами браузерные стандарты, написанные обычным английским в виде HTML-файлов, и WPT. А процент прохождения WPT, в частности, дает хорошее представление о том, насколько браузер «веб-совместим», то есть способен ли он реально рендерить сайты из живого интернета. Именно поэтому новые браузеры вроде Ladybird и Servo так сильно на него опираются.

У меня нет ни терпения, ни денег, чтобы собрать целый браузер. Но мне стало интересно: можно ли с нуля реализовать один браузерный API одним промптом и пройти нетривиальную долю Web Platform Tests. Я выбрал IndexedDB, потому что хорошо знаю эту спецификацию: я работал и над PouchDB, и над fake-indexeddb, а еще отправлял небольшие PR и баг-репорты в саму спецификацию.

IndexedDB — не простой API. Это полноценная NoSQL-база данных с несколькими типами ключей, включая ключи-массивы и массивы в роли ключей, курсорами, режимами долговечности транзакций, транзакциями, планированием выполнения и так далее. Если строить поверх SQLite, часть всего этого достается почти бесплатно — вероятно, поэтому и Firefox, и WebKit используют его в своих реализациях. Но все равно нужно обрабатывать типы JavaScript-объектов вроде Date и ArrayBuffer, специфичный для JavaScript порядок выполнения микрозадач, автоматические транзакции и кучу других особенностей.

Эксперимент

Эксперимент выглядел так:

  1. Создать репозиторий с подмодулями, в которых лежат Web Platform Tests и спецификация IndexedDB.

  2. Попросить Claude в режиме планирования составить план рабочей реализации IndexedDB на TypeScript и Node.js поверх SQLite, которая проходит больше 90% тестов.

  3. Подключить этот план к Ralph loop, чтобы несколько агентов могли последовательно итерироваться над решением задачи.

  4. Лечь спать.

Если вы не знакомы с так называемой техникой Ralph Wiggum, она до смешного проста: запускаете Claude в Bash-цикле, даете ему Markdown-файл с инструкциями и текстовый файл для отслеживания прогресса. Буквально всё. Главная идея — избежать деградации контекста, чаще начиная новую сессию с чистого листа. Иначе говоря, чем дольше идет разговор, тем сильнее LLM тупеет, поэтому разговоры лучше делать короче. Я использовал реализацию Мэтта Покока — это буквально 24 строки Bash — в режиме --dangerously-skip-permissions, для безопасности запущенном в контейнере Podman.

Проект завершился за несколько часов работы, а агент решил ослушаться моих инструкций и пройти заметно больше 90% целевых тестов, добравшись до 95%. Плохой робот! При этом он пропустил часть тестов, потому что счел их неподходящими для среды Node.js, но итог все равно составил 1208 проходящих тестов из 1272 в целевом подмножестве.

Ниже промпт. Обратите внимание, что в нем были опечатки и грамматические ошибки — например, я имел в виду instanceof, а не typeof, — но агент все равно разобрался:

Промпт

Помоги мне спланировать проект. У тебя есть вся спецификация IndexedDB и web-platform-tests, подключенные как подмодули Git.

Проект такой: нужно собрать проект на TypeScript, который реализует IndexedDB на чистом JavaScript без зависимостей поверх SQLite — ну ладно, SQLite будет единственной зависимостью. Нужно попытаться пройти хотя бы 90% тестов IndexedDB из WPT.

Условия:

  1. Используй TypeScript и запускай в обычном Node.js: у тебя уже установлен Node v24, который поддерживает TS из коробки. Но для линтинга используй tsc.

  2. Пиши тесты с помощью node:test.

  3. Нужно запускать WPT-тесты в Node без изменений. Для этого почти наверняка понадобятся shim-прослойки, потому что тесты рассчитаны на запуск в браузере, а не в Node. Но по возможности лучше опираться на встроенные API. Сейчас Node поддерживает много встроенных вещей вроде Event и EventTarget, так что это не должно быть слишком сложно.

  4. Начать стоит с базового каркаса проекта и тестовой инфраструктуры. Для начала попробуй заставить проходить ОДИН тест, даже если для этого придется сделать простейшую реализацию IndexedDB на чистом JS, буквально «hello world».

  5. По ходу работы записывай эти базовые условия и структуру проекта в CLAUDE.md для следующего агента: например, как запускать тесты, как запускать линтинг и так далее.

  6. В итоге реализация должна хранить данные в SQLite. Для этого используй пакет better-sqlite3. И снова: никаких зависимостей, кроме этой. devDependencies может быть сколько угодно, например typescript.

  7. Мы строим план, и я хочу, чтобы он покрывал всё, что нужно для выхода примерно на 90% прохождения тестов. Для этого, вероятно, стоит разбить PRD на какое-то подмножество тестов, за которое разумно взяться в первую очередь. Но будущие агенты могут поменять порядок, если это будет иметь смысл.

  8. По возможности старайся сделать реализацию независимой от конкретной JS-среды. Запускать мы будем в Node, но если когда-нибудь захотим запустить это в браузере поверх SQLite-on-WASM, это не должно быть невозможно. Код тестовой обвязки может содержать Node-специфичные вещи, если без них не обойтись, но сама библиотека, которую мы пишем, должна стремиться быть средонезависимой.

  9. В конце у тестового набора должен быть файл-манифест: какие тесты проходят, какие падают, какие упираются в тайм-аут и так далее. Это хороший способ оценивать прогресс по тестам и подсказывать следующему агенту, за что браться дальше. В идеале в этом файле-манифесте должны быть комментарии, чтобы агенты понимали, какие тесты сложные или вообще невозможные. Для формата, возможно, хорошо подойдут TOML или YAML.

  10. Ты работаешь в песочнице с sudo, так что если нужно установить какой-то инструмент, просто установи.

  11. Проект считается завершенным, когда ты достигаешь 90% прохождения тестов IndexedDB из WPT. Обрати внимание: этот процент должен считаться по количеству проходящих тестов, а не по количеству проходящих файлов с тестами.

  12. Тестовый скрипт должен ВЫВОДИТЬ манифест проходящих и падающих тестов. Так следующий агент сможет понять, какие тесты проходят, а какие падают, БЕЗ необходимости реально запускать тесты, потому что это занимает время. Кроме того, этот файл-манифест нужно коммитить в Git при каждом коммите.

  13. Для простоты тесты должны использовать подпроцессы или воркеры для изоляции, а не какой-нибудь подход на базе vm: он может привести к cross-realm-проблемам в JavaScript, например к тому, что typeof Array будет работать не так, как ожидается.

  14. Для целей этого проекта «одной задачей» нужно считать ОДИН ТЕСТ — ну, может быть, два — за раз, чтобы всё оставалось простым. Не пытайся откусить сразу огромную фичу IndexedDB целиком, например курсоры или индексы. Вместо этого разбивай работу на небольшие куски.

  15. Главная цель проекта — соответствовать спецификации, но производительность тоже приветствуется. Постарайся использовать возможности SQLite для максимальной производительности и не имитируй их на чистом JavaScript. Если задача сводится просто к «улучшить производительность», это тоже нормально.

На английском языке:

Help me plan a project. You have the entire IndexedDB spec and web-platform-tests checked out in git submodules.

Here’s the project: build a TypeScript-based project that implements IndexedDB in raw JavaScript (no dependencies) on top of SQLite (so okay, SQLite is the one dependency). You should try to pass at least 90% of the IndexedDB tests from WPT.

Stipulations:

  1. Use TypeScript and run in native Node (you have Node v24 already installed which supports TS out-of-the-box). Use tsc for linting though

  2. Write tests using node:test

  3. You must run the WPT tests UNMODIFIED in Node. To achieve this you will no doubt have to use some shims since the tests were designed to run in the browser, not Node. But as much as possible, you should prefer built-ins. Node supports a lot of built-ins now like Event and EventTarget so this shouldn’t be super hard.

  4. You should start first by setting up the basic project scaffolding and test scaffolding. To start, try to get ONE test passing, even if you have to do a basic pure-JS implementation of IndexedDB (i.e. a “hello world”) to get that to work.

  5. You should store some of these basic stipulations and project structure in CLAUDE.md as you go for the next agent. E.g. how to run tests, how to lint, etc.

  6. Your implementation should ultimately store data in sqlite. You should use the better-sqlite3 package for this. Again, no dependencies other than this one. (You may have as many devDependencies as you want, e.g. typescript)

  7. We’re building a plan, and I want this plan to encompass everything that’s needed to get to roughly 90% test coverage. To do so, we should probably divide up the PRD into some subset of tests that make sense to tackle first, but we can leave it up to future agents to change the order if it makes sense

  8. As much as possible, try to make your implementation JS-environment-agnostic. We’ll be running in Node, but if someday we want this running in a browser on top of SQLite-on-WASM then that shouldn’t be impossible. Your test harness code can have Node-specific stuff in it if necessary, but the actual library we’re building should strive to be agnostic.

  9. In the end, your test suite should have a manifest file of which tests are passing, failing, timing out, etc. This will be a good way to judge progress on the test suite and give guidance to the next agent on what to tackle next. Ideally this manifest file will have comments so that agents know if certain tests are tricky or outright impossible (toml or yaml may be a good format).

  10. You’re running in a sandbox with sudo so if you need to install some tool just do it.

  11. The project is complete when you reach 90% test coverage on the IndexedDB tests in wpt. Note that this number should be based on the number of passing tests, not the passing test files.

  12. Your test script should OUTPUT the manifest of passing/failing tests. This allows the next agent to know which tests are passing/failing WITHOUT having to actually run the tests (which takes time). You should also commit this manifest file whenever you commit to git.

  13. For simplicity, your tests should use sub-processes/workers for isolation rather than any kind of vm technique since this can introduce JavaScript cross-realm issues (e.g. typeof Array not being right).

  14. For the purposes of this project, “one task” should be considered to be ONE TEST (or maybe two) at a time to keep things simple. Don’t try to bite off huge entire feature of IndexedDB (e.g. cursors, indexes, etc.) and instead try to break work up into small chunks.

  15. The main goal of this project is to be spec-compliant, but being performant is great too. Try to leverage SQLite features for maximum performance (and don’t fake it by doing things in raw JavaScript instead). If a task is just “improve performance” then that’s fine.

А вот сам проект на GitHub

Если по истории Git это не видно, сложнее всего было просто удержать цикл в рабочем состоянии. Несмотря на безжалостность Bash-цикла, Claude Code время от времени падал с ошибкой:

Error: No messages returned
    at FKB (/$bunfs/root/claude:6151:78)
    at processTicksAndRejections (native:7:39)

Похоже на баг. Раздражает, но не критично: я просто перезапускал цикл после падения. Так что «за ночь» он не закончил, но к концу моего завтрака всё уже было готово.

Оцениваем код

Если посмотреть на структуру проекта, она довольно прямолинейная, а файлы называются привычно — по крайней мере для меня: IDBCursor.ts, IDBFactory.ts и так далее. Это неудивительно: проект следует соглашениям об именовании из спецификации, а также паттернам проектов вроде fake-indexeddb, который, уверен, был частью обучающих данных LLM. Тестовой обвязке приходится добавлять shim-прослойки для некоторых браузерных API вроде window.addEventListener и ImageData, чтобы часть тестов проходила. Ровно то же самое мы делали и в fake-indexeddb.

По данным cloc, в директории src 4395 строк кода. Я посмотрел на несколько мест, которые, как я знал, должны быть сложными, например на диспетчеризацию событий, и не удивился, увидев стратегию, похожую на fake-indexeddb: там добавлена shim-прослойка для логики отправки событий и обработчиков, а не используется встроенная реализация Node.js. Это действительно не такая простая штука.

Но интересно, что в одном месте проект отошел от fake-indexeddb: он реализовал собственную логику structuredClone через v8.serialize(). Думаю, причина в том, что, в отличие от fake-indexeddb, здесь нельзя просто держать JavaScript-объекты в памяти: их нужно сериализовать в SQLite. Так что, даже если можно сказать, что проект подглядывает в обучающие данные, в этом конкретном месте он делает что-то вполне своеобразное.

Что касается планировщика транзакций, он совсем не похож на логику fake-indexeddb, но выглядит здраво спроектированным и как минимум читаемым. Ну и, конечно, есть sqlite-backend.ts: он отличается от единственной сопоставимой реализации, о которой я знаю, — IndexedDBShim, — тем, что содержит нормальный бэкенд для SQL-логики, а не смешивает SQL с API-слоем, как это делает IndexedDBShim. На мой взгляд, там это выглядит несколько костыльно.

Одна неприятная особенность стиля кода: он почти не ссылается на саму спецификацию. Если читать fake-indexeddb или исходники браузеров — особенно Ladybird и Servo, по моему опыту, — там часто встречаются комментарии с дословными формулировками спецификации. Это удобно: спецификация и так часто написана почти как псевдокод, а такие комментарии помогают читателю понять, действительно ли браузерная реализация соответствует спецификации. Claude, похоже, полностью этого избегал. Возможно, он целиком полагался на WPT, а возможно, просто не видел смысла в дословных комментариях.

Еще во время ревью кода я заметил, что агент немного приврал насчет процента прохождения: из исходных файлов с тестами, на которые он нацелился, 9 завершились с падением, и поэтому их не включили в знаменатель — видимо, потому что агент не знал, сколько тестов там должно было запуститься. Так что «настоящий» процент прохождения на самом деле 92%, если считать все упавшие тесты неуспешными: 1208 / 1313. Настоящий знаменатель я взял из wpt.fyi. Хотя, справедливости ради, 95% — корректная цифра для файлов с тестами, которые запустились без падения.

В качестве финальной проверки я прогнал код на собственном WPT-наборе fake-indexeddb — просто чтобы убедиться, что никакого мухлежа нет и LLM не подбирала тесты выборочно, чтобы выглядеть лучше. Эти два набора тестов не совпадают один к одному: агент решил пропустить несколько крупных, но сложных тестов вроде IDL harness, плюс есть 9 упавших тестов, о которых я говорил выше. Поэтому тесты самого fake-indexeddb дают более точный способ оценить этот код на фоне сопоставимой реализации IndexedDB.

В этом более строгом тесте реализация набирает 77,4%, что неплохо смотрится на фоне 82,8% у самого fake-indexeddb — отставание всего около 5%. Можно также сравнить с браузерами:

Реализация

Версия

Пройдено

%

Chrome

144.0.7514.0

1651

99,9%

Firefox

146.0a1

1498

90,6%

Safari

231 preview

1497

90,6%

Ladybird

1.0-cde3941d9f

1426

86,3%

fake-indexeddb

6.2.5

1369

82,8%

One-shot

1279

77,4%

77,4% против 82,8% — это правда неплохо, учитывая, что fake-indexeddb около 10 лет и над ним работали 15 контрибьюторов. Хотя, по моим ощущениям, после отметки примерно в 40% у вас уже есть в основном рабочая реализация: многие WPT проверяют пограничные случаи или особенности IDL, например является ли свойство enumerable/configurable или нет.

One-shot-реализация на самом деле проходит 30 тестов, которые падают в fake-indexeddb, в основном в области тестов IDL harness. А 88 тестов, которые проходит fake-indexeddb, но не проходит one-shot-реализация, в основном связаны со структурированным клонированием и сериализацией Blob-объектов, свойствами объекта IDBCursor, ошибками для недопустимых ключей вроде отсоединенных ArrayBuffer и другими пограничными случаями.

WPT-тесты fake-indexeddb также выполнились за 49,2 с против 125,5 с у one-shot-реализации — в 2,5 раза медленнее, медиана по 3 прогонам. Так что по производительности там точно есть куда расти. Хотя, справедливости ради, мы сравниваем реальную реализацию с постоянным хранением в SQLite и in-memory-реализацию, а уж над оптимизацией fake-indexeddb я в свое время поработал как следует. Подозреваю, еще одна причина в том, что агент выбрал простой setTimeout для постановки задач в очередь, тогда как в fake-indexeddb мы использовали куда более оптимальную стратегию.

Вывод

В последнее время я много говорю о LLM и о том, как они изменили мой процесс разработки. Значительная часть моей аудитории относится к LLM настороженно из-за этических вопросов: энергопотребления, авторского права, мотивации крупных технологических компаний и так далее. Но моя цель была просто показать, что эти штуки работают. От них было бы легко отмахнуться, если бы технология была просто раздута хайпом, но — к некоторому моему сожалению — она действительно работает.

Этот эксперимент хорошо показывает, насколько далеко продвинулись новейшие модели вроде Opus 4.5: если дать достаточно хороший промпт с четкими тестами и спецификацией, можно лечь спать и проснуться утром уже с рабочей кодовой базой. До LLM реальные независимые реализации IndexedDB, вероятно, можно было пересчитать по пальцам двух рук: примерно пять браузерных вендоров плюс fake-indexeddb и IndexedDBShim. А теперь новую можно создать по запросу.

И стоило это тоже не так уж дорого: проект съел примерно 20% моего недельного бюджета на месячном тарифе Claude за 100 долларов, так что будем считать, что он обошелся мне в 7 баксов. Конечно, кто-то скажет, что сейчас эти расходы субсидируются и, скорее всего, вырастут — спорить не буду. Но всё же: сегодня вы платите именно столько. Новую реализацию IndexedDB можно получить примерно по цене порции картошки фри в модном пабе.

И куда этому проекту двигаться дальше? Если бы это было пять лет назад и у меня на руках оказалась более-менее приличная реализация IndexedDB, я бы выложил ее в open source, опубликовал в npm, принимал PR и так далее. Сейчас я в этом особого смысла не вижу. Вы сами можете получить версию лучше, если сделаете не one-shot, а two-shot. Или придумаете более удачный one-shot. Или соберете ее поверх LevelDB, Rust или чего угодно еще.

Что я по этому поводу чувствую? Честно говоря, ничего хорошего. За последний год я вложил кучу времени в fake-indexeddb, вообще без AI — только на своем хилом приматском интеллекте. Мне нравился процесс, и я об этом не жалею, но такие эксперименты обесценивают усилия, которые я вкладывал годами. Они снижают ценность вещей. Думаю, отчасти поэтому у многих из нас возникает рефлекторное желание отвергнуть эти инструменты: если они действительно работают, это, откровенно говоря, оскорбительно.

Но я не думаю, что я или кто-то еще сможет просто взять и отменить LLM силой желания. С учетом их возможностей уже становится очевидным, что они будут ключевой частью разработки ПО в будущем. Может быть, это хорошо, может быть, плохо, но их доминирование теперь кажется мне неизбежным. Впрочем, я стараюсь не смотреть на это совсем уж мрачно: если следить за некоторыми «AI-инфлюенсерами» вроде Мэтта Покока, Саймона Уиллисoна и Стива Йегге, видно, что им невероятно весело. Как недавно сказал мой бывший коллега по Edge Кайл Пфлуг:

AI-first-разработка снова делает создание чего-то в вебе радостным и по-настоящему доступным для каждого. Мне не хватало этого ощущения с тех пор, как View Source стал нечитаемым, и это та самая светлая сторона, которая подоспела как раз вовремя.

Как занудный мужик среднего возраста, который пытается понять, чему так радуется молодежь, я вынужден согласиться. Даже если лично мне вайб-кодинг сейчас не кажется особенно радостным занятием, я понимаю, почему другим он так нравится: он дает огромную творческую мощь и резко снижает порог входа. Саймон Уиллисoн прогнозирует, что к 2029 году мы увидим веб-браузер промышленного уровня, собранный небольшой командой с помощью AI. И я бы не стал с ним спорить.


Эксперименты с LLM редко заканчиваются на одном удачном промпте: дальше приходится разбираться с качеством результата, тестами и ограничениями инструментов. Эти темы можно продолжить на бесплатных уроках — посмотреть практические подходы, задать вопросы экспертам и оценить формат обучения.

  • 18 июня, 20:00. «Тесты, которые чинят себя сами: практика ИИ в UI-тестировании». Записаться

  • 22 июня, 20:00. «Продвинутое структурирование промптов: как получать предсказуемый результат». Записаться

  • 29 июня, 20:00. «Обзор ИИ-технологий для разработчиков: от идей до рабочих решений». Записаться

Больше бесплатных уроков июня смотрите в дайджесте.

Комментарии (3)


  1. Dhwtj
    16.06.2026 18:56

    Стоит прочитать первый комментарий к оригинальному посту

    Судя по вашему резюме и предыдущим работам, вы, скорее всего, легко впечатляемый посредственный человек с большим рупором. Как и большинство поклонников искусственного интеллекта.


  1. saitporyadke
    16.06.2026 18:56

    Самое полезное — не «95% за ночь», а честный пересчёт до 77,4% на втором наборе. Без внешней приёмки любой one-shot выглядит готовым продуктом. WPT здесь не декорация, а контракт: спецификация + тесты, и модель не может «нарисовать галочку». В прикладной разработке то же: сначала воспроизводимый кейс, потом фикс — иначе «работает у меня». Ralph loop с короткими сессиями узнаваемо: длинный контекст в дебаге клиентского сайта деградирует так же, как в чате с моделью.


  1. Ra2007
    16.06.2026 18:56

    Главное тут не модель, а то что у IndexedDB есть WPT как исполняемое определение «готово». Гонял Claude в похожем цикле на TS миграции, где тестов почти не было, агент уверенно зеленил то что сам же и придумал, дрейф пошёл сразу. Там где тесты заданы заранее, цикл сходится, где нет, ночью получаешь красиво оформленный мусор. Спека на английском плюс приёмочные тесты решают больше чем выбор модели.