Універсальні рішення зустрічаються постійно при використанні бібліотек або фреймворкі, ви точно бачили магічні функції які уміли приймати всі можливі параметри, робити багато магії і віддавати результат.
Коли така функція є продуктом реалізації бібліотеки або фреймворку - ви не витрачаєте ресурси критичного мислення для оцінки правильності такої функції і просто використовуєте цей функціонал.
Відкладаючи у своїй памʼяті подібні приклади, ви запамʼятовуєте “псевдо-правельніʼʼ рішення і використовуєте у своїх проектах. Але тут потрібно провести аналіз:
Чи буде ваше рішення змінюватись у майбутньому ?
Чи має це рішення бути універсвльним ?
Як важко буде замінити, при потребі, або розширити існуючий функціонал ?
Скільки тестів потрібно написати щоб покрити 100% функціоналу вашого рішення ?
Відповідаючи на ці запитання для бібліотек:
ні, навряд.
так.
просто буде задипрікейчено у нових версіях, при потребі.
Тут спірно
Або покривають 100% тестів для всього що тільки можна.
Або тестується на валідних данних (все інше ішʼю на гітхабі).
Антипатерни
При побудові будь-яких універсальних функцій, класів, модулів потрібно опиратись на здоровий глузд.
Існує цілий список антипатернів які, розгляну ті із них які найчастіше зустрічаються при побудові “універсальних рішеннь"
Copy + Paste
Часта проблема при побудові великих модулів, або класів. У процесі розробки яка триває не 1-2 години, розробник просто копіює цілі рядки із уже існуючих методів, тим самим повторюючи існуючий функціонал.
У 100% випадків це приводить до дуже тяжкого процесу дебагінгу, або модифікації.
Якщо цей код потім проходить етап ревью - це значно уповільнює ревью тим що кожен такий блок коду потрібно переглядати повторно і якщо там є помилка то її потрібно повторно підсвітити.
Spaghetti code
Колись давно - це називалось (kangaroo code). У часи Паскалю який був переповнений мітками “go to” через які читання і дебінг такого коду був дуже складним.
Ця проблема часто виникає у людей які працюють над розробкою коду дуже великий проміжок часу і вони не розуміють, що люди які не знайомі з цим кодом не зможуть зрозуміти всю ідею просто поглянувши на код.
Проблеми тіж самі - складний дебагінг та модифікація.
Magic numbers
Якісь магічні числа або рядки які постійно виникають у коді, ці числа ніяк не визначені і не описані, що приводить до проблем при подальшій роботі із таким кодом через певний проміжок часу.
KISS або Accidental complexity
Ця проблема виникає у більшості випадків у початківців. Приклади:
Реалізаціє методів які уже були реалізовані кодовою базою проекту.
Перенавантажені області коду які вміють робити занадто багато логіки, яка не потрібна.
const callFn = <P extends any>(params: P , fn: Function) => {
return fn(params);
}
const log = (name: string) => console.log(`My Name is - ${name}`);
const awsomeLog = callFn('Naruto', log);
цінність такого коду трішки менша нуля, але виглядає наче тут думали.
Reinventing the wheel
Відноситься до попереднього антипатерну, вони часто йдуть у комплекті. Іноді складно зрозуміти чи уже існує той функціонал який вам потрібно імплементувати, але гугл був придуманий давно, і я на своїй практиці майже не зустрічав помилок фреймворку або завданнь які ще не доводилось комусь вирішувати.
Навіть якщо це відео на ютубі із “Демоном і Андроїдом" - краще подивитись його перед тим як пробувати вирішити все самостійно.
God Object
По назві уже зрозуміло і це є головний антипатерн при розробці комплексного універсального рішення, часто при розробці зʼявляються нові вимоги, або ламається уже існуючий функціонал. Найкращим рішенням чомусь виявляється - розширення уже існуючого класу або методу, щоб він вмів трішки більше. Це велика проблема, після декількох таких підходів уже буде складно цим користуватись, а ще страшніше буде модифікувати або замінити.
Не робіть так.
Чи потрібні універсальні рішення ?
Так. Однозначно потрібні, але це повинно бути добре продумана структурно і архітектурно самостійна одиниця коду. Як, наприклад, патерн - це універсальне рішення для великої кількості завданнь.
Патерни - це шаблони які варто знати хочаб для того щоб розуміти як робити правильно, і де не варто видумувати велосипед.
При розробці фронтенд застосунків дуже мала кількість задач є досі не вирішеною, що змушує засмучуватись, але з іншого боку - це дає розуміння того що вирішені задачі уже використовують найкращі рішення із можливих, бо були протестовані на самих різноманітних ситуаціях і користувачах.
Як зробити добре ?
Насправді, прочитавши антипатерни уже можна зрозуміти як робити не потрібно, що дасть розуміння слабких сторін при розробці універсальних рішень у майбутньому.
Яку задачу вирішуємо ?
Хоч це і “універсальне рішення" але все ж спектр задач які потрібно покрити цим рішенням - має бути обмеженим, або хочаб чітко зрозумілим. Якщо це стратегія по роботі із якимось стороннім сервісом то створіть чіткий план того що потрібно реалізувати і як це повинно працювати і тільки після цього - вперед до перемоги.
Які масштаби ?
Після того як ви чітко зрозуміли для чого ви створюєте це рішення, вам потрібно зрозуміти які його масштаби, чи це має бути сервіс, функція, клас, модуль, окремий пакет.
Якщо ваше рішення виходить за рамки 3к рядкі коду то це уже щось складне і занадто велике в рамках фронтенд проектів.
Передивіться чи точно у вас немає антипатернів і чи точно воно вирішує тільки ті задачі які повинно.
Чи потрібна оптимізація ?
Якщо ваше універсальне рішення потребує оптимізації - це уже не добре, спробуйте порозбивати код на менші блоки, декомпозиція існуючого функціоналу часто приводить до того що ви знаходите функції або блоки коду які не потрібні, або які можна замінити простішими рішеннями.
Підсумки
Потряпляючи у пастки антипатернів велику кількість разів ви навчитесь писати гарні “універсальні рішення". Пробуйте, тренуйтесь - це не легкий процес і швидко ви не отримаєте круті результати, але ви вирішуєте не самі легкі задачі якщо створюєте універсальні рішення.
Загалом існує велика кількість “граблів" на які можна наступини, не переймайтесь про це, тільки процес рефакторингу та саморефлексії буде давати плоди у вигляді професійного розвитку.