Оцінка боргу у проектах з дизайном схем GraphQL за допомогою LLM та механізму CI Ratchet
Як використовувати перевірку за допомогою LLM та оцінку від 1 до 5, щоб виявити суб’єктивні проблеми з проектуванням GraphQL у нових запитах на інтеграцію та визначити наявний «борг» у вашій схемі.
Схема GraphQL, яку змінюють десятки чи сотні інженерів у різних продуктових доменах, поступово виходить з ладу, незалежно від того, наскільки хорошим є посібник зі стилю. Інструменти лінтингу виявляють механічні проблеми, але складніші вимагають суб’єктивних рішень: String, який мав би бути енумерацією, список, який продовжує зростати без меж, поле з можливістю значення null, яке насправді ніколи не повертає null. У цій статті описується двочастинна система для таких випадків: ревізор, який працює за допомогою LLM та запобігає новим проблемам ще на етапі подання змін, та система оцінювання існуючої схеми, яка перетворює старі проблеми на пріоритизований, відстежуваний список завдань під контролем інструментів CI.
Чому якість API стає проблемою системи
З невеликою кількістю інженерів послідовність API здебільшого залежить від спільних уподобань. Люди працюють поруч, переглядають зміни схем один одного та дійшують спільних рішень щодо структури. Коли організація росте, цей підхід перестає ефективним. Постійно додаються нові функції, старі конвенції існують поруч із новими, а рішення, які здавалися очевидними для початкової команди, інші команди, які їх ніколи не бачили, застосовують по-іншому.
У такій ситуації потрібні відповіді на три запитання, які не залежать від уваги окремого переглядача:
- Як зберігати послідовність проектування API, коли багато команд одночасно редагують схему?
- Як переконатися, що нові типи та поля відповідають сучасним найкращим практикам?
- Як знайти ті частини API, які були спроєктовані до появи цих практик?
Перші два стосуються профілактики. Третій — археології, і саме його найчастіше ігнорують під час зусиль з управління.
Де закінчуються правила лінтера та починається суб’єктивне судження
Велика частина стандартів API є механічними, і статичний аналіз ефективно їх обробляє. Конвенції найменування, використання застарілих полів, обов’язкові описи та послідовна форма помилок — усе це є характеристиками типу «так» чи «ні» для схеми: поле або дотримується правила, або ні, і лінтер може визначити це.
Інші стандарти не можна звести до простих правил. Типові приклади:
- Чи повинен цей
Stringбути енумерацією? - Чи потрібно сторінкувати цей список?
- Чи повинен цей
Intбути користувацьким скаляром? - Чи може це поле з можливістю значення null безпечно стати без неї?
- Чи відповідає ця форма тому, як подібні концепції моделюються в інших частинах API?
Жоден з цих випадків не має відповіді без урахування контексту. Іноді правильним є повернення String, або навіть не типового JSON-об’єкта. Щоб визначити, чи це правильно, потрібно разом розглянути три аспекти: оголошення схеми, реалізацію механізму обробки та мету надання цих даних клієнтам. Лише за наявності всіх трьох елементів можна визначити, яка форма найкраще підійде клієнтам.
Організації зазвичай вирішують цю проблему шляхом перегляду коду, зустрічей із командою платформи та письмових правил. Це ефективно, але погано масштабується. Тиск щодо швидкого виконання скорочує час перегляду, команда платформи не може аналізувати кожну зміну схеми в кожному репозиторії, а найкращі практики розвиваються швидше, ніж старі API отримують оновлення. У результаті виникають дві пов’язані проблеми: запобігання новим „боргам“ у дизайні та виявлення вже існуючих „боргів“.
Перенесення фокусу на початковий етап: рецензент на основі LLM для змін схем
Перша частина системи кодує керівні принципи проектування API у автоматизований агент для перевірки коду. Мета не в тому, щоб замінити людських рецензентів, а щоб надати їм додатковий погляд саме на ті проблеми, які залишаються поза увагою під час звичайної перевірки pull request. Особисте схвалення кожної зміни у форматі GraphQL командою платформи в усіх репозиторіях є неефективним з точки зору масштабування; проте впровадження стандартів у AI-рецензента, який працює всюди, є ефективним.
Оскільки агент бачить більше, ніж лише відмінності схеми, він може аналізувати контекст, а не лише синтаксис. Він читає оголошення, реалізацію, яка підтримує відповідне поле, та відповідний текст правил, а потім ставить конкретні запитання. Ось два приклади коментарів:
- Поле під назвою
updatedAtоголошується якString. Якщо резолвер повертає часовий маркер у форматі ISO 8601, йому слід використовувати спеціальний скалярISO8601DateTimeзамість цього. Company.employeesповертає звичайний список. Кількість працівників компанії не має природного верхнього обмеження, тому це поле має повертати структуру з пагінацією.
Жоден з цих випадків не може бути надійно виявлений інструментом лінтингу. Правило лінтингу, яке стверджує, що «поля, що закінчуються на At, мають бути датовими скалярами», створює хибні позитивні результати та ігнорує поле lastModified; правило, яке вимагає, щоб «усі списки були з пагінацією», є неправильним для поля, яке повертає три підтримувані валюти. Штучний інтелект може проаналізувати те, що насправді робить резолвер.
Ключовим фактором є час. Виявлення таких проблем під час розробки API коштує недорого. Але якщо їх знаходити після того, як клієнти вже прийняли певну структуру, це означає необхідність циклу виходу з ужитку та міграції.
Озирання назад: оцінка наявної схеми
Профілактика не допомагає з уже існуючою частиною схеми, а в зрілому API ця частина є досить великою. Частина з неї існує ще до сучасних стандартів. Інша частина містить компроміси, які здавалися доцільними на момент їх створення. А ще частина просто нерівномірна, оскільки різні команди моделювали однакові концепції по-своєму. Тож потрібен спосіб озирнутися назад.
Друга частина системи — це пакетний процес, який доповнює інструменти статичного аналізу, що вже обробляють схему. Його послідовність:
- Проаналізувати схему по окремих доменах та вибрати поля чи типи, щодо яких потрібні суб’єктивні оцінки з боку проектувальника.
Перший крок має значення для контролю витрат та зайвої інформації. Немає причин запитувати модель щодо полів, які вже були класифіковані за допомогою детерміністичної перевірки; LLM має розглядати лише ті варіанти, де справді потрібен осуд.
Чому оцінка від 1 до 5 краща за результат «пройшло/не пройшло»
Оскільки це суб’єктивні оцінки, примусове вміщення кожного результату у бінарний вердикт призводить до втрати інформації. Натомість кожне поле отримує оцінку за перевірку від 1 до 5:
- 1: поле виглядає належним згідно з дизайном.
- 2: сигнал слабкий, але, ймовірно, все гаразд.
- 3: необхідно, щоб це перевірив людина.
- 4: поле, ймовірно, порушує правила.
- 5: це класичний приклад ситуації, якої слід уникати.
Щоб зробити це конкретнішим: поле типу String, яке містить довільний текст, написаний користувачем, має знаходитися близько значення 1. Поле типу String під назвою errorCode, чий механізм обробки може повертати лише одне з трьох зафіксованих значень, має знаходитися близько значення 5, оскільки це фактично enum у прихованому вигляді.
Оцінка з рангуванням дає набагато корисніший сигнал, ніж простий список порушень. Команди можуть почати з результатів 4 та 5 високої достовірності, водночас бачачи області з нижчою достовірністю, які потребують уважнішого аналізу. Середня частина шкали має ще одну функцію: група значень 3 вказує команді платформи на те, де формулювання правил чи запит є неоднозначними, що є зворотним зв’язком для вдосконалення запитів до оцінки, щоб вони приносили більш достовірні результати.
Якщо ви створюєте щось подібне, попросіть модель надати структурований результат (оцінку та пояснення у окремих полях), щоб результати можна було зберігати та об’єднувати без необхідності аналізу прозового тексту, а також зберігайте версії тексту правил та критеріїв оцінювання разом із запитом, щоб зміни в оцінках можна було простежити до змін у правилах.
Перетворення результатів на дії
Оцінки в базі даних самі по собі нічого не змінюють. Їх об’єднання за доменами у панель керування дає кожній команді, яка їх володіє, чітке уявлення про проблеми в дизайні API у її сфері: не розрізнені факти чи одноразові коментарі до огляду, а пріоритизований список полів та типів, які можуть потребувати міграції.
Ці ж дані дозволяють впроваджувати поступові зміни в процесі неперервної інтеграції. Мета не в тому, щоб виправити все одразу, що є нереалістичним для великого API з багатьма клієнтами у продакшені. Мета — переконатися, що ситуація ніколи не погіршиться, водночас поступово покращуючи існуючий стан:
- Нові зміни схеми мають відповідати поточним стандартам.
- Існуючі проблеми фіксуються як відомі борги, а не ігноруються.
- Коли команди мігрують або відмовляються від старих підходів, допустимий поріг посилюється, щоб вже виправлені проблеми не знову не з’явилися.
Рачети є знайомим підходом під час міграцій: фіксується поточна кількість порушень у кожній області, будування зазнає невдачі, якщо зміни її збільшують, а зафіксований базовий рівень знижується, коли хтось виправляє конкретний випадок.
Цей підхід є найважливішим для публічних або широко використовуваних API, де очищення залежить від міграцій клієнтів. Результатом є не інструкція про видалення кожного проблематичного поля, а пріоритизована карта тих місць, де API більше не відповідає сучасним стандартам, що дозволяє командам планувати свої дії.
Чому LLM — це правильний інструмент для цієї задачі
LLM не є бездоганними оцінювачами дизайну API, і система не ставиться до них як до таких. Їхня сила тут є вузькою: вони можуть читати код та схему разом, порівнювати їх із правилами, написаними простою мовою, та створювати структуровану оцінку для випадків, які не можна описати за допомогою статичних правил.
Статичне правило може повідомити вам, що поле повертає список. Воно не може сказати, чи зростає цей список під впливом даних, введених користувачем, і чи потрібна тому пагінація. Модель може прочитати резолвер, порівняти його з прикладами у політиці та пояснити, чому поле відповідає або не відповідає цьому шаблону.
Це пояснення має більшу цінність, ніж число, яке до нього додано. Коли поле позначається як проблемне, команда, відповідальна за нього, повинна знати причину, щоб швидко вирішити, чи є це справжньою проблемою, і, якщо так, як спланувати міграцію. Оцінка без пояснення лише створює ще один черговий список для обробки.
Обмеження, які варто враховувати
Перевірка за допомогою LLM не замінює відповідальності за API чи судження людини-дизайнера, тому важливо чітко вказувати, що залишається:
- Хибні позитивні результати все ще трапляються.
Проте вона забезпечує масштабований спосіб виявлення закономірностей, які раніше були обмежені кількістю людського перегляду. Рекомендації кодуються один раз, послідовно застосовуються у кожному репозиторії, а результати дають командам об’єктивну відправну точку для дискусій щодо дизайну.
Підсумок
Система складається з двох частин, які спільно реалізують одну ідею. Під час подання запиту на зміни використовується LLM-рецензент, який застосовує керівні принципи дизайну до нових змін схеми ще до того, як клієнти почнуть від них залежати. У режимі пакетних обробок той самий алгоритм оцінює існуючу схему в межах від 1 до 5 балів; ці оцінки накопичуються у панелях керування для кожної команди, а механізм CI стримує зростання загальної кількості балів, поступово збільшуючи порогові значення. Це не повністю автоматизоване управління, і так і має бути. Воно робить якість API достатньо прозорою, щоб команди могли вживати заходів, а також забезпечує команді платформи механізм зворотного зв’язку для удосконалення власних правил у міру поширення цього підходу на все більшу частину схеми.