Структура та архітектура програмних систем: корисний кейс для якісної діджиталізації

КИЇВ — 21 квітня 2020 року. Настала епоха тотальної цифровізації, а карантин, у свою чергу, чудово сприяє самоосвіті та вдосконаленню. Ми пропонуємо об'єднати ці фактори, та нарешті зрозуміти, як влаштований цифровий світ? Допоможе нам в цьому архітектор технологічного стеку Metarhia, викладач КПІ Тимур Шемседінов.

За мотивами вебінару АПУ.

«Іноді все це може здатися дивним, мовляв, як взагалі воно може працювати?»

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

«Балачки нічого не варті. Покажіть мені код».

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

«Програми повинні писатися для людей, які будуть їх читати, а машини, які будуть ці програми виконувати  другорядні» (с) Гарольд Абельсон

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

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

«Я завжди мріяв про те, щоб моїм комп'ютером можна було користуватися так само легко, як телефоном; моя мрія збулася: я вже не можу розібратися, як користуватися моїм телефоном» (с) Бйорн Страуструп

Всі програмні компоненти, в будь-якій парадигмі, складаються з двох важливих речей  це алгоритми та структури даних, причому останні значно важливіше. Лінус Торвальдс сказав про це так: «Погані програмісти турбуються про коди. Хороші програмісти турбуються про структури даних і зв'язки між ними». Разом, алгоритми та структури даних, реалізовані у вигляді програмних компонентів, складають програми. Парадигма програмування  це набір ідей і понять, припущень та обмежень, концепцій, принципів, постулатів, прийомів і технік програмування для вирішення задач. Парадигма пропонує модель вирішення завдань, певний стиль, шаблони (приклади хороших і поганих рішень), що застосовуються для написання програмного коду.

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

«Програми стають повільнішими швидше, ніж «залізо» стає швидшим» (с) Ніклаус Вірт

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

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

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

«Більшість з програм на сьогодні подібні єгипетським пірамідам із мільйона цеглинок один на одному та без конструктивної цілісності - вони просто побудовані грубою силою і тисячами рабів» (с) Алан Кей

Що ж таке модуль? Бібліотека? Фреймворк? Чим вони відрізняються та як співвідносяться?

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

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

Врешті, тепер можна визначити, що ж таке структура і архітектура програмних систем. Архітектура – це про те, як розділити систему на частини, дати назви цим частинам і зібрати їх в єдине ціле. Існують архітектури файл-серверні, клієнт-серверні (бекенд і фронтенд), багатоланкові (наприклад, трьохланкові), багатошарові, монолітні, гексагональні, цибулеві, мікросервісні, хмарні тощо. Структура програми — це про те, як програмні компоненти пов'язані один з одним, а типів цього зв'язку є три: зв'язування з даними, за викликами та щодо подій. Зв'язування за даними — найефективніше за продуктивністю та найнебезпечніше при модифікації програм. За викликами ж — найпоширеніше. Вони можуть бути прямі (до конкретних програмних структур)та з застосуванням узагальненого програмування (зв'язування через інтерфейси). Останнє — найкраще з позиції гнучкості. Щодо ж подій  найслабший спосіб. Адже, якщо якщо у модуля відсутній обробник події, а вона приходить, то нічого страшного не станеться. Програма не випаде з помилкою, однак знайти її з таким зв'язуванням набагато складніше.

Процес розробки включає не тільки побудову програмних абстракцій і їх зв'язування, але й інші активності: перехресне рев'ю коду декількома розробниками, рефакторинг і оптимізацію, покриття тестами, застосування автоматичних аналізаторів вихідного коду і лінтерів, налаштування та запуск систем CI/CD (безперервних інтеграції та розгортання). Для цього застосовуються системи контролю версій, системи управління репозиторіями та проєктами. Все це підвищує і якість «володіння» кодом. Тут ми розуміємо не володіння інтелектуальною власністю, але знаннями про структуру коду, що дозволяє швидко вносити зміни. Іншими словами, покращення володіння кодами знижує «bus-factor» тобто, ризики, пов'язані з потенційним зниженням керованості та можливості швидкої модифікації, у момент, коли фахівець «володіє» знаннями про структуру коду, перестає виконувати свої обов'язки (за станом здоров'я, через зниження мотивації і раптового припинення життєдіяльності організму).

Використання бібліотек і фреймворків у відкритому коді (Open source) у розробці ПЗ, дозволяє швидше розробляти системи, однак додає інші ризики. Це вимагає особливої ​​уваги до якості та безпеки використання коду, «володіння» яким винесено з продуктового колективу. На жаль, програмісти, часто не мають часу та кваліфікації для аналізу залежностей, а менеджмент «жене терміни» і не передбачає ані спеціального часу, ані залучення окремих фахівців для цього. Крім якості, серйозною проблемою стає аналіз ліцензій на залежності, особливо коли цих залежностей сотні  а кожна з них має ще сотні власних. Навіть швидко переглянути ліцензії для тисяч залежностей, стає перепоною. Для цього створюють спеціальні програмні інструменти.

«В інженерії завжди є місце мистецтву та інтуїції» (с) Тимур Шемседінов

Витяг практичної користі з застосування обчислювальної техніки та ресурсів пам'яті, мережі, пристроїв зберігання, процесорів та інших апаратних компонентів, за допомогою різних методик, організаційної структури, прийомів і знань, називається інженерією. Інженерія програмного забезпечення  це додаток до індустрії розробки програмного забезпечення. Вона включає архітектуру, дослідження, розробку, тестування, розгортання і підтримку ПЗ. У сучасному програмуванні вона, звичайно, спирається на наукові знання, однак, не на 100%. В інженерії завжди є місце мистецтву та інтуїції, адже інженер змушений приймати рішення в умовах величезної невизначеності, а зробити це можна тільки при великому досвіді вирішення різнотипних завдань.

«Програмування сьогодні  це гонка розробників програм, які прагнуть писати програми з більшою та кращою ідіотостікістю, і Всесвітом, який намагається створити більше добірних ідіотів. Поки Всесвіт перемагає» (с) Рік Кук

Іноді все це може здатися дивним, мовляв, як взагалі все це може працювати?

«Більшість хороших програмістів роблять свою роботу не тому, що очікують оплати або визнання, а тому, що отримують задоволення від програмування» (с) Лінус Торвальдс

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

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

Мови з суворою типізацією, на відміну від мов із динамічною (які визначають типи ідентифікаторів під час компіляції), так само дозволяють знайти та виправити значну кількість помилок на етапі розробки (design time). Тоді як динамічні мови пришвидшують процес написання коду, та частина помилок виявляється лише під час виконання (run time), адже визначають типи ідентифікаторів вони вже саме на цьому етапі.

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

«Обмеження можливостей мови з метою запобігання програмістів помилок, в найліпшому з випадків небезпечне» (с) Бйорн Страуструп

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

Системне програмування — це розробка програм, які є засобами виробництва. Тобто, за економічною класифікацією, це товари групи А (виробництво засобів виробництва), на відміну від групи Б  прикладного програмування, тобто виробництва товарів споживання.

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

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

Do you have an interesting idea for an event?