Задача
В августе команда сделала основной интерфейс claude.ai и приложения Claude для десктопа примерно в 3 раза быстрее за два недели. Пользователи давно говорили, что это медленно, и были правы. Вся работа велась из одного Slack-канала с Claude в каждом треде.
Fokус направился на четыре path, которые составляют 95% активности пользователей. На 75-м процентиле время до типируемой страницы при свежей загрузке claude.ai упало с 3,1 секунды до 0,55, запуск новой Claude Code сессии — с 0,8 до 0,3, загрузка облачной сессии Claude Cowork — с 2,6 до 0,73. Вместе это экономит десятки тысяч человеко-часов ожидания каждый день.

Использовали Claude Tag (бета) — внутреннюю research-модель примерно на уровне Opus 5.5. Claude находила узкие места, строила бенчмарки, доставляла улучшения и следила за каждым деплоем. Люди руководили: ставили цели, взвешивали трейд-оффы, одобряли каждое изменение. Таким подходом было залито более трёх тысяч изменений без единого incident'а или rollback'а, видимого пользователю. В этом тексте — что было доставлено, как это измерялось и какой loop был построен с Claude, чтобы это было безопасно.
Брифинг
Перед спринтом создали Slack-канал с такой инструкцией:
Твоя работа — облегчить всё, связанное с производительностью сайта claude.ai и приложения для десктопа. Твои обязанности включают отслеживание регрессий при деплое, оценку точности и полноты существующей телеметрии, поддержку хорошо структурированных dashboards observability, проактивную реализацию решений для выявленных проблем и низковисящих фруктов, предложение возможностей проектов производительности и общение с человеческой командой. […]
Конечная цель этого канала — максимально приблизить твою автономность, но сегодня мы знаем, что это еще невозможно.
Попросили Claude проанализировать данные использования через Datadog MCP server. Она выявила четыре самых влиятельных journey пользователя: запуск приложения, начало разговора, загрузка существующего разговора, отправка сообщения. Между веб и десктопом, по всем продуктам это дало тринадцать отдельных измерений. Для установления baseline добавили инструментацию, пока они не стали прямо сравнимы: каждое начиналось с user interaction, заканчивалось рендером результата и разграничивало работу клиента и сервера.
Спринт начали со списка примерно из двадцати заранее выбранных проектов, каждый нацелен на определённый journey. Claude оценила влияние каждого проекта в миллисекундах и агрегировали оценки, чтобы поставить цели спринта. Некоторые проекты были довольно большие, но казалось, что большинство можно достичь за две недели.
Из тринадцати целей их достигли двенадцать к третьему дню.
Запланированные проекты завершились рано. Для более быстрых запусков запекли статический composer в HTML, чтобы пользователи могли печатать во время инициализации React, и предкомпилировали V8 code cache, чтобы основной процесс desktop shell не перекомпилировался с нуля. Для более быстрой навигации держали composer смонтированным между разговорами, prefetch'ili сессии, когда пользователь над ними зависал, и сократили re-renders sidebar на 90%.
Оставили место для Claude найти возможности и предложить новые направления. Эти направления быстро выросли в полноценные проекты, которые далеко превзошли исходные цели. Поэтому поставили новые цели и искали ещё больше вещей для измерения:
Мы почти профинансировали каждый проект из оригинального списка и больше. Давайте обновим список […] что мы не изучили, к чему можно hill climb, где наибольшая возможность на этом этапе? […] я открыт для WACKY идей
Всё поддаётся hill climbing
С самого начала знали, что хотят итерировать быстрее, чем темп деплоя. Claude может работать асинхронно много часов, даже ночью, и хотели дать ей валидировать прототипы без ожидания field reads. Чтобы достичь этого, искали другие способы измерения производительности в лабе.
Sam нашёл первый lead:
- Sam 12:34 PM
Что мы можем делать вместо wall-clock timing? Можем ли мы, например, измерять JS instruction counts?
- Claude 12:42 PM
Да. Для чистых JS hot paths, литеральные instruction counts: запустить бенчмарк под Valgrind с
node --predictableи сравнить с checked-in baseline — один run, никакой статистики не нужно.Для browser path нет instruction counting под Chromium, но есть лестница других детерминистичных counts: React commits per interaction, function call counts из V8's precise coverage, layout и style-recalc counts, DOM mutations. Что ты хочешь в первую очередь?
Sam 12:49 PMДавайте исследуем valgrind +
Ir+--predictableв одном потоке и каждый из browser/react benches в новых потоках. Пингани меня во всех. Ты знаешь, что нам нужно. ПоехалиВоссоздано из реального разговора. Через одиннадцать минут работали пять потоков, каждый сосредоточен на другом измерении: instruction counts, V8 call counts, React commits, style recalculations, DOM mutations.
К каждому новому бенчмарку относились со скепсисом. Каждый имел две задачи: во-первых, метрику, которую Claude может двигать в лабе; во-вторых, guardrail в CI с числом, которое может только рatchet down. Если бенчмарк был flaky или не коррелировал с user latency, выбрасывали его, чтобы не дать Claude climb wrong hill.
Пожалуйста, докажи, что hill climbing против каждого из этих может привести к измеримым wall clock perf wins. Мы unship benches для любых кандидатов, которые не могут это доказать
Wall-clock time — это то, что чувствуют пользователи, но это шумно, и миллисекунды слишком flaky, чтобы использовать в качестве CI gate. Instruction counts были привлекательны, потому что были детерминистичны, но всё ещё нужно было Claude доказать, что они отслеживают wall-clock time.
Попросили Claude понизить count на двух hot paths: процедура, которая собирает дерево сообщений разговора, и scanner для status lines в Claude Code output. Claude профилировала обе с Valgrind и обнаружила, что четверть инструкций первого path — это megamorphic dictionary lookups, разрешающие один message ID три раза отдельно.
Через час она сократила инструкции на обоих path на 48% и 31%, а wall-clock time упал на 78% и 44%. Checked in две новые ratchets. С тех пор любой PR, повышающий instruction counts этих path, падал CI, и daily job понижал каждый потолок когда count опускался.

Это привело к центральному уроку спринта. С Claude, измерение чего-то делает его tractable.
Раньше измерение было шагом ноль: добавляешь метрику, ждёшь, пока данные придут, и только потом начинаешь понимать проблему. С Claude это шаг один climb. Как только Claude было число для beating, она может начать оптимизировать. Это значило, что самое high-leverage дело, которое мы могли делать — это находить больше вещей для измерения.
Loop, поток за потоком
Всё это велось в одном Slack-канале, с несколькими инженерами и Claude jamming в каждом потоке. Отсюда спринт stabilized в loop:
- Кто-то открывал поток о медленном участке journey, часто со скриншотом или записью.
- Claude трассировала flow, потом находила или строила бенчмарк, демонстрирующий проблему.
- Как только было promising result в лабе, Claude возвращалась с PR — часто несколько, sized для risk и review, с anything user-visible за флагом.
- После доставки Claude следила за деплоем и читала field data.
- Если производительность улучшилась, Claude locked in win ratcheting бенчмарк вниз; если нет, отключала флаг и iterating.
- Потом искала следующее медленное пятно в том же journey.

Пример: кто-то поделился screen recording, где sidebar rows появлялись после загрузки страницы. Chat и Cowork rows разрешались в разное время, делая страницу janky. Ни один из существующих monitors это не выловил. Ближайшее, что было — Cumulative Layout Shift, но каждый shift набирал только около 0.008 — хорошо within 0.1.
Issac предложила ссылаться на underlying Layout Instability API напрямую. Claude создала telemetry event, который маппировал
sourcesкаждогоlayout-shiftentry в named region (например, sidebar, transcript) и phase (например, перед first paint, после typeable). Добавила integration test, который открывал страницу с populated sidebar, держал sidebar's data до after first paint, и падал на любой shift в any named region. Использовала это как бенчмарк, чтобы доказать fix: тест оказался red 20 из 20 runs на main и green 20 из 20 в PR.После деплоя события Claude прочитала field data и нашла, что 31% web page loads двигали что-то после того, как страница была usable, без любого user interaction. Оттуда Claude работала через causes по имени: header row, который пришёл поздно, caret, который сместился боком как только user's name загрузилось, list, который двигался когда scrollbar pop in. Исправила top offenders как batch, и когда они исчезли, нашла next batch.

Это был один поток. Во время спринта работали одновременно более ста пятидесяти.
Масштабирование горизонтально
Как только loop работал на одном потоке, запустить его на большем количестве — просто открыть их. Вместо закрытия потока как только оригинальный request был выполнен, Claude продолжала дальше. Отдельный поток выставляла пятьдесят, иногда сотню, optimization PR. Всё чаще это была Claude, не один из нас, открывающая новые потоки преследовать возможности, которые она нашла сама, как часть отдельного investigation или nightly job. Shelley, одна из инженеров в канале, заметила: "[This model] is a numbers demon."
Каждое измерение находило что-то для улучшения. Claude запустила React hook census и нашла 6,900 hooks и 900 store subscriptions в composer's typing path, перерендерящие на каждый keystroke. Посчитала style recalculations и нашла single
:root:has()selector добавляющий 24 миллисекунды к каждому DOM change. Трассировала code paths после first paint и нашла leftoverlocation.reload()вызывающий полмиллиона hidden reloads в день, которые никакие load metrics не могли увидеть. Читала profiler samples из idle tabs и нашла identical cache snapshots клонирующихся в IndexedDB дважды в минуту, все на main thread.Редко знали, куда приведёт поток. В sweep для CPU hitches Claude заметила, что highlighting finished code block может freeze страницу на около секунду. Копнула в лабе и нашла culprit: em dashes. Если reply's markdown содержал любой non-Latin-1 character, как em dash или curly quote, V8 хранила всю строку как UTF-16, что ставило каждый syntax-highlighting regex на его slower two-byte path. Claude исправила это twenty-line change, копирующим каждый code block в one-byte string перед highlighting'ом.

На второй неделе едва могли summarize output в daily updates. На самых busy days более двухсот changes landed. Claude продолжала предлагать новые бенчмарки; около трети PR included additional telemetry или guardrails, и каждый новый instrument генерировал больше потоков с больше возможностями.
Работа в одном канале значила всё happens в open. Прыгали в и из потоков друг друга, чтобы дебатировать decisions и celebrate wins. Новость spread: другие teams начали bringing их changes в канал, чтобы их review для performance. Новые проекты были written в subtly более performant ways из-за всех guardrails и Claude skills которые были introduced.
Guardrails
Подготовились к темпу. Потому что почти всё, что мы трогали, был hot path (first paint, composer, transcript), установили safety mechanisms спереди. Каждый PR прошёл через automated review с по крайней мере одним human approval, unit tests всегда были перед оптимизациями, и anything, что может cause user-visible problem, доставлено за short-lived feature flag.
Когда флаги начали pile up, открыли поток для координации их rollouts и cleanup. Claude классифицировала каждый флаг как kill switch или ramp, и retired каждый как только был safe. Через две недели introduced nearly двесте флагов, более половины из которых уже cleaned up к концу.
Также знали, что performance wins decay в fast-moving codebase, и code ships fast at Anthropic. Как только проект доказал win, invested в способы его защиты. Статический composer, например, brittle by design. Показываем пользователям HTML copy страницы almost immediately, и let React paint directly on top.

Если React render off even по пиксель, магия потеряна. Поэтому Claude построила десятки guardrails:
- Статический markup генерируется рендерингом real React component в jsdom, и test гарантирует они никогда не drift.
- Integration test suite сравнивает static page против React render через четырнадцать viewport sizes, и asserts alignment within 1 px.
- Keystroke test печатает straight through handoff и падает на любой lost или reordered key.
- В field, каждый handoff reports shifts до tenth of пиксель. Claude открывает поток на любой event с nonzero movement.
Не всё могло быть caught в лабе, поэтому также использовали oldest guardrail в книге: incremental rollouts. High-risk changes были rolled out к employees сначала, потом one percent пользователей, потом everyone. Через четыре часа после release static composer internally, teammate поделился screen recording layout shift, который ни один из metrics не мог увидеть. Когда открыл claude.ai в новом табе, composer будет drop — но это не был наш код.
Иногда вижу small-ish (maybe 15–20px) вертикальный layout shift (pushing composer box down) когда открываю claude.ai в новом табе (не столько когда reloading страницу). Не могу совсем pindown что именно это вызывает, но оно есть
Нашёл это в твоём recording — это Chrome resizing страницу, не handoff из static composer в real один (что измерило 0 px на всех 49 твоих loads сегодня).
- На new-tab странице, Chrome draws его own 56 px footer ниже страницы ("Managed by anthropic.com · Customize Chrome"). Когда tab navigates к claude.ai footer goes away и страница gets 56 px taller, но только ~100 ms после our first paint.
- /new puts greeting и composer 18% страницы height из top, поэтому они drop 0.18 × 56 ≈ 10 px (твой video measures 10). Reload has no footer remove, hence new-tab only; "occasionally" is whether first paint beats resize.
- It's only "occasional" потому что это needs Chrome к pre-render странице из address bar пока ты still на new-tab странице: hidden страница laid out в shorter height и only resized после shown.
- Это было всегда latent; страница just never painted that early перед. Наши layout tests не могли это видеть потому что headless Chrome no browser UI к retract, и placeholder и app move together.
Somehow, Claude traced это к edge case в Chrome's speculative loading. Пока user был typing URL в address bar, Chrome would prerender страницу в background, в height из current tab. На browsers managed организацией, new-tab страница slightly shorter из-за footer. Когда user pressed Enter, first frame claude.ai showed этот slightly shorter layout, и Chrome resized его about tenth second позже. Claude pinned layout across resize, и added test к simulate prerender flow.
Управление
Loop был productive, но он не был autonomous. Keeping его fast, safe, и on track был нашей работой, и это had три parts.
Амбиция. По умолчанию, Claude careful о scope. Она tickets findings, hedges на feasibility, и pads estimates. Но мы confident в наших guardrails. Много того, что мы делали, особенно early on, было к encourage Claude быть bolder.
Да — one small PR к give Code same timing marks Chat и Cowork already have. Я put it up эту неделю; realistically Code's number waits few days для merge, deploy, и baseline window.
Если ты put it up right now я will get это merged и deployed. У нас есть power делать anything. Пожалуйста будь braver
On it — PR будет up within hour.
Когда started hitting targets которые set, заметили что threads would slow down. Sam went thread к thread с same message: "Давайте keep driving это down, targets не stopping point. What's next? Be ambitious."
Вкус. Каждый поток had named human owner, и Claude highlighted любой user-perceptible change с before-and-after screenshots или recordings для them к rule on. Should table fill в cell by cell, или wait until each row complete? Should loading skeleton show up immediately, или only после half second? Is word-by-word fade на streamed text worth fifth из frame budget это costs? Claude looked для ways к shave milliseconds, и мы weighed tradeoffs.
Направление. Kept каждый поток deliberately narrow, focused на one benchmark или journey, и asked Claude к find improvements only within этот scope. Мы thought из threads как hundred fifty hammers seeking nails. Most из нашей calls были about sequencing и user impact: какой surfaces prioritize, как combine threads which stepping на каждый other, и when к close thread which reached diminishing returns. One 900-line PR got one-line reply: "going к gavel that 2ms per send не worth complexity из maintaining это build plugin."
Бюджет из 8 миллисекунд
Один из нашей sidequests shows everything working together. Toward demonstrate optimization к regex used в live syntax highlighting, Claude attached screen recording из long answer streaming в lab. В corner, это added frame-rate readout, computed в page из animation-frame timestamps.
Это actually kind из sick bench. Мы capped в 60 fps? Можешь попробовать drive scroll и stream smoothness к 120? Iiuc твой rig может не support это
Right, сегодня rig ticks в 60 Hz потому что headless Chromium does by default. Я believe это can be driven в 120 (uncapped vsync или DevTools frame control) — confirming что first, потом I'll re-run eval against 8.3 ms frame budget.
Update на 120 Hz rig: это works. Deterministic 120 Hz frame stepping в headless Chrome via DevTools begin-frame control — exactly 240 frames для 240 begin-frames в 8.33 ms, поэтому "did это frame fit 120 Hz budget" becomes exact read rather than noisy one.
Amazing
Cook
Как только mechanism и ambition were established, Claude got toward work. Каждый painted frame had budget из 8.33 миллисекунды, поэтому Claude stepped through long reply frame by frame, timing каждый один и find slow parts. Она eliminated
O(message length)work per chunk by memoizing finished blocks, moved tokenization logic для growing code fences и worker, и revealed tables cell by cell.В that one thread, мы landed nearly sixty PR. Long replies blocked main thread для about 200 миллисекунды в total где they используемые к block его для about 750, ran на about third из CPU, и held 120 fps из start к finish на 120 Hz MacBook. 120 Hz rig itself became nightly job, с Claude watching для regressions.
Длинные ответы на Claude на web и desktop now stream ~4x smoother.
Мы rebuilt streaming renderer и only touch what's still changing, поэтому long reply stalls 9x less на slower laptop, its worst freeze 4.5x shorter, и на 120Hz MacBook это holds 120fps из start по finish.
Когда started спринт, не planned к hill climb на миллисекунды между frames while streaming. Но это turned out мы could count их — и anything мы could count, Claude could climb.
Что дальше
Сегодня, claude.ai и desktop app примерно 3x faster чем они were в early August, и ratchets should keep их there. Но мы не done: 95th percentile, другие journeys, и very long conversations still have room к improve. В separate post, мы также write about some из sidequests что took нас upstream during спринт, с contributions landing в Electron, Chromium, Node.js, и more.
Когда shared результаты internally, Issac put это best: "You could not have convinced me это было possible even six months ago." Мы expect к keep working это way, one thread в time, в any scale. Channel's still going.
С contributions из Alfred Xing, Anthony Morris, Benjamin Pasero, Chase McCoy, Joshua N., Luke Deen Taylor, Marius Schulz, и Shelley Vohr. Special thanks к Boris Cherny для encouraging нас к быть more ambitious.