Панель інструментів Unity - отримані уроки, масштабування наших інтерфейсів, культури та процесів розвитку

У Unity нещодавно ми взялися за вдосконалення наших інформаційних панелей - це завдання, яке кардинально змінило не лише наш стек технологій, але і способи роботи та співпраці.

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

Ця стаття збирає ці практики і має на меті надати якомога більше міркувань за кожним рішенням. Але спочатку якийсь контекст.

Спадщина

Дивлячись на кількість інженерів, за останні 4 роки компанія Unity зросла більш ніж у чотири рази. Оскільки компанія зростала як органічно, так і шляхом придбань, зростала і її пропозиція продуктів. Хоча продукти, розроблені спочатку в Unity, були здебільшого однакові з точки зору технологій та мови дизайну, нещодавно придбані, природно, не були.

В результаті ми отримали декілька візуально різних панелей інструментів, які працювали і поводилися по-різному і які не мали спільних елементів навігації. Це призвело до поганого користувацького досвіду та розчарування користувачів. У прямому сенсі слова стан наших продуктів коштував нам доходу.

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

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

Міркування

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

Тим не менше, те, що наші розробники знали найкраще і про що писали більшість наших активно розроблюваних додатків, - це AngularJS. Рішення почати міграцію всього за один раз було б катастрофою, яка очікувала б відбутися. Натомість ми спробували спочатку перевірити наші припущення у значно меншому масштабі.

Мабуть, найбільш роз'єднаною групою продуктів, яку ми мали, були інформаційні панелі монетизації . Ці проекти, які врешті-решт опинилися б під контролем приладової панелі Operate, були майже різними практично будь-якими можливими способами: використовуваними технологіями, підходом до UI / UX, практикою розробки, умовами кодування - ви самі це називаєте.

Ось як приблизно виглядала ситуація:

Після деякого мозкового штурму ми визначили основні напрямки, над якими нам потрібно було б працювати, щоб об’єднати всі продукти:

1. Окремий товар

Нам потрібні були ці інформаційні панелі (розділені між кількома програмами, доменами та стеками технологій), щоб:

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

2. Підтримка спадщини

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

3. Практики та інструментарій

Хоча майже всі команди використовували AngularJS, для вирішення одного і того ж набору проблем використовувались різні інструменти. Різні бібліотеки для запуску тестів і тверджень, рішення для управління станом або їх відсутність, jQuery проти власних селекторів браузера, SASS проти LESS, бібліотеки діаграм тощо.

4. Продуктивність розробника

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

Крім того, багато наших команд працюють у місцях, розділених 10-годинною різницею (Гельсінкі, Фінляндія та Сан-Франциско), що робить ефективне прийняття рішень щодо будь-яких спільних частин справжнім викликом.

Новий

Основними напрямками нашої діяльності були:

  1. Заохочуйте та зберігайте спритні способи роботи в наших командах та дозволяйте командам бути в основному незалежними одна від одної
  2. Використовуйте та розробляйте спільні інструменти та конвенції, наскільки це можливо, щоб документувати їх та робити легкодоступними та корисними

Ми вірили, що досягнення цих цілей значно покращить наш час виходу на ринок та продуктивності розробників. Щоб це сталося, нам знадобилося рішення, яке:

  • Побудуйте функції продукту з кращим користувацьким досвідом
  • Поліпшити якість коду
  • Забезпечте кращу співпрацю, не заважаючи нікому прогресувати роботу в процесі.

Ми також хотіли заохотити та полегшити перехід на сучасний стек технологій, щоб зробити наших розробників більш задоволеними своєю роботою, і з часом відійти від наших застарілих рамок та інструментів.

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

Як засіб пісочниці всіх застарілих програм, але все одно відображаючи їх у контексті тієї самої нової програми, ми завантажуємо їх всередину внутрішнього фрейму, з якого вони можуть спілкуватися з основним SPA за допомогою шини повідомлень, реалізованої за допомогою postMessage()API.

Монохранилище

Ось структура каталогів, з якої ми почали:

/src /components /scenes /foo /components package.json foo.js /bar /components package.json bar.js package.json index.js

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

Усі великі фрагменти інтерфейсу називаються сценами . Кожна сцена містить package.jsonде, де dependenciesвикористовуються компоненти цієї сцени, визначені. Це робить можливим дві речі:

  1. Розгортання оновлює лише ті файли, які змінилися

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

  2. Сцени завантажуються лише за потреби

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

Ось як таке налаштування виглядає на практиці (спрощене для читабельності):

// In src/routes.jsconst FooLoader = AsyncLoadComponent( () => import(‘src/scenes/foo/foo’), GenericPagePreloader,);
// In src/scenes/foo/foo.js 

Це AsyncLoadComponentтонка обгортка навколо React.lazy(), додатково приймає компонент попереднього завантажувача, той самий, який пройшов через відновлення React.Suspense(), і затримку, після якої попередній завантажувач повинен бути відтворений, якщо сцена не закінчила завантаження.

Це корисно, коли наші користувачі бачать один і той самий попередній завантажувач без будь-яких перерв або спалаху вмісту з моменту запиту сцени до моменту, коли всі її файли були завантажені, всі критичні запити API завершені, а компонент закінчив візуалізацію.

Рівні компонентів

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

Нам потрібна була наша структура каталогів, щоб повідомити нас про:

  • Чи компоненти розроблені для загального використання, чи вони призначені лише для конкретного випадку використання?
  • Чи вони достатньо загальні, щоб їх можна було використовувати в усіх додатках, чи вони повинні використовуватися лише у певному контексті?
  • Хто відповідає за код і має найвідоміші відомості про нього?

Виходячи з цього, ми визначили наступні рівні компонентів :

1. Спеціальна програма (src / app)

Компоненти одноразового використання, які обслуговують конкретні випадки використання в цій програмі, і які не призначені для повторного використання або вилучення до бібліотеки компонентів (маршрути, нижній колонтитул, заголовок сторінки тощо).

2. Загальний (src / компоненти)

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

3. Компоненти однієї сцени (src / сцени / моя сцена / компоненти)

Компоненти, розроблені з урахуванням конкретного випадку використання; не призначений для використання в будь-яких інших сценах. У випадках, коли компонент з однієї сцени потрібно використовувати в іншій, ми використовуємо:

4. Багатосценові компоненти (src / scene / components / my-feature)

Компоненти, що використовуються в декількох сценах, але не мають на меті бути загальнодоступними для використання де-небудь ще. Щоб проілюструвати, чому просто перенести їх src/componentsнедостатньо добре:

Уявіть, що дотепер у вас була одна сцена, яка містила компоненти, використовувані для побудови досить певних діаграм даних. Зараз ваша команда створює другу сцену, яка використовуватиме різні дані для діаграм, але візуально ці два виглядатимуть приблизно однаково.

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

З цією метою будь-який компонент або група компонентів, що приблизно називається об’єктом, буде розміщено src/scenes/componentsзвідки, де його можна імпортувати та використовувати будь-якою іншою командою, однак:

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

5. Спільна бібліотека

Компоненти, які ми пройшли бойові випробування у виробництві та хочемо витягти до нашої спільної бібліотеки компонентів, що використовуються іншими командами інформаційних панелей Unity.

Ода спільним залежностям

Хоча було б дуже зручно мати можливість будувати та розгортати кожну частину нашого додатка в повністю ізольованому середовищі, певні залежності - як зовнішні бібліотеки, так і внутрішній код програми - просто будуть використовуватися у всій кодовій базі. Такі речі, як React, Redux та вся логіка, пов’язана з redux, загальні навігаційні компоненти тощо.

Розгортання змін

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

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

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

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

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

Як уникнути поломки речей

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

  1. Тестування

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

Деякі уроки, які ми отримали з різних стратегій тестування:

  • Спробуйте модульно протестувати якомога більшу частину коду, особливо: умовну логіку, перетворення даних та виклики функцій
  • Вкладайте кошти та використовуйте інтеграційні тести в повній мірі, перш ніж приймати рішення писати будь-які тести e2e. Початкова вартість інтеграційних тестів набагато вища, але вона блідне порівняно з ціною на утримання пакета e2e
  • Постарайтеся не надто реагувати, починаючи писати тести e2e на речі, які не були виявлені модульними або інтеграційними тестами. Іноді процеси або інструменти винні
  • Нехай тестові приклади пояснюють поведінку інтерфейсу користувача, а не деталі реалізації
  • Автоматизовані тести не можуть повністю замінити ручне тестування

2. Мінімізуйте поверхню спільного коду

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

3. Підзвітність

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

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

Щоб заохотити старанність, ми наголошуємо, що автор PR та рецензент несуть однакову відповідальність за те, щоб все працювало .

Бібліотека компонентів

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

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

  • API є досить гнучким, щоб підтримувати передбачувані випадки використання
  • Компонент був протестований у різних контекстах
  • Враховуються продуктивність, чуйність та UX

Цей процес відповідає Правилу трьох і має на меті допомогти нам випустити лише ті компоненти, які справді багаторазово використовуються і використовувались у різних контекстах перед тим, як перенести їх у нашу загальну бібліотеку.

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

У перші дні бібліотека компонентів раніше розташовувалася в тій самій кодовій базі, що і сама програма. З тих пір ми виділили його в окреме сховище, щоб зробити процес розробки більш демократизованим для інших команд в Unity - що важливо для просування його прийняття.

Модульна конструкція компонентів

Найдовше побудова багаторазових компонентів означала вирішення багатьох завдань, багато з яких часто не мали хороших рішень:

  • Як легко імпортувати компонент разом із його стилями, і лише це
  • Як замінити стилі за замовчуванням без війн конкретності селектора
  • У більших компонентах, що складаються з декількох менших, як замінити стиль меншого компонента

Наша інформаційна панель, а також наша бібліотека компонентів сильно залежать від інтерфейсу матеріалу та використовують його. Що унікально переконливим у стилістичному рішенні Material UI є потенціал JSS та їх уніфікована мова стилів (що варто прочитати), які дозволяють розробляти інтерфейси, інкапсульовані дизайном, як у випадку з модулями CSS, та вирішувати вищезазначені питання поступово.

Це суттєво відрізняється від підходів, таких як BEM, які забезпечують інкапсуляцію за домовленістю, які, як правило, менш розширювані та менш інкапсульовані.

Посібник стилю життя

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

У нас був досить хороший досвід роботи з Storybook, який було смішно просто налаштувати та розпочати, але через деякий час ми зрозуміли, що потрібне більш надійне та наскрізне рішення. Досить близький до того, що пропонує Styleguidist, але більш пристосований до наших потреб.

Існуючі дизайнерські документи

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

  • Специфікація дизайну матеріалів продовжує змінюватися, і тому ми часто виявляли, що витрачаємо час на оновлення всіх знімків екрана та інструкцій, або дозволяємо нашим інструкціям щодо дизайну застарівати
  • З’ясувати, що правильніше: реалізація чи специфікація не завжди були простими завданнями. Оскільки ми публікували демонстрації Storybook для кожного компонента та для кожної версії бібліотеки, ми могли бачити, що і як змінилося. Ми не могли зробити те саме для специфікацій дизайну.
  • Знімки екрана та відео можуть спілкуватися лише стільки . Щоб забезпечити високоякісні компоненти, які можуть використовуватись кількома командами, необхідно перевірити, чи працює кожен компонент у всіх роздільних здатностях, чи не містить помилок та має хороший інтерфейс - це було важко, якщо дизайнер не сидів буквально поруч із вами, щоб побачити демонстрація реалізації, що відображається на екрані

Додаток для документації на компоненти

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

  • Є одна точка відліку, яка демонструє компоненти , якякщо вони повинні виглядати, поводитись і використовуватись - передбачено для кожного випуску - замінюючи докладні описи на реальні демонстраційні програми
  • Зробіть так, щоб дизайнери та розробники могли настільки легко співпрацювати над компонентами та їх документами, і робіть це до випуску компонентів - без необхідності обмінюватися відео, знімками екрану або перебувати фізично в одному місці
  • Розділіть конструкції на те, що ми плануємо зробити, на те, що було зроблено

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

  1. Дизайнери вносять вклад в документацію компонентів безпосередньо шляхом редагування файлів документації через інтерфейс Github, вносячи зміни до останнього випуску.
  2. Демонстраційні компоненти, як WYSIWYG - той самий код, який ви бачите як приклад того, як реалізувати компонент, використовується для рендерингу демонстрації, включаючи будь-який імпорт проміжного файлу, декларації змінних тощо. Як додатковий бонус, загорнуті компоненти withStyles()відображаються правильно (проблема присутня на даний момент в Storybook).
  3. Зміни в документації та коді майже миттєво видно без локальної перевірки гілки та запуску програми документації - програма переробляється та публікується під час кожного коміту.

Досвід розробки

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

Щоб зробити це завдання максимально безперешкодним, ми розробили Preview Server, здатний створювати нову збірку нашого додатку щоразу, коли PR створюється або оновлюється.

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

Заключні слова

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

Ми масштабували фронтенд-проект не лише з точки зору рядків коду, але й з точки зору кількості інженерів, які працюють у його базі коду - число, яке з початку зросло в чотири рази.

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

Поки нас чекає ще довгий шлях до того, щоб ми могли розширити свій підхід до більшої кількості команд та до більших викликів, ми вже помітили низку вдосконалень:

  • Дорожня карта та видимість роботи

    Завдяки тому, що в одному місці відбувається вся робота, прогрес відстежується, і всі питання збираються

  • Швидкість розробки та час виходу на ринок

    Нові функції можна створити значною мірою з уже існуючих та добре перевірених компонентів - легко знайти за допомогою нашого додатка для документації

  • Якість коду та охоплення тесту

    При створенні нових речей, рішення подібної проблеми, як правило, вже існує і є в межах досяжності, а також приклади, як це перевірити

  • Загальна якість та UX

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

Звичайно, на цьому шляху ми зіткнулися з низкою викликів, які нам потрібно вирішити, або які потребуватимуть вирішення в майбутньому:

  • Побудова & CI продуктивність

    Зі зростанням кількості залежностей, пакетів побудови та тестів зростає час, необхідний для розгортання. Надалі нам потрібно буде розробити інструментарій, який допоможе нам лише створювати, тестувати та розгортати ті частини, які змінилися.

  • Культура розвитку

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

  • Ізоляція поломок та оновлення

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

Держава тоді, зараз і в майбутньому

Міграція передбачала перехід від AngularJS до React. Ось як ситуація змінилася за минулий рік:

Це обгортання! Дякую за читання! Ви можете знайти мене на LinkedIn тут.

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