Як Google будує веб-фреймворки

Загальновідомо, що Google використовує єдине сховище для обміну кодом - усі його 2 мільярди рядків - і що використовує парадигму розробки на основі магістралі.

Для багатьох розробників поза компанією це дивно і протиінтуїтивно, але це працює дуже добре. (Стаття, на яку посилається вище, наводить хороші приклади, тому я не буду їх тут повторювати.)

Кодовою базою Google користуються понад 25 000 розробників програмного забезпечення Google з десятків офісів у різних країнах світу. У типовий робочий день вони вносять 16 000 змін до кодової бази. (джерело)

Ця стаття розповідає про особливості побудови веб-середовища з відкритим кодом (AngularDart) у цьому контексті.

Лише одна версія

Коли ви використовуєте розробку на основі магістралі в одному величезному репо, у вас є лише одна версія всього. Це очевидно. Хоча це все-таки добре вказати тут, оскільки це означає, що - в Google - ви не можете мати додаток FooBar, який використовує AngularDart 2.2.1, і інший додаток BarFoo, що працює на 2.3.0. Обидва додатки мають бути в одній версії - останньої.

Ось чому співробітники Google іноді кажуть, що все програмне забезпечення в Google живе на кривавій межі.

Якщо вся ваша душа кричить "небезпечно!" прямо зараз, це зрозуміло. Залежно від магістралі ("master" у термінології git) бібліотеки з вашим виробничим кодом, звичайно, звучить небезпечно. Але попереду сюжет.

74 тисячі тестів за коміт

AngularDart визначає 1601 тест (тут). Але коли ви робите зміну коду AngularDart у сховищі Google, він також запускає тести для всіх в Google, які залежать від фреймворку . На даний момент це близько 74 тисяч тестів (залежно від того, наскільки великими є ваші зміни - евристика пропускає тести, про які система знає, що ви на них не впливаєте).

Добре мати більше тестів.

Я щойно вніс зміни, які проявляються лише 5% часу, імітуючи щось на зразок стану перегонів в алгоритмі перевірки повторної вставки виявлення змін (я додав && random.nextDouble() >.05 до цього твердження if). Це не виявилося в жодному з тестів 1601 року, коли я їх провів (один раз). Але це розірвало купу клієнтських тестів.

Справжнє значення тут полягає в тому, що це тести реальних додатків . Вони не тільки численні, вони також відображають, як фреймворк використовується розробниками (а не лише авторами фреймворку). Це важливо: власники фреймворків не завжди правильно оцінюють, як використовується їх фреймворк.

Також допомагає те, що ці програми працюють, і щомісяця через них протікають мільярди доларів. Існує велика різниця між демонстраційними програмами, які автор фреймворку збирає у вільний час, і реальними виробничими додатками, в які вкладено десятки або сотні людських років. Якщо Інтернет має бути актуальним у майбутньому, нам слід краще підтримувати розвиток останнього.

То що трапиться, якщо фреймворк зламає деякі програми, які на ньому побудовані?

Ви зламаєте це, ви виправите

Коли автори AngularDart хочуть внести надзвичайну зміну, вони повинні піти та виправити це для своїх користувачів . Оскільки все в Google живе в одному репо, виявити, кого вони ламають, просто, і вони можуть негайно розпочати виправлення.

Будь-яка змінна зміна AngularDart також включає всі виправлення цієї зміни у всіх додатках Google, які залежать від цього. Тож поломка та виправлення переходять у репо одночасно і - звичайно - після належного перегляду коду всіма сторонами, що постраждали.

Наведемо конкретний приклад. Коли хтось із команди AngularDart вносить зміни, що стосуються коду в програмі AdWords, вони переходять до вихідного коду цієї програми та виправляють це. Вони можуть запускати існуючі тести AdWords у процесі, а також можуть додавати нові. Потім вони все це вносять до свого списку змін і просять переглянути. Оскільки їхній список змін стосується коду як у репозиторії AngularDart, так і в репо AdWords, система автоматично вимагає схвалення перегляду коду від обох цих команд. Тільки тоді зміну можна подати.

Це має очевидний ефект запобігання розробці структури у вакуумі. Розробники фреймворку AngularDart мають доступ до мільйонів рядків коду, побудованих на їх платформі, і вони регулярно самі торкаються цього коду. Їм не потрібно припускати, як використовується їх фреймворк. (Очевидним застереженням є те, що вони бачать лише код Google, а не код усіх Workivas, Wrikes та StableKernels світу, які також використовують AngularDart.)

Необхідність оновлення коду користувачів також уповільнює розробку. Не так сильно, як ви можете подумати (подивіться на прогрес AngularDart з жовтня), але це все одно сповільнює ситуацію. Це і добре, і погано, залежно від того, що ви хочете від фреймворку. Ми повернемось до цього.

У всякому разі. Наступного разу, коли хтось із Google скаже, що альфа-версія якоїсь бібліотеки стабільна і працює, тепер ви знаєте, чому.

Великі зміни

Що робити, якщо AngularDart потрібно внести суттєві зміни (скажімо, перейти від 2.x до 3.0), і ця зміна призведе до 74 тисяч тестів? Чи піде команда та виправить усіх? Чи внесуть вони зміни до тисяч вихідних файлів, більшість з яких вони не є авторами?

Так.

Однією з цікавих речей щодо створення звукової системи є те, що ваші інструменти можуть бути набагато кориснішими. Наприклад, у програмі Sound Dart інструменти можуть бути впевнені, що змінна має певний тип. Для рефакторингу це означає, що багато змін можуть бути повністю автоматичними, без необхідності підтвердження від розробника.

Коли метод у класі Foo змінюється з bar()на baz(), ви можете створити інструмент, який проходить через єдине сховище Google, знаходить усі екземпляри цього класу Foo та його підкласи та змінює всі згадки bar()на baz(). З системою звуку типу Дарт ви можете бути впевнені, що це нічого не зламає. Без типів звуку навіть така проста зміна може привести вас до неприємностей.

Інша річ, яка допомагає при масштабних змінах, - це dart_style, стандартний форматчик Dart. Весь код Dart у Google відформатований за допомогою цього інструменту. На той час, коли ваш код потрапляє до рецензентів, він був автоматично відформатований за допомогою dart_style, тому немає аргументів щодо того, чи слід розміщувати новий рядок тут чи там. І це стосується і масштабних рефакторів.

Показники ефективності

Як я вже говорив вище, AngularDart виграє від тестів своїх утриманців. Але це не просто тести. Google дуже ретельно оцінює продуктивність своїх додатків, тому більшість (усіх?) Виробничих додатків мають базові набори.

Отже, коли команда AngularDart вносить зміни, які заважають завантаженню AdWords на 1% повільніше, вони знають перед тим, як внести зміни. Коли в жовтні команда заявила, що з серпня додатків AngularDart стало на 40% менше і на 10% швидше, вони не говорили про деякі синтетичні крихітні програми TodoMVC. Вони говорили про реальні, критично важливі виробничі програми з мільйонами користувачів та мегабайтами коду ділової логіки.

Примітка: Герметичний інструмент нарощування

Можливо, вам цікаво: звідки цей хлопець знав, які тести у величезному внутрішньому сховищі запускати після введення нестабільної помилки в AngularDart? Звичайно, він не відбирав 74 тисячі тестів вручну, і настільки ж точно не проводив усіх тестів у Google. Відповідь криється в чомусь, що називається Базель.

У такому масштабі ви не можете мати серію сценаріїв оболонки для створення матеріалів. Все було б непомітно і непомірно повільно. Вам потрібен герметичний інструмент нарощування.

„Герметичний” у цьому контексті дуже схожий на „чистий” у контексті функцій. Ваші кроки збірки не можуть мати побічних ефектів (наприклад, тимчасові файли, зміни до PATH тощо), і вони повинні бути детермінованими (однакові дані завжди ведуть до однакових результатів). Коли це так, ви можете будь-коли запускати збірки та тести на будь-якій машині, і ви отримаєте послідовний результат. Не потрібно make clean. Тому ви можете надсилати свої збірки / тести для побудови серверів та їх паралелізму.

Google витратив роки на розробку такого інструменту побудови. Він був відкритий минулого року як Bazel.

І завдяки цій частині інфраструктури інструменти внутрішнього тестування можуть визначити, на які збірки / тести впливає кожна зміна, та запускати їх, коли це доречно.

Що це все означає?

Явна мета AngularDart - бути найкращим у своєму класі за продуктивністю, продуктивністю та надійністю для створення великих веб-додатків. Сподіваємось, ця публікація висвітлює останню частину - надійність - і чому важливо, щоб критично важливі програми Google, такі як AdWords та AdSense, використовували фреймворк. Справа не лише в команді, яка хвалиться своїми користувачами - як пояснювалося вище, наявність великих внутрішніх користувачів робить AngularDart менш імовірним внести поверхневі зміни. Це робить структуру більш надійною.

Якщо ви шукаєте фреймворк, який проводить капітальний ремонт та впроваджує основні функції кожні кілька місяців, AngularDart точно не для вас. Навіть якщо команда хотіла побудувати фреймворк таким чином, я думаю, з цієї статті ясно, що вони не змогли. Однак ми щиро віримо, що є простір для менш модної, але надійної системи.

На мій погляд, найкращим прогнозом довгострокової підтримки технологічного стеку з відкритим кодом є те, що це велика частина бізнесу основного супровідника. Візьмемо Android, кинджал, MySQL або git як приклади. Ось чому я радий, що Dart нарешті має один бажаний веб-фреймворк (AngularDart), одну улюблену бібліотеку компонентів (AngularDart Components) та один улюблений мобільний фреймворк (Flutter) - усі вони використовуються для створення критично важливих для бізнесу додатків Google.

[Матан Лурі та Кеті Уолрат взяли участь у цій статті.]

[Обговорити на Reddit, HN, Twitter.]