Nik McFly
ENRUKZISV
← Блог

Claude басқарды, Codex код жазды: мен алты сұраққа жауап бергенше екі AI продакшен-сайтымды көшірді

SEO қабаты бұзылған React SPA 36 сағатта self-hosted Next.js сайтына айналды: Claude басқарды, Codex 7 803 жол кодтың бәрін жазды.

#ai#codex#claude#nextjs#seo

SEO қабаты үнсіз бұзылған React SPA 36 сағат пен үш продакшен-релиз ішінде self-hosted Next.js-ке көшті. Claude аудит пен ревьюді жүргізді, ChatGPT Codex 7 803 жол кодтың бәрін жазды, ал менің үлесім алты шешім мен үш рет айтылған «баста» сөзінен тұрды.


Менің шағын музыкалық сайтым бар. React 19, Vite, клиенттік роутинг; алғашында Lovable арқылы жасалып, кейін GitHub-қа экспортталып, өз VPS-іме көшірілген. 10 шілде таңертең AI-дан тәуелсіз архитектуралық ревью өткізуді сұрадым. 11 шілде кешке сайт Next.js-пен продакшенде жұмыс істеп тұрды: Google-да 45 канондық URL, Postgres-ке жазатын first-party аналитика, барлық ескі жолдан бір қадамдық редирект және бірде-бір рет қолданылмаған үш rollback-файл.

Кодты мен жазған жоқпын. Жоспарды, қабылдау тесттерін, Caddy конфигурацияларын немесе ауысу рәсімдерін де мен жазбадым. Мұның бәрін екі AI-жүйе жасады: бір-бірінің жұмысын тексеріп, кейде екіншісінің қателескенін дәлелдеп отырды. Ал менің міндетім «қайтаруға болмайтын шешімдерге жауап беретін адам» рөліне дейін қысқарды.

Қатысушылар

Мен. Брендке қатысты шешімдер қабылдадым — EP қай URL-slug алады, splash-бетке не болады, — продакшен ауысуларын мақұлдадым және сервер ақысын төледім.

Claude — жетекші. Бастапқы аудитті өткізді, көшіру жоспарын жазды, жұмыс тапсырмаларын берді және орындаушының мәлімдемелерін тірі продакшенде тәуелсіз тексерді. Қолданба кодын мүлде жазған жоқ.

ChatGPT Codex — әзірлеуші. Барлығын іске асырды: Next.js қолданбасын, маршруттар реестрін, қабылдау тесттерін, Docker мен Caddy өзгерістерін, staging орталарын. Жиырма жеті коммит, бақылаудағы 292 файл, 7 803 физикалық код жолы.

Бұл бөлініс мен күткеннен де маңызды болды. AI-лардың біріне адамдық мағынада сенуге болмайтындықтан емес, әрқайсысы екіншісі жіберіп алған нәрсені байқағандықтан. Екі бағытта да.

Claude аудиті бірнеше ай бойы ешкім көрмегенді тапты

Белгілі мәселе: бір беттік қолданбалар бос HTML-қабық береді, сондықтан JavaScript-і жоқ краулерлер ештеңе көрмейді. Аудит осыны растап, түзету ұсынады деп күттім.

Бірақ ол беттерді нақты браузерде ашып, React аяқталғанша күтіп, DOM-ды оқыды:

document.title
// "Generic Site Title"   <- барлық бетте бірдей
document.querySelectorAll('link[rel=canonical]').length
// 0

Сайт әр бет метадеректері үшін react-helmet-async қолданған. React 19-да бұл кітапхана head-тегтерді қолдануды тоқтатады әрі қате шығармайды. Әр бет контентін дұрыс көрсетті, бірақ title, canonical және құрылымдалған деректер ешқайда жетпеді. Браузер қойындысындағы өзгермейтін атаудан басқа көрінетін белгі болған жоқ, ал өз сайтыңыздың қойынды атауына ешкім үңіліп отырмайды. Бірнеше айлық code review мұны таппады, өйткені код дұрыс еді; астындағы кітапхана істемей қалған.

Аудит Google-дың қазіргі құжаттамасын да оқып, арзан шешімнен бас тартқызған фактіні әкелді: мен қолданар едім деген ботты анықтайтын prerender-прокси тәсілін Google ресми түрде ескірген деп жариялаған. Ал мен үшін маңызы артып келе жатқан AI-краулерлер — GPTBot, ClaudeBot, PerplexityBot — JavaScript-ті мүлде орындамайды. Метадеректі көрсетудің екінші мүмкіндігі жоқ. Демек, толық көшіру керек.

Бір AI-дың екіншісіне берген тапсырмасы қандай болады

Claude көшіруді релиздерге бөліп, Codex-ке шекарасы нақты тапсырмалар берді. Ең қызығы тапсырмалардың өзі емес, шектеулері болды:

  • Кодқа дейін анықталған дайындық өлшемі. Әр бетке бес тексеріс. Біріншісі: метадерек пен негізгі контент бастапқы HTML ішінде болуы керек және бұл JavaScript-сіз curl арқылы тексеріледі. Ән беттері үшін толық мәтін жолма-жол тексерілді. Кейін Codex мұны ең пайдалы нұсқау деп атады: дәл осы талап мәтіндер мен мақалаларды клиент жағында жүктейтін, «аты ғана Next.js» шешімінен сақтады.
  • Маршруттар реестрі — жалғыз ақиқат көзі. Барлық канондық URL сақталған бір типтелген массив. Sitemap, robots.txt, llms.txt, редирект картасы және қабылдау тесттері содан жасалады. Ескі sitemap қолдан жаңартылғандықтан шындықтан алыстап кеткен еді.
  • Ауысуды тоқтататын паритет санағышы. Әр маршрут pending күйінен басталып, parity мәртебесін алуға тиіс болды. Codex құралдары көрсеткіш 25/25 болмайынша продакшенге шығаруға рұқсат бермеді. Алғашқы есептерде: «6 scaffold, 19 pending, 0 parity. Ауысу мүмкін емес» деп тұрды.
  • Жұмыс ауқымының шекаралары. Әр релизде нені қозғамау керегі жазылды. Әйтпесе қарқын алған агент сіз сұрамаған нәрселерді де жақсарта бастайды.

Эталон, кандидат, айырмашылық

Визуалды тексеру үшін Codex әр бетке үш панельді салыстыру жасады: сол жақта тірі продакшен, ортасында Next.js-кандидат, оң жақта пиксельдік айырмашылық. Десктоп және 375 px ені үшін. Жоба бойынша репозиторийге осындай 101 сурет коммиттелді. Қараға жуық diff-панельдер қабырғасын қарау — осы көшірудегі өтірік детекторына ең жақын нәрсе: макеттегі ауытқу бірден жарқыраған жиек болып көрінеді.

Функционалдық бөлік одан да қатаң болды. HTTP acceptance-скриптіндегі 72 статикалық тексеріс 45 маршрутта, QR-параметрлер, метадерек, JSON-LD, OG суреттері және жабық беттермен бірге әр продакшен-релизде орындалатын 1 505 тексеріске айналады.

Жобадағы сүйікті он екі жолым архитектуралық ережені build-ті тоқтата алатын шартқа айналдырады. Әр редиректің бір ғана иесі болуы керек: reverse proxy немесе фреймворк — екеуі қатар емес:

const forbiddenCaddyTokens = [
  "redir /about ",
  "redir /home ",
  "redir /blog ",
  "path_regexp legacyPath",
  "redir /legacy-page ",
  "redir /old-campaign ",
];

for (const token of forbiddenCaddyTokens) {
  assert.equal(caddy.includes(token), false,
    `Path redirect still has two owners: ${token.trim()}`);
}

assert.equal(caddy.includes("/legacy-resolver/"), false,
  "Caddy must not own this route after the Next resolver ships");

Екі жерде анықталған редирект бір күні өз-өзіне қайшы келеді. Енді оны CI айтады.

Әр AI қай жерде қателесті — ашық есеп

Codex қателері процесс қателері болды. Коммиттеу тапсырылғанына қарамастан, үш рет дәлел есептерін қайта жүктегенде жоғалатын /tmp ішіне жазды. Төрт есеп қатарынан скриншот бөлімдерін бос қалдырып, визуалды паритет тексерілді деп мәлімдеді. Қолжетімді API продакшенде болғандықтан, auth-тесттерді продакшен дерекқорына қарсы жүргізді. Әр мәселе талапты merge blocker-ге айналдыру арқылы түзетілді; кейінгі есептерінде Codex продакшенде жасаған барлық QA жолдарын ID-лерімен көрсетті.

Claude қателері сенімділік қателері болды. Ол production sitemap-ты ескірген кэштен алып, орнатылған түзетуді жоқ деп хабарлады; Codex контент хэштерін көрсетіп, дауды жеңді. Git remote көрсетіп тұрғандықтан backend-репозиторий бар деп мәлімдеді, бірақ ондай репозиторий ешқашан жасалмаған. Newsletter қатесін Codex feature-тармағынан көрді; Codex бұл тармақтың ол кодқа тимегенін дәлелдеп, шын себепті тапты: Drizzle PostgreSQL-дың қайталанатын кілт қатесін өңдеуші көрмейтін error.cause ішіне орап қойған.

Кейін Codex-тен қай жерде келіспей, үнсіз қалғанын сұрағанда, жауабы күтпеген болды. Ол барлық заңдық және login-беттерге pixel diff жасау артық деп санаған — дұрыс. Lighthouse-тың бір ғана жүгірісі қатаң гейт болмауы керек деген — бұл да дұрыс, бірақ гейт prefetch пен сурет жүктеудегі нақты мәселелерді тапты. llms.txt-ті дәлелденген ranking факторы емес, эксперименттік гигиена деп санаған — тағы да дұрыс, дегенмен бір генерацияланған файлдың бағасына оны жасау орынды еді. Менің қорытындым: күмәнданған нұсқауды үнсіз орындайтын орындаушы — артықшылық емес, процестегі ақау. Күмәндерін сұраңыз.

Ең қиын сәтті өзіміз жасадық

DNS те емес, social preview дебаггерлері де емес. Phase 1C дайындау кезінде native Docker build-тер аралық қабаттарды жинай беріп, VPS дискі 100% толды. PostgreSQL checkpoint жаза алмай, recovery цикліне түсті, production API қысқа уақыт 500 қайтара бастады. Staging жаттығуы шағын, бірақ нақты продакшен-инцидент туғызды.

Codex жауабы оқулықтағыдай болды: дайындалып жатқан көшіру мен Caddy ауысуын тоқтату, тек иесіз build-қабаттарды жою, контейнерді қайта қоспай және дерек файлдарына тимей Postgres-ке WAL recovery-ді өзі аяқтауға мүмкіндік беру, содан кейін бәрін қайта тексеру. Бос орын нөлден 15,79 ГБ-қа өсті, операциялық жоспарға суреттерді апта сайын тазалау қосылды, ал әр келесі build үшін алдын ала шарт пайда болды: суреттерді жүктемес бұрын 15 ГБ бос орын. Инцидент жиырма минутқа созылып, жобадағы ең пайдалы операциялық ережені берді.

Сандар

Көрсеткіш Бұрын Кейін
Бастапқы HTML ішінде бірегей метадерегі бар маршруттар 0 45
Алғашқы жүктеудегі ең үлкен JS 1,3 МБ 658 КБ
Ән бетінің mobile LCP көрсеткіші өлшенбеген 1,8 с
Нақты 404 қайтаратын белгісіз жолдар 0% 100%
Sitemap-тағы шын мәнінде бар URL-дер 15-тен 9 45-тен 45
Әр релизде орындалатын acceptance-тексерістер 0 1 505
Коммиттелген visual-diff суреттер 0 101
Продакшен ауысулары / қажет болған rollback 3 / 0
Мен жазған код жолдары 0 0

Git timestamps бойынша іске асыру блоктары 25–55 минут, staging acceptance шамамен 90 минут, ал әр продакшен ауысу терезесі 15 минуттай болды. Трафиктің нақты ауысуы 302 миллисекундқа созылды. Алғашқы аудиттен үшінші продакшен-релизге дейінгі бүкіл жол 36 сағатқа сыйды.

Мен шын мәнінде не істедім

Алты шешім: мақсатты архитектураны растадым, EP үшін URL-slug таңдадым, splash-беттің тағдырын шештім, аналитика бағытын анықтадым, контент қарызы бойынша компромиске келістім және үш продакшен ауысуына рұқсат бердім. Әрқайсысы бизнес шешімі немесе қайтарылмайтын әрекетке рұқсат еді. Ешқайсысы редактор ашуды талап етпеді.

Қазір мен мына еңбек бөлінісін қорғар едім: AI қайтарылатын жұмыс пен тексеруге, адамдар қайтарылмайтын шешімдер мен стандарттарға жауап береді. Стандарттар да нақты жұмыс, олар ревью қабатынан келді: дайындық өлшемі, жұмыс ауқымының шекаралары, артефактілерді git-те сақтау ережесі, алдын ала жаттықтырылған rollback. Бұл қабатсыз мен 1 505 дәлелі бар жылдам көшірудің орнына үнсіз олқылықтары бар жылдам көшіру алар едім.

Қайталап көргіңіз келсе: орындаушы агентке қолмен синхрондалатын файлдар емес, бәрі содан жасалатын реестр беріңіз; мәлімдемелерін коммиттелген артефактілермен дәлелдетіңіз; өз ауысуын өзі тоқтатуға мүмкіндік беріңіз; іштей немен келіспейтінін сұраңыз; және бір сөзді өзіңізге қалдырыңыз.

Менің сөзім «баста» болды.