10 помилок, що призводять до оверинжинирингу

Кілька речей гарантовано будуть збільшуватися з часом: відстані між зірками, ентропія всесвіту і бізнес-вимоги до ПЗ. Багато статті пишуть «Не ускладнюйте!», але чому не пишуть або як це зробити. Ось вам 10 ясних прикладів.

1. Інженерам видніше
Ми, інженери, вважаємо себе умнейшими людьми. Ну, оскільки ми створюємо різні штуки. І ця помилка часто призводить до оверинжинирингу. Якщо ви спланували і побудували 100 модулів — Бізнес завжди попросить у вас 101-ий, про який ви ніколи не замислювалися. Якщо ви зберетеся з силами і вирішите 1000 проблем — вони прийдуть до вас і викладуть на стіл 10 000 нових. Ви вважаєте, що у вас все під контролем, а насправді ви навіть не уявляєте, в якому напрямку вас завтра поведе дорога.
image
За мої 15 років роботи програмістом я ще ні разу не бачив, щоб Бізнес видав закінчені і стабільні раз і назавжди вимоги до ПЗ. Вони завжди змінюються, розширюються. І це природа бізнесу, а не помилки людей, керуючих їм.

Мораль: Казино (бізнес) завжди перемагає


2. Повторне використання коду
Коли Бізнес підкидає нам все більше і більше вимог (як очікується), ми іноді говоримо собі: «Гаразд, давайте спробуємо згрупувати та узагальнити все, що тільки можна!».
image
Ось чому більшість MVC-систем закінчуються Великою Моделлю або Жирним Контролером. Але, як ми вже з'ясували, бізнес-вимоги ніколи не припинять додаватися, а значить це шлях в нікуди. Насправді нам потрібно реагувати на це ось так:
image

Приклад: одного разу для одного з замовників ми розробляли модуль профілю користувача. Почали з класичного CRUD-контролера, ну бо це ж просто профіль користувача і там все має бути просто. Закінчилося це все реалізацією 13 різних сценаріїв створення і заповнення цього профілю: реєстрація через соціальну мережу, звичайна, спрощена, швидка з подальшим редагуванням, зовсім інакше виглядає для підсайтів, і т. д. Вони мали між собою дуже мало спільного. Аналогічно відрізняються і, наприклад, сценарії «перегляд замовлення» і «редагування замовлення».
Спробуйте для початку розділити бізнес-функціональність вертикально, перш ніж ділити її горизонтально. Це навіть робить більш простий завдання поділ моноліту на микросервисы. Іноді і зовсім вже стає все одно моноліт у вас чи сервіси. В іншому ж випадку стає все складніше і складніше змінювати частини вашої системи.

Мораль: віддавайте перевагу ізольовані дії, уникайте комбінування

Підказка: Візьміть деяку видиму користувачеві частина вашої програми (форму\сторінку\команду). Скільки перемикань контексту знадобиться програмісту, щоб зрозуміти весь код, пов'язаний з нею?

3. Узагальнимо взагалі все
(Дещо перегукується з попереднім пунктом, але іноді трапляється і незалежно від нього)
  • Потрібно під'єднатися до бази даних? Давайте напишемо Узагальнений Адаптер.
  • Зробити запит? Узагальнений Запит.
  • Передати йому параметр? Узагальнений Параметр.
  • Зібрати кілька параметрів у що-то? Узагальнений Builder.
  • Відобразити отриманий результат у що-то? Узагальнений Data Mapper.
  • Обробити запит користувача? Узагальнений Запит.
  • щось виконати? Узагальнений Виконавець.
  • і т. д.


Іноді інженерів заносить. Замість вирішення бізнес-проблем вони витрачають час для вибору правильного батьківського класу. А відповідь проста.
image

Архітектура ПЗ завжди грає в догонялки з вимогами бізнесу. Так що навіть якщо ви з допомогою якоїсь магії знайдете ідеальну абстракцію сьогодні, в комплекті з нею відразу буде йти термін її придатності — див. пункт №1 вище — бізнес завжди перемагає. Так що найкраща характеристика якості збірки вашої архітектури — як швидко вона може бути розібрана. відмінна стаття про те, як писати код, який буде легко видалити, а не легко змінити.
Мораль: Дублювання коду краще, ніж невірна абстракція

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

4. Написання обгорток для всіх зовнішніх бібліотек
Існує практика написання обгорток (врапперов) для кожної використовуваної зовнішньої бібліотеки (в основному з-за прямування стилю основного продукту, іноді з причин, описаних в попередніх двох пунктах). Дуже популярний підхід у ентерпрайз-програмуванні. Програмісти на динамічних мовами начебто Node/Ruby/Python просто додають бібліотеку і починають її використовувати. Але Ентерпрайз Розробник на це піти не може — йому необхідно створити обгортку. А потім ще обгортку над обгорткою. Можливо, це мало якийсь сенс у 2000-их, коли більшість бібліотек представляли собою мішанину, та й взагалі відкритого коду було не так багато.

Але сьогодні вже 2016-ий рік. Зовнішні бібліотеки покращилися на порядок. Вони стали просто фантастичні. Швидше за все вони написані відмінними програмістами і оттестировать краще вашого основного коду. Вони мають виразний API. У них можна вбудувати логування. Вам не потрібно витрачати свій час на написання обгорток навколо і так вже хорошого коду. Крім того, більшість обгорток — абсолютно безглузді. Їх інтерфейс дуже тісно пов'язаний з лежить нижче бібліотекою, часто просто у вигляді відображення функцій «один до одного». Якщо в майбутньому бібліотека змінитися, більшість коду з використанням даної обгортки також доведеться змінити. Створення «агностической» обгортки, здатної залишитися незмінною навіть, наприклад, при повній заміні обіг бібліотеки на іншу — нетривіальне завдання. Такі завдання іноді ставляться з думкою про потенційну «конфигурабельности» спільного рішення, сама ідея якого буде розглянуто трохи нижче.

Мораль: обгортки повинні бути винятками, а не нормою

5. Сліпе застосування метрик якості коду
Бездумне слідування концепціям якості коду (на зразок того, що всі змінні повинні бути оголошені як «private final», кожен клас повинен мати інтерфейс) не зробить ваш код АВТОМАТИЧНО хорошим. Подивіться, якщо ще не бачили, на ентерпрайз-версію FizzBuzz або Hello World. Прорва коду. На мікро-рівні кожен клас принципів SOLID і використовує відмінні патерни. Якщо ви натравите цей код на який-небудь інструмент аналізу якості коду — він скаже, що це прекрасний продукт, прямо диво. Але якщо ви зробите крок назад відразу побачите яким кошмаром є ця програма, всього лише друкуюча «Fizz Buzz».

Мораль: завжди робіть крок назад і дивіться на загальну картину.

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

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

5.1. Шари сандвіча


Давайте візьмемо деякий тісно пов'язаний функціонал і розділимо його на 10-20 шарів, де кожен шар не має жодного сенсу без решти. Ну, тому що ми хочемо реалізувати концепцію «Тестованого коду», або «Принцип єдиної відповідальності», або назвіть це ще як-небудь модно. У минулому це робилося через успадкування. Клас А успадковується від Б, який успадковується від З і т. д.
image
Сьогодні люди роблять все те ж саме, крім того, що кожен клас тепер представляється парою інтерфейс/клас і инжектируется на наступний шар, тому що у нас же SOLID.
image
Мораль: концепції вимагають вдумливого застосування. Їх не можна застосовувати скрізь і завжди, як інструменти.

6. Синдром Новинки
Вивчили дженерики. Тепер у нас простий «HelloWorldPrinter» стане «HelloWorldPrinter<String,Writer>».
Не потрібно використовувати дженерики, коли реально необхідна форма буде завжди инстанциироваться одними і тими ж типами даних. Параметрів достатньо у більшості випадків.

Вивчили патерн Стратегія. Тепер всі оператори if» будуть стратегією.
Чому?

Навчилися писати DSL. Тепер у нас DSL буде скрізь і все.
Ну я навіть не знаю…

Виявили моки. Тепер у нас буде замокано все вздовж і впоперек.
Та ну як же…

Метапрограмування — шикарна річ, давайте використаємо тут і тут!
Поясни навіщо…

Методи розширення\концепти\щось ще — хороша штука, давайте будемо використовувати кругом!
А давайте не будемо!

Мораль: нічого не треба застосовувати скрізь і завжди. І розділ «Мораль» теж не треба б писати в кожен пункт.

7. «X–сть»
  • Конфигурабельность
  • Безпека
  • Масштабованість
  • Поддерживаемость
  • Розширюваність
  • ...


Розпливчасто. Незмінно. Важко посперечатися.
Приклад 1: Давайте створимо CMS, щоб клієнт зміг сам додавати поля от у цю форму.
Результат: Клієнти ніколи не будуть нею користуватися. Коли знадобляться — вони знайдуть розробника і спантеличать його. Можливо замість повноцінної CMS варто накидати коротку інструкцію про те, як швиденько додати поле.

Приклад 2: Давайте спроектуємо великий шар для роботи з базами даних, для спрощення «Конфигурабельности». Ми повинні отримати можливість змінити базу даних правкою одного конфіг-файл.
Результат: За 10 років роботи я бачив лише один проект, де знадобилося міняти одну базу даних на іншу з об'єктивних причин. І, коли до цього дійшла справа, то «правкою одного конфіг-файл» справа зовсім не обійшлося. Була маса супутньої роботи. Несумісність, прогалини в функціоналі. А ще одного разу клієнт попросив перевести ПОЛОВИНУ наших моделей даних в нову NoSQL базу даних. У нас був «магічний файл» з підключенням бази даних, але по-перше, тільки реляційної, а по-друге для всіх даних, а не для половини. Можливо всі ці танці з конфігурабельністю мають сенс, коли йдеться про щось на кшталт Оракловской бази даних, яка стоїть як найм 20-ти програмістів — там і правда зручно мати можливість переключитися на щось інше при необхідності. Але для сучасних баз даних, все що нам необхідно — це простий набір вертикальних DAO-класів замість широкого горизонтального шару ORM. Не існує єдиної моделі, вдало поєднує в собі SQL та NoSQL, так що, можливо, нам дійсно варто їх розділяти, використовуючи у кожному випадку те одне, то інше, замість того, щоб намагатися поєднати непоєднуване і городити жахливі невірні абстракції.

Приклад 3: Ми побудували систему OAuth-для авторизації ентерпрайз-клієнтів. Для адміністраторів цієї системи нас попросили додати ще один рівень — авторизацію адміністраторів через Google OAuth. «Тому, що має бути безпечно». Якщо хтось зламає нашу OAuth-авторизацію, потрібно щоб хоча б учеткі адміністраторів залишилися недоступні. Google OAuth — надійна штука, так що заперечувати тут начебто особливо нічого.
Результат: Тому, хто захоче зламати цю систему зовсім не потрібно пробиватися через OAuth-шар. Можна знайти уразливість в чому-небудь простіше (такі завжди є). У підсумку всі витрати на підтримку двох рівнів OAuth (а вони проходили наскрізь через всю систему) дали приблизно ніяких результатів. Краще витратити цей час на усунення звичайних вразливостей.
image
Мораль: Не приймайте всі ці характеристики з закінченням на "-сть" як незмінну даність. Вони мають свою ціну — так що чітко визначайте Сценарій\Історію\Використання.

8. Велосипедостроение
Це завжди круто на початку. Але через кілька років це вже буде Вантаж Спадщини.
Пара прикладів:
  • Свої бібліотеки (HTTP, міні-ORM, кешування, конфігурація)
  • Свої фреймворки (CMS, обробка подій, багатопоточність, фонові завдання)
  • Свої інструменти (система збирання, система деплоя)


Що тут поганого:
  • Тривіальні, здавалося б, завдання насправді вимагають неабияких знань і глибокого занурення в предметну область. Який-небудь «запускатель процесів» вимагає розуміння менеджменту процесів в ОС, роботи демонів, системи вводу\виводу і ще купи всього. CMS — це не просто щось, подставляющие дані шаблони — там є залежності, перевірки, визарды, узагальнення і т. д. Сама проста на вигляд бібліотека може мати нетривіальну функціональність.
  • Все це потрібно ще й підтримувати, оновлювати.
  • Якщо ви викладете код у відкритий доступ — всім буде плювати. Ну, може, крім тих, хто працював над цим кодом раніше.
  • Люди, які розбираються в цьому коді — рано чи пізно підуть. А крім них у цій реалізації не розбирається ніхто в світі.
  • Впровадження в проект вже існуючих бібліотек та фреймворків, заточування їх під ваші потреби вимагає час прямо зараз. Але винахід власних велосипедів вимагає куди більше часу в довгостроковій перспективі.


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

9. Розвивайте свій код
Як тільки ви реалізували що-небудь певним чином — всі інші починають використовувати в тому вигляді, в якому це реалізовано. Ніхто не замислюється, чи правильно це. Поки ваш код працює — це «хороший код». Люди починають застосовувати його навіть для завдань, які він спочатку не мав вирішувати. Виходить іноді добре, а іноді дуже погано. І тут треба не боятися брати і змінювати ваш код, удосконалюючи його під поточні завдання. Здорові програмні системи завжди живуть, змінюються. Нездорові — лише доповнюються. Якщо шматок коду не бачив комітів дуже давно — з ним щось не так, він «пахне». Кожна частина коду повинна розвиватися. Ось відмінна стаття, описує це.
Ось як команди працюють над завданнями, а ось як вони повинні це робити, Кожен День:
image

Мораль: рефакторинг — це частина будь розробки. Ніякої код не залишається незмінним.

10. Неправильні оцінки термінів
Часто ми бачимо, як начебто непогані команди або окремі кодери раптом видають відверто слабкий продукт. Ми дивимося на кодову базу і дивуємося — «Як же так, невже це було дійсно створено цією командою\людиною, а я ж вважав їх\його такими розумними\розумним!». Якість вимагає не тільки вміння, але й часу. Навіть розумні розробники часто переоцінюють свої можливості. І раптом виявляють себе перед палаючим дедлайном, болісно вписывающих код жахливий милицю, дає шанс якось вкластися в терміни.

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

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.