1. Ефективність

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

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

Коли програма має гарну архітектуру, завжди досить легко зрозуміти, як вона влаштована і де писати код. Програму з гарною архітектурою легше змінювати, тестувати, налагоджувати та розвивати. Розумні люди сформулювали такі критерії гарної архітектури:

  • Ефективність;
  • Гнучкість;
  • Розширюваність;
  • Масштабованість;
  • Тестованість;
  • Супроводжуваність коду.

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

Ти постійно зустрічатимеш програми, які не роблять те, що заявлено.

  • Libre Office – повноцінна заміна Microsoft Office (насправді ні);
  • Браузер Edge підтримує всі вебстандарти (насправді ні);
  • Банк дбає про безпеку особистих даних своїх користувачів (насправді ні).

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

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

2. Гнучкість

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

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

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

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

По можливості архітектурні рішення не повинні «висікатися на камені», і наслідки архітектурних помилок мають бути в розумній мірі обмежені. "Гарна архітектура дозволяє відкладати прийняття ключових рішень" (Боб Мартін) і мінімізує "ціну" помилок.

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

Масштабованість

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

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

3. Розширюваність

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

Це так званий принцип YAGNI — you ain't gonna need it, "тобі це не знадобиться". А проте архітектура повинна дозволяти легко нарощувати додатковий функціонал у міру потреби. Таким чином, щоб внесення найімовірніших змін потребувало найменших зусиль.

Вимога, щоб архітектура системи мала гнучкість і розширюваність (тобто була здатна до змін та еволюції), є настільки важливою, що вона навіть сформульована як окремий принцип — "Принцип відкритості/закритості". Open-Closed Principle — другий із п'яти принципів SOLID: програмні сутності (класи, модулі, функції) мають бути відкритими для розширення, але закритими для модифікації.

Іншими словами: повинна бути можливість змінювати та розширювати поведінку системи без переписування вже існуючих частин системи.

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

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

Пам'ятаєш сервлети та фільтри? Навіщо були потрібні фільтри, та ще й з окремими інтерфейсами, якщо по суті ту саму логіку можна було реалізувати за допомогою сервлетів?

Саме винахід концепції фільтрів (службових сервлетів) дозволив винести різні службові функції до окремого шару. І в майбутньому при зміні поведінки фільтрів не потрібно було змінювати сервлет.

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

4. Тестованість

Якщо ти Java Backend Developer, твої серверні програми часто віддають назовні набір методів у вигляді REST API. І щоб перевірити, що всі твої методи працюють як задумано, їх потрібно покрити тестами.

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

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

"Навіщо потрібні тести, якщо код працює?", — запитає новачок.

"Навіщо потрібен робочий код, якщо його неможливо тестувати?", - запитає професіонал.

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

Наведу цитату з книги “Ідеальна Архітектура”: "Використовуйте принцип "тестованості" класу як "лакмусовий папірець" гарного дизайну класу. Навіть якщо ви не напишіть жодного рядка тестового коду, відповідь на це питання в 90% випадків допоможе зрозуміти, наскільки всі" добре” чи “погано” з його дизайном”.

Існує ціла методологія розробки програм на основі тестів, яка так і називається – розробка через тестування (Test-Driven Development, TDD). Це вже, звісно, інша крайність: напиши код, перш ніж писати код.

5. Супроводжуваність коду

Над програмою, як правило, працює багато людей — одні йдуть, нові приходять. Середній час роботи програміста в IT-компанії – півтора роки. Тож якщо ти прийшов на проєкт, якому 5 років, то лише 20% твоїх колег працювали над ним із самого початку.

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

Тому хороша архітектура має давати можливість відносно легко та швидко розібратися в системі новим людям. Проєкт має бути:

  • Добре структурований.
  • Без дублювання.
  • З добре оформленим кодом.
  • З документацієюЮ яку підтримують.
  • Із застосовуванням стандартних та звичних рішень для програмістів.

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

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