Анаіз большых переработак у AI: чаму пасаванне тэстаў не выявляе зместу, які быў выдалены
Дазвольце дазнаць, чаму ай-тэ-і-асыставаныя процесы переработкі тыхо адчыняюць код, який несе навантажэння, чаму тесты на адпрацоўкі гэтага не выявляюць, а таксама п’ять практык перагледу, якія робяць масовыя переработкі безпечнымі для выпуску.
Крыніцы: I Let Claude Refactor 4,000 Lines. Here’s the Part I Had to Undo.
Прамагчымі для напісання коду на асоцыяцыйных суперкомп’ютэрах зараз настолькі хорашы, што можу працаваць над занедбаным модулем за адно popołudnie, і рэзультат зазвычай выглядае краща, чым тое, што ён заменіў. Апасцярожнасць не становіць некачэны код; апасцярожнасць становіць код, які выглядае правільна, але таямніча пазбавляе працэздатнасці таго, што ніхто не запісаў. У гэтым кяле показаны рэалістычны прыклад такой нядачы, пояснены прычыны, чаму і тэставанне, і аднароднік яго працяглі не пазначыць, а таксама даўаны конкрэтны процес перагляду для большых перерабоць, адбываецца за дапамогою асоцыяцыйных суперкомп’ютэраў.
Перерабка, якая паспрабавала падбаць код і зламала систему
Разглянем модуль, у якім за два гады накупілася велика колькасць змян: апошней час прыбліжна 4 000 ліній коду, распадзеленых на чатыры файлы, — гэта тая частка кодбазы, якую інжынеры стараюцца не чапаць, калі толькі можна. У ям няэфектыва обработка памылак, тры розныя способы парсавання дат і калькі функцый, пра якія ніхто не можа з упэўненасцю сказаць, чы роўна ўсё яшчэ вызываюцца.
Разработчык перадае код AI-асистэнту пры двух сесіях і атрымле ў вынік чыста кращы варыянт: аднообразныя шаблоны, правільныя падсказкі пра типы, выдалены непатрэбныя частины коду, прыблізна на трохце менш ліній. Тэсты праходзяць. Разработчык пераглядае рэзультаты змены, або прынімае, што ўсё гаразд, і выкладзе код у продакцыю.
Чырвоў дзён пасля чаго-то пачынае згубляцца даннэ. Няма явных адхылень; проста бярэ апошнія чатыры відсоткі записаў. Прычына — рефакторынг, а точнейша — змена, якую разработчык прачытаў і санкціонаваў.
У чым асистэнт справдзіўся выключна
Варта быць точным ў тым, што пайшло добра, калі ўважаць, што вывад не ёсць у тым, што «AI паслабае пад час рефакторынгу». Асистэнт чыніў выключна добрае роботу падчас кожнай трансформацыі, якая залежала толькі ад коду, який быў у яго распарадзе:
- Аб’еднанне трох памочных функцый для парсінгу дат у адну функцыю
Для механічных, локальных змян, якія можна пераканацца праз ўжоўчытванне, гэты спосаб быў шырэй і надзейней за роботу чалавека, і ён не трапляўся на спуску пасля калькі сотняў ліній, дзе ручныя перерабкі часта паслабляюцься. Калі задача — «зробіць так, каб гэты код яшчэ чытра выражаў тое жа поведзенне», гэтыя інструменты працуюць вельмі добра. Проблемы пачынаюцца тады, калі стаўкаецца на тое, што саме включае пярэдбачанае «тое жа поведзенне».
Чаму код, які несе навантажэння, выкарыстоўваецца
Код, у якога няма відчутнай прычыны для існавання, выглядае абоўсумна непатрэбным. Ёсць затверджаная змена:
# before
def normalise_timestamp(raw):
ts = raw.strip()
if ts.endswith("Z") and "." in ts:
ts = ts.split(".")[0] + "Z" # partner sends 7-digit microseconds
return datetime.fromisoformat(ts.replace("Z", "+00:00"))
# after
def normalise_timestamp(raw):
return datetime.fromisoformat(raw.strip().replace("Z", "+00:00"))
Удаленыя галузь выглядае як класычны прыклад для рефакторавання: спецыяльны кейс, дзівна, частка-спецыфікатар, разлучэнне строкі. Бна здаецца зайвой, адколі fromisoformat вже парсуе ISO-часовы пазнакі.
Справа ў самым вхідным дадзенні. Адна з сторон прысылае часовыя меткі з семью дробнымі цифрамі. До Python 3.11 функцыя fromisoformat парсіла толькі тые форматы, якія выдавала сама функцыя isoformat(), тое значыць, не болейш чым шасць дробных цифраў, таму значэнне з семью цифрамі выклікала памылку ValueError. Python 3.11 і новейшыя версіі падтрымліваюць значна шырэйшы падсет стандарта ISO 8601 і ў такіх случаях є болей пацяплівымі, таму тое, чы рэшае гэта змена проблему, залежыць ад версіі Python, што ўжо адна з прычын пераканальвацца, а не прымыляцца. Гэтыя варіанты коду былі створеныя з-за запиту на падтрымку, надасленага за адзіннаццаць месцаў раней, і ўсё, што было напісана як короткі коментар, не дапамагало ні людям, ні моделям належна оцаніць ўсе плюсы і мінусы такога рашэння. Таксама варта зазначыць, што стары варіант коду робіў больш, чым здавалася: ён абсолютна выкасываў дробную частку, таму нават выход “нават новай” версіі разлічыўся для тых вхідных дадзенняя, якія ўсё-такі моглі быць запарсаваны.
Асалінскі проблема простая: модэль могла бачыць код, але не інцыдэнт, які яго стварыў. Заява пра проблему, аналіз пасля яе наступлення, лінія чату і гадзіны, пасвячаныя адлучэнню багоў, усе гэта было параду ў файле. Выкананне git blame паказала бы запіс пры внесенні змян у стыле "fix timestamp parsing for partner feed", які і быў весь практычны адказ, але ніхто не прасіў асистэнта паглядзець. Рэвізор затвердзіў выдаленне з таго жа падставы, якую наводзіў асистэнт: оба чыталі толькі разлік змян, а не історыю файлаў.
Чаму набір тэстоў заставаўся ў стане "зеленага"
Тэсты пераканаліся, калякі рэзультат вяртае функцыя для тых значэнняў вхідных дадзеных, пра якія хтось падумаў, а проблематычны вхідны данный, па самай апісанні, быў такім, пра які ніхто не падумаў.
У наладжэннях былі часовыя пазначкі з трыма дробнымі цифрамі, таму што самэй гэта люди пішучы теставаныя даныя вручную. Значэння з семі цифрамі з’являліся толькі ў працоўным строеўку аднаго з партнера.
Самэй гэта робіць большыя переработкі рызыковымі, незалежна ад таго, ўключана ЛІЧНАЯ АЎТАМАТЫЗАЦыя чы не: працоўны набор тэстаў паказвае толькі тое, што прызначэнне, якое описваюць тэсты, засталося незменным. Гэта нічога не сказвае пра прызначэння, якія ў іх не описваюцца. Калі вы перерабляеце трыдзць ліній вручную, вы думаеце пра кожную з іх. Калі зараз жа змінюецца чатырнацать тысяч ліній, працоўны набор тэстаў выконвае практычна всю пераканальную роботу, а ён ніколі не быў створаны для такага навантажэння.
Паказначыкі паверхності пакрыцча таксама не дапамаглі. Функцыя мела павнае пакрыцча ліній: кожная лінія выконвалася пад час тэстаў. Одна з гілакоў проста ніколи не выконвалася з тымі данымі, якія мелі значэнне. Пакрыцча ліній вимервае, які код выконваны, а не якія форматы даных былі выкарыстоўваны.
Сортаванне змян AI за рызыкамі
Практычны спосаб — перастаць рэгулярна адзінакова пераглядаць разлічыны, створаныя AI, і сортаваць іх па категоріях. Кожная з категорыяў мае абліковы рызыкі:
- Низький рызык: анотаціі типаў, перэйменаванні, форматаванне, раннія выходы та падобныя механічныя перапісвы. Яны локальныя і лёгкія да пераканання праз чытанне.
- Середні рызык: адзінаванне дуплікатных памагальнікаў. Зазвычай гэта нормальна, але трэба пераканацца, што з’едыненая версія адпрацоўвае всі вхідныя даны вызывача.
- Высокі рызык: выдаленне коду, які выглядае невыкарыстоўваным, адзінаванне спецыяльных случаў, якія выглядаюць зайвымі, і уніфікацыя поведзення, якое выглядае нэўсуперажнасным.
У групе з высокім рызыкам є адна спяльная рэч: у кожным случае модель правільна заўважвае, што ў чымсь ёсць проблема, і сама гэта проблема якраз і є прычынойяе існавання моделі.
Пасёлкі гэтаго праблемы таксама проявляюцца тыхае. Уявіце сабе модуль, які фарматавыць адказы пра аберанціях інакш, чым усе іншыя, і процэс переработкі, які змусіў бы яго падыходзіць адпаведна. Разлік быў намеровым, таму што наступныя элементы системы парсуюць самэ гэты конкретны формат адказаў. Старанне прабавацца досягнуць аднообразнасі было правым інстынктам; але застосаванне гэтага падходу тут было памылкай.
Пяць практык для наступной большай переработкі
Ооні распаўненыя прыблізна па тым, насколькі кожная з іх могла бы запобiec вышэўшай проблеме.
Робіце кожныя змены достатна малымі, каб іх можна было рэальна пераглядзець
Чатырнаццаць тысяч ліній — гэта не та кантэнцыя, якую можна пераглядзець. Пасля калькі сотняў ліній большасць людзей проста шукае явныя проблэмы, а не аналізуе кожную частку, незалежна ад таго, што яны сабе кажуць. Обмежыце кожную сесію адзінам модулем, а кожны запрошэнне да змін — толькі змянамі ў адзінам модуле. Вузькай вярою не ёсць праўедлівасць модэлю; вузькай вярою ёсць увага таго, хто яго пераглядае.
Патрабуеце падтверджэнне для кожнага выдалення
Перш чым прыймаць рэзультат, запытайце асистента пра спіс усьго, што ён выдаліў, і для кожнага пункта — чаму ён вырашыў, што выдаленне было безпечным. Запісаўшы логіку ў текст, вы адзначыце адны з найважнейшых крокаў, якія можаце здзейсніць. Кожна падтверджэння на кшталт "гэта здаецца зайвым" павінна спрацаваць як сігнал для ручнага адзінаковага перагляду.
Дайце модэлю історыю
Прабачце асистенту вышукваць git blame для кожнай лініі, яю ён прыметыць да выдалення, і адмаўляць пра змест месажа про коміт. Прычына стварэння незвычайной галузі часта фіксуецца там, і модэль добра выкарыстоўвае гэты контекст, калі ён яго мае; але ён не будзе шукаць сама без падказкі.
Запішыце коментары "чаму" пры рэфактораванні
Спачатку трэба дадзіць адказвальныя каментары, а не пасля. Точны каментар, у яком бы был названы партнер, семзначныя дробі і межа шасцячнаго парсера, верагатэльна запобег быў выдаленню. Неясныя прыметкі пра часавыя меткі партнера гэтаго не зрабілі. Якщо той, хто чытае код далей, чалавек або інструмент, не можа з’ясавіць, чаму існуе яка-небудзь лінія, то гэтая лінія за адны крок да зникнення.
Пораўняйце старую і новую версіі з рэальным трафікам
Прайсніце за дзень рэальных даных з працы через як старую, так і новую реалізацыю і пораўняйце, што кожна з яных вяртае. Для чыстых трансфармацый, такіх як парсінг і нормалізацыя, гэта даскладнае заведамства, і гэта точна выявляе тыя проблемы, якія не ўловіваюць фіксаты, таму што рэальны трафік містіць даны, пра якія ніхто не падумаў напісаць. Якщо немагчыма перадаць трафік занова, то наступныя найлепшыя варыянты — це выбіранне рэальных пэйлоадаў для фіксатаў регрэсіі.
Ключовыя выводы
Рефакторынг все ж такі быў карэтны: модуль стаў лепшы, робота была завершана за аднаго дня, а не за тыдзень, і адна регрэсія среди чатырох тысяч змененых лінакоў — гэта няшто інша, чым многі большыя ручныя рефакторынгі. Называць гэты результат „памылкай ШЫ“ — значы не разумець сутнасці. Система вывела разумны вынік на адной падставе таго, што могла бачыць, у спецыяльным случаю без зазначэнага разлуку, а людзкі рэвізор вывёў той жа вынік на адной падставе таго ж непূরнага рэзультата.
- Репазітарый — толькі частка системы; прычыны незвычайнага коду знаходзяцца ў тікетах, прыемных паведамленнях, аналізах пасля выканання і спамяці людзей.
- Інструмент, які бачыць толькі код, будзе адмаўляць любы элемент, для якого ніколи не была запісана падстава.
- Пазытывныя тэсты і павна адкрыць лінакоў не дазволяюць стверджваць, што поведзенне захавана для вхідных дадзенаў, якія ніхто не моделюваў.