Перейти до вмісту

Розмістіть дані там, де їм місце

Також доступно 28 мовами

English · Español · 日本語 · Português (Brasil) · Français · Deutsch · Italiano · 한국어 · 简体中文 · Português (Portugal) · 繁體中文 · Bahasa Indonesia · Türkçe · Polski · Tiếng Việt · ไทย · Nederlands · العربية · עברית · हिन्दी · Русский · Svenska · Dansk · Norsk bokmål · Suomi · Español (México) · Français (Canada) · Deutsch (Schweiz)

Two identical drawers on a workbench, seen from above: one jammed with everything mixed together, one divided so each kind of thing has its own compartment.
Зображення згенеровано ШІ

Модуль 4 з 6 розділу Моделювання даних заради змісту, розділ 2 курсу Data Architect. Кожен модуль самостійний. Попередній: Побудуйте логічну модель · Наступний: Проєктуйте до третьої нормальної форми, а потім відступіть

Питання, на яке відповідає цей модуль: Як нормалізація допомагає зрозуміти, де насправді місце даних?

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

Звучить просто. Але це не так. Я бачив чимало систем, де кожне поле мало сенс, коли його хтось створював. Проблема була не в тому, що поля безглузді, а в тому, що факти зберігалися не в тому місці. Доставка містить інформацію, яка насправді описує розподільчий центр. Та сама адреса з’являється на сотнях записів. Одне поле містить три значення через кому. Зміна одного бізнес-факту означає оновлення 10 000 записів. Саме цю проблему допомагає розв’язати нормалізація, і перш ніж ми потонемо в термінології баз даних, ось уся ідея: розмістіть факт там, де йому місце. Усе інше в цьому модулі допомагає робити це системніше.

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

Навіщо це? Бо факт, збережений один раз, — це факт, який ми змінюємо один раз. Нормалізація запобігає надлишковості (redundancy), тобто копіюванню того самого факту в багато записів, а разом із нею й невідповідностям, що закрадаються, коли одну копію оновили, а інші — ні. Ось чому вона пасує транзакційним системам на кшталт Salesforce, де записи створюються й оновлюються цілий день.

Ми нормалізуємо кроками, які називаються нормальними формами, і кожна ставить одне запитання:

  • Перша нормальна форма (1НФ): одне значення в полі і щось, що унікально ідентифікує кожен рядок.
  • Друга нормальна форма (2НФ): кожен факт описує все, що ідентифікує рядок, а не лише його частину.
  • Третя нормальна форма (3НФ): кожен факт описує саме цю річ, а не щось інше, з чим вона пов’язана.

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

Навіщо ми нормалізуємо?

Section titled “Навіщо ми нормалізуємо? ”

Коли я викладаю це наживо, я не починаю з доставок. Я починаю із собак. Собак розуміють усі, тож ніхто не відволікається на бізнес, і ми можемо спостерігати, як дані шукають свій дім. Потім ми переносимо ті самі кроки на Steve Trucking.

Сидіти, чекати, моделювати

Section titled “Сидіти, чекати, моделювати ”

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

СобакаНародженняВласникІндексМістоПородаЗаводчикТрюки
Bella2017Marcus80202ДенверлабрадорMaple RidgeСидіти, Чекати, Дай лапу
Milo2019Marcus80202ДенверпудельGolden MeadowСидіти, Перекинутися
Max2022Ana64105Канзас-СітіметисСидіти, Завмерти
Luna2020Kenji98101СіетлхаскіCascadeСидіти, Чекати, Крутитися
Rocky2024Priya30301АтлантабігльMaple RidgeСидіти
Coco2014Ana64105Канзас-СітічихуахуаLittle PawsДай лапу, Крутитися

Це працює. Кожна таблиця працює першого дня. Але подивіться, що вже не так. Як знайти всіх собак, які вміють «Дай лапу»? Поштовий індекс Ana стоїть у двох рядках, як і індекс Marcus: що станеться, якщо хтось оновить один і не оновить інший? Maple Ridge з’являється двічі, як і Канзас-Сіті. А Max узяли з притулку, тож заводчика немає взагалі.

Крок 1: перша нормальна форма. Два правила. Кожне поле містить одне значення, тож список трюків через кому має зникнути. І кожному рядку потрібне щось, що його унікально ідентифікує. Не кличка собаки: собак на ім’я Max дуже багато. ID: автоматично згенерований ID або GUID. Трюки виносимо в окремі рядки, по одному рядку на кожну собаку і кожен трюк. А щойно трюк має власний рядок, з’являється місце для того, чого ніколи не скажеш у списку через кому: наскільки трюк складний, наскільки добре ця собака його виконує і коли вона його вивчила.

Собака (ідентифікується за ID собаки)

ID собакиКличкаНародженняВласникІндексМістоПородаЗаводчик
D-001Bella2017Marcus80202ДенверлабрадорMaple Ridge
D-002Milo2019Marcus80202ДенверпудельGolden Meadow
D-003Max2022Ana64105Канзас-Сітіметис
D-004Luna2020Kenji98101СіетлхаскіCascade
D-005Rocky2024Priya30301АтлантабігльMaple Ridge
D-006Coco2014Ana64105Канзас-СітічихуахуаLittle Paws

Трюк собаки (ідентифікується за ID собаки + трюком)

ID собакиТрюкСкладністьРівень навичкиВивчено
D-001СидітиЛегкийЕксперт2017-11
D-001ЧекатиЛегкийЕксперт2018-01
D-001Дай лапуСереднійДобре2018-06
D-002СидітиЛегкийЕксперт2020-02
D-002ПерекинутисяСкладнийВчиться2023-05
D-003СидітиЛегкийДобре2022-06
D-003ЗавмертиСкладнийВчиться2024-03
D-004СидітиЛегкийЕксперт2020-11
D-004ЧекатиЛегкийДобре2021-01
D-004КрутитисяСереднійЕксперт2021-06
D-005СидітиЛегкийВчиться2025-02
D-006Дай лапуСереднійЕксперт2015-06
D-006КрутитисяСереднійВчиться2025-04

Тепер «усі собаки, які вміють „Дай лапу“» — просте запитання. Але зверніть увагу, за чим ідентифікується Трюк собаки: не за однією річчю, а за двома разом — собакою і трюком. Запам’ятайте це.

Крок 2: друга нормальна форма. Друга нормальна форма важлива лише тоді, коли рядок ідентифікується комбінацією, а Трюк собаки саме такий. Тож я питаю про кожен стовпець: чи описує він усю комбінацію, чи лише її частину? Рівень навички описує цю собаку, що виконує цей трюк: Bella — експерт у «Сидіти», а Rocky ще вчиться. «Вивчено» — так само. А складність? «Сидіти» легкий, хоч би яка собака його виконувала. Це описує трюк, лише частину ключа, і саме тому «Легкий» повторюється в кожному рядку із «Сидіти». Тож трюк отримує власну таблицю, а залишається пара.

Трюк

ID трюкуТрюкСкладність
T-01СидітиЛегкий
T-02ЧекатиЛегкий
T-03Дай лапуСередній
T-04ПерекинутисяСкладний
T-05ЗавмертиСкладний
T-06КрутитисяСередній

Трюк собаки (ID собаки + ID трюку)

ID собакиID трюкуРівень навичкиВивчено
D-001T-01Експерт2017-11
D-001T-02Експерт2018-01
D-001T-03Добре2018-06
D-002T-01Експерт2020-02
D-002T-04Вчиться2023-05
D-003T-01Добре2022-06
D-003T-05Вчиться2024-03
D-004T-01Експерт2020-11
D-004T-02Добре2021-01
D-004T-06Експерт2021-06
D-005T-01Вчиться2025-02
D-006T-03Експерт2015-06
D-006T-06Вчиться2025-04

Ця таблиця Трюк собаки — та сама, яку ми знайшли в модулі 2, і в моделюванні даних вона має назву: з’єднувач (junction), або, як я його називаю, логічний об’єкт-з’єднувач. Собака знає багато трюків; трюк знає багато собак. З’єднувач стоїть між ними, і це не порожня сантехніка: він містить факти, що належать самому зв’язку, — рівень навички і дату. Ми зустрінемо його знову в модулі 6, а в розділі 3 побачимо, що Salesforce вживає «junction object» вужче: об’єкт-з’єднувач Salesforce (Salesforce Junction Object) — це користувацький об’єкт із двома зв’язками master-detail (розділ 3, модуль 7).

Крок 3: третя нормальна форма. Тепер повернімося до таблиці Собака. Усе в ній ідентифікується за ID собаки, тож другій нормальній формі нічого сказати. Третя нормальна форма ставить інше запитання: чи описує цей факт собаку, чи щось інше, з чим собака пов’язана? Порода — окрема річ: лабрадор є лабрадором, хоч би яка це була собака, і факти про нього, як-от розмір, належать породі, а не Bella. Індекс і місто описують власника, а не собаку: там живе Ana, хоч на яку з її собак ми дивимося. Дані заводчика описують заводчика. Кожен із цих фактів — факт про інший факт, тож кожен переходить у власну таблицю, а собака посилається на нього зовнішнім ключем:

  • Собака: ID собаки, Кличка, Народження, ID власника, ID породи, ID заводчика
  • Власник: ID власника, Ім’я, Індекс, Місто
  • Порода: ID породи, Порода, Розмір
  • Заводчик: ID заводчика, Назва, Місто
  • Трюк і Трюк собаки — з кроку 2

Тепер Ana зберігається один раз, і обидві її собаки посилаються на неї. Maple Ridge зберігається один раз, а таблиця Порода дає дім фактам про кожну породу, як-от розмір, яким у таблиці не було де жити. Зовнішній ключ може бути порожнім: у Max немає ID заводчика, і це чесна відповідь, а не прогалина. Потім я ставлю аудиторії каверзне запитання: коли власник отримав собаку — куди йде ця дата? На собаку. У цій моделі в собаки один власник водночас, тож ця дата описує зв’язок собаки з її власником.

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

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

Модель собак після третьої нормальної форми Шість прямокутників, з’єднаних лініями. Власник володіє однією або багатьма Собаками. Собака має одного Власника, належить до однієї Породи, і її вивів нуль або один Заводчик (Заводчик розводить одну або багато Собак). Між Собакою і Трюком стоїть Трюк собаки: Собака знає нуль або багато Трюків собаки, Трюк відомий у нуль або багатьох Трюках собаки, і кожен Трюк собаки належить рівно одній Собаці й одному Трюку. володіє розводить належить до знає відомий у Власник Заводчик Собака Порода Трюк собаки Трюк рівно один нуль або один один або багато нуль або багато © 2026 Twinshark LLC · stevetecharc.com
Click to enlarge

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

Тепер застосуймо це до Steve Trucking

Section titled “Тепер застосуймо це до Steve Trucking ”

Собаки роблять ці кроки легкими для сприйняття. Реальний бізнес робить їх важливими, бо ті самі помилки в масштабі коштують реальних грошей. Тож перенесімо ті самі три запитання на Steve Trucking. Уявіть, що компанія веде свої доставки так:

ДоставкаРоздрібний партнерРозподільчий центрМісто DCБригадаВантажівка
1001KeystoneKeystone Phoenix DCФініксБригада 12117
1002KeystoneKeystone Phoenix DCФініксБригада 8122
1003KeystoneKeystone Phoenix DCФініксБригада 12117

На перший погляд тут нічого складного. Але подивіться на розподільчий центр. Ми повторюємо «Keystone Phoenix DC, Фінікс» на кожній доставці, що з нього виходить: Steve Trucking робить близько 3 300 доставок на день із дев’яти DC, тож по кількасот на день із кожного. Що станеться, якщо цей DC переїде? Скільки записів ми змінимо і що буде, якщо один пропустимо? Тепер дані кажуть, що DC перебуває у двох містах одночасно. Це аномалія оновлення. А якщо ми хочемо завести новий DC до його першої доставки? А якщо видалимо останню доставку з DC — чи не видалили ми щойно єдине місце, де зберігали про нього хоч щось?

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

Перша нормальна форма: не ховайте повторювану структуру

Section titled “Перша нормальна форма: не ховайте повторювану структуру ”

Почнімо з першої нормальної форми, або 1НФ, де люди іноді роблять нормалізацію складнішою, ніж треба.

Припустімо, у доставки є поле «Позиції»: «Холодильник, комплект льодогенератора, вивезення старого». Ми поклали три шматки інформації в одне поле. Здається зручним, доки хтось не спитає: «Скільки вивезень ми зробили минулого місяця?» Або уявіть поля «Позиція 1», «Позиція 2» і «Позиція 3». Це працює — до доставки з чотирма позиціями. Проблема не в тому, що ми обрали неправильну кількість полів «Позиція». Проблема в тому, що ми не розпізнали повторювану структуру. Доставка може мати кілька позицій. Це дані, які варто змоделювати: Позиція доставки, яку ми знайшли в модулі 2.

Позиції доставки трьома способами Три картки показують позиції однієї доставки, збережені трьома способами. Одне поле: у доставки 1001 є поле «Позиції» зі значенням «Холодильник, комплект льодогенератора, вивезення старого», яке не відповість, скільки вивезень було минулого місяця. Повторювані поля: Позиція 1 холодильник, Позиція 2 комплект льодогенератора, Позиція 3 вивезення старого, які не вмістять доставку з чотирма позиціями. Дочірній запис, виділений: у доставки 1001 три Позиції доставки, холодильник, комплект льодогенератора і вивезення старого, а наступна позиція — новий запис, а не нове поле. Примітка внизу: якщо щось може траплятися багато разів, можливо, там ховається ще одна сутність. Одне поле три факти в одному значенні ДОСТАВКА 1001 Позиції: холодильник, комплект льодогенератора, вивезення старого ЗАПИТАННЯ, НА ЯКЕ ВОНО НЕ ВІДПОВІСТЬ Скільки вивезень було минулого місяця? Повторювані поля Позиція 1, Позиція 2, Позиція 3 ДОСТАВКА 1001 Позиція 1: холодильник Позиція 2: комплект льодогенератора Позиція 3: вивезення старого ДОСТАВКА, ЯКА НЕ ВМІСТИТЬСЯ Та, що з чотирма позиціями Дочірній запис Позиція доставки для кожної ДОСТАВКА 1001 Позиція доставки: холодильник Позиція доставки: комплект льодогенератора Позиція доставки: вивезення старого НАСТУПНА ПОЗИЦІЯ Новий запис, а не нове поле Якщо щось може траплятися багато разів, можливо, там ховається ще одна сутність. © 2026 Twinshark LLC · stevetecharc.com
Click to enlarge

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

Ви постійно бачитимете це в реальних org. Комусь потрібен опис кількома мовами, тож створюють поле Description_English, потім Description_French, потім Description_German. Або для погодження потрібні кілька людей, і в нас з’являються «Погоджувач 1», «Погоджувач 2», «Погоджувач 3». Коли я знаходжу таке під час рев’ю — по руках, виправляйте модель даних. Одне чи два, можливо, якщо в бізнесу справді завжди лише одне чи два. Усе, що більше, має бути дочірнім об’єктом: Переклад із мовою, Крок погодження з погоджувачем. Наступна мова чи четвертий погоджувач стає новим записом, а не новим полем і деплоєм.

Друга нормальна форма: описуйте всю річ

Section titled “Друга нормальна форма: описуйте всю річ ”

Друга нормальна форма, або 2НФ, важлива, коли запис ідентифікується комбінацією речей. Візьмімо Позицію доставки. Вона має власний ID запису, але кожна унікальна за Доставкою + Продуктом. Припустімо, ми зберігаємо доставку, продукт, кількість і опис продукту.

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

Тож запитання 2НФ таке: чи описує цей атрибут усю річ, яку я ідентифікую, чи лише її частину? Якщо він описує лише продукт, він іде до Продукту. Якщо він описує місце продукту в цій конкретній доставці (кількість, чи його встановлюють), він належить Позиції доставки. Багато адміністраторів ніколи свідомо не скажуть: «Зараз я застосовую другу нормальну форму». І це нормально. Мені набагато важливіше, щоб ви навчилися питати: що насправді описує цей факт?

Третя нормальна форма: не зберігайте факти про інші факти

Section titled “Третя нормальна форма: не зберігайте факти про інші факти ”

Тепер та, яка для мене найважливіша в щоденному моделюванні: третя нормальна форма, або 3НФ.

Кожен розподільчий центр має менеджера з боку роздрібного партнера — людину, яка користується партнерським порталом. Припустімо, ми записуємо ім’я менеджера DC прямо на кожну доставку. Що станеться, коли в DC з’явиться новий менеджер? Ми оновлюємо кожну доставку з цього DC. Знову той самий запах: ми повторюємо факт, який належить деінде. Доставка виходить із DC; у DC є менеджер. Це два зв’язки, і ім’я менеджера належить DC.

Класична підручникова версія — адреса: поштовий індекс, місто і штат зберігаються разом на кожному записі, хоча місто і штат зазвичай випливають з індексу. Та сама форма, те саме запитання.

Факт про інший факт: менеджер DC Дві панелі. На кожній доставці: запис Доставки з ID запису, датою доставки, розподільчим центром, а також містом і менеджером DC, обидва позначені як повторювані. Один раз, у DC: Доставка зберігає свій ID запису, дату доставки та ID розподільчого центру і виїжджає з Розподільчого центру, який має власний ID запису, назву, місто і менеджера. Примітка внизу: новий менеджер DC означає сотні оновлень ліворуч і одне праворуч. НА КОЖНІЙ ДОСТАВЦІ ОДИН РАЗ, У DC виїжджає з Доставка ID доставки ID ЗАПИСУ Дата доставки Розподільчий центр Місто DC ПОВТОРЮВАНИЙ Менеджер DC ПОВТОРЮВАНИЙ Доставка ID доставки ID ЗАПИСУ Дата доставки ID розподільчого центру Розподільчий центр ID розподільчого центру ID ЗАПИСУ Назва Місто Менеджер Новий менеджер DC: сотні оновлень ліворуч, одне праворуч. © 2026 Twinshark LLC · stevetecharc.com
Click to enlarge

Практичне запитання 3НФ таке: чи описує цей факт цю річ, чи щось інше, з чим ця річ пов’язана? Я хочу, щоб ви ставили його постійно.

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

Steve Trucking після нормалізації П’ять прямокутників, з’єднаних лініями. Роздрібний партнер керує нулем або багатьма Розподільчими центрами. Доставка виїжджає рівно з одного Розподільчого центру і містить одну або багато Позицій доставки. Кожна Позиція доставки — це рівно один Продукт, а Продукт є в нулі або багатьох Позиціях доставки. керує виїжджає з містить є в Роздрібний партнер Доставка Позиція доставки Product Розподільчий центр рівно один нуль або один один або багато нуль або багато © 2026 Twinshark LLC · stevetecharc.com
Click to enlarge

Ті самі три запитання, той самий результат: кожен факт в одному місці.

Простий спосіб думати про 1НФ, 2НФ і 3НФ

Section titled “Простий спосіб думати про 1НФ, 2НФ і 3НФ ”

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

  • 1НФ: чи не ховаю я кілька значень або повторювані структури всередині поля чи запису?
  • 2НФ: чи описує цей факт усю річ, яку я ідентифікую?
  • 3НФ: чи справді цей факт описує цю сутність, чи щось, пов’язане з нею?

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

Так, нормалізація йде далі: нормальна форма Бойса–Кодда (BCNF) посилює правила 3НФ для моделей зі складнішими ключами, а ще є четверта, п’ята і навіть шоста нормальні форми. Вони мають справу з рідкіснішими видами повторення — тими, що виникають більше з незвичних структур ключів, ніж зі звичайних бізнес-записів. Багато теорії, і, з мого досвіду, вона не варта головного болю для тих систем, які ми будуємо на Salesforce. У бізнес-системах тиск зазвичай іде в протилежний бік: ми набагато частіше відступаємо від третьої нормальної форми — заради користувачів, звітності й продуктивності, — ніж ідемо далі за неї. Це тема наступного модуля. Знайте, що подальші форми існують, але для більшості того, що ми робимо, 1НФ–3НФ дають нам потрібні інструменти мислення.

Проєктуйте до третьої нормальної форми

Section titled “Проєктуйте до третьої нормальної форми ”

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

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

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

Що тут вирішує архітектор

Section titled “Що тут вирішує архітектор ”

Коли я нормалізую логічну модель, я шукаю:

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

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