Чаму абстракціі фронтэнду таямніча ператвараюцца ў тэхнічны борг
Дазвольце даклэ расказаць, чаму прытарпелыя абстракціі фронтэнда ствараюць скрытую сложнасць, і як выявіць, калі спакульнаныя компоненты, хукі чыстаінстры насправды вартае створваць.
У практычна кожнай базе коду фронтэнду ёсць момент, калі яна тыха пачынае перастаць быць самостоячным прыкладам і стае фрэймворкам, створаным для падтрымкі прыкладу.
Вы запускаеце бібліятэку компонентоў.
Потым прыходзіць система дизайна.
Потым прыўязуецца шар керавання станам.
Потым з’яўляецца абстракцыя для запрашання дадзеных.
Потым спецыяльны хук абгортае гэтую абстракцыю.
Потым хтось пішае універсальны компонент формы, який прымае об’ект налашоўкі, які описвае, як павінны дзейсць формы.
Чэраз некалькі часоў змена чаго-небудзь настолькі простага, як кантактная кнопка, значыць пераследаваць шэсць файлоў, тры шары абстракцыі і правілу, якога ніхто не можа прыцягнуць да яго творца.
Страннае ў тым, што ні аднаго з гэтых окремых выбораў на той момент не выдавалася неразумным.
Гэта сама сутнасная проблема ў тым, як команды фронтэнду керуюць абстракцыяй.
Большая частка абстракцый не ўласна пакінутая, і багато з іх дзейсна дапамагаюць. Проблема тая, што разработчыкі фронтэнду сталі надзвычайна майстрыя ў стварэнні абстракцый раней, чым зберуць достатню колькість доказаў таго, што такія абстракцый дасканальна патрэбны.
Мы больш не проста абстрагуем існуючую складнасць.
Мы абстрагуем нават самую можлівасць таго, што складнасць могла бы з’явіцца калі-небудзь.
Такая прычына стварае адзін відлічны вид тэхнічнага боргу.
Абстракцыя зазвычай пачынаецца з добрых намераў
Уявіце тры адзеленыя компаненты, кожны з якіх адпаведальны за запрашэнне дадзейнаў корыстніка.
У першага є частка дублюючайся логікі завантажэння.
У другага павторюецца майже той самы шаблон.
У трэціяга знову робіцца ўсё чырвоныя разлікі.
Хтось зазначае павторэнне і прапануе:
"Мабыць, нам следуе перакласты гэта ў спяльную абстракцыю."
Гэта сама па сабе ўзгодная заўважэнне.
Тады команда стварае спецыяльны хук:
const { data, loading, error } = useUserData(userId);
Гарна і апранутая структура.
Потым іншам компоненту патрабуецца невялікая корэктаванне ў працэсе выконання.
У замест на безпосередній выкалкавання да API, команда выбирае іншы варыянт:
useUserData(userId, {
includePermissions: true,
cache: true,
retry: 3,
});
Чэрез калькі месцаў хук больш не толькі запрашоўвае даныя корыстніка.
Тепер ён таксама карыстуецца кэшаванням, павторнымі спробамі, пераказамі прав, трансформацыямі дадзеных, нормалізацыёю адказоў пра аберанцыі, оптымістычнымі актуальненнямі і рознымі особлівасцямі, прызначанымі для конкрэтных функцый.
Первыя дублікацыі зниклі.
Але з’явілася іншая проблема: зростаючы разлік межа кодам і тым, што ён на самай працэ прадае.
Пагляд на компонент больш не паведамляе вас пра тое, звядзе ўзяліся яго даны.
Спачатку трэба зрозумець абстракцыю, яка стоіць перед ней.
Это той компрэс, пра які ніхто не говорыць.
Абстракцыя не пазбавляе сложнасці.
Яна толькі перамешчае яе.
Інодзе такое перамешчанне дэйсна ўцёмкавае.
Іншыя разы вы проста заменілі пяць ліній простай логікі на трыста ліній внутранняго механізма.
Абстракцыя мае цэну
Разработчыкаў з самага пачатку научаюць вважаць дуплікацыю недагодай.
Це цяперашняе, аднойчы так і ёсць.
Але дуплікацыя — здалека не ўсё, што ёсць сложнасцю.
Іншыя формы включаюць:
- непрямасць
- канфігурацыю
- непрамае паведанне
- гэнерычныя API
- закрытыя залежнасці
- канвэнцыі
- спадчына
- компоненты-обгорткі
- дэбагаванне, якое існуе толькі завяроўка абстракцыі
- кангэнітывны навантажэння
Дзеяная дуплікацыя часамі ўсё-такі дешэўшая, чым складная абстракцыя.
Пораўняйце два падходы.
У аднам варыянце невеликі фрагмент логіки павтараецца тры разы.
У іншам ствараецца універсальны інструмент з дзесятком параметраў, што ўсупершце падставлена таму, што тры ныякія варыянты выкарыстоўвання перакрываюцца прыблізна на 60 процэнтаў.
Другі варыянт выглядае болей апранутым, болей «інжэнерным».
Яго таксама можа быць значна сложней падтрымліваць.
Самэль гэта фронтэнд-работа рэгулярна падводзіць ся на галоўную прычыну.
Команды оптымізуюць код па прынцыпу DRY, а не па крэатывнасці коду, які лёгка ў разумеўцы.
Гэтыя цілі не ўзаемна заменныя.
Экасыстэма фронтэнда спрыяе гэтаму
Развіцце фронтэнда мае сваячыны стосункі з абстракцыяй, галоўная прычына — уся экасыстэма з самага пачатку практыкуеся як шары.
Адзін сучасны прыемлік можа лёгкая адчыніць фрэймворк для рэндаравання для стварэння компонентаў, якісь бібліятэку для маршрутацыі, менеджер стану на баке кліента, адзінокы інструмент для керавання станам на баке сервера, а таксама бібліятэку форм з сопрацоўнічальным пакетам для верыфікацыі. Да гэтага дадаецца система дизайна, набор ужо створаных компонентаў UI, абстракцыя стайлізацыі, якая паверх звычнага CSS, інструмент для складання, які координуе всё, і фрэймворк для тэставання, який стежыць за всім цім распадам.
Кожны з гэтых элементаў сам по сабе рашаяе рэальную проблему.
Усё пачынае ідзіць не так, калі прыемлік начынае дадаваць своия спецыяльныя шары на верх усьго гэтага.
У змену на безпосередняе выкарыстоўванне фрэймворка, разработчык вядзецца да внутраняга обёртака навакол яго.
Эты обёртак, у свою чаргу, залежыць ад ўсё новага обёртака.
Чырз некалькі часоў модэль праграмавання каманды перастае адпавядаць асновнай платформе.
Такія ситуацыі часта выклікаюцца ў большых арганізацыях.
Каманда можа знайсці ся ў стане, калі:
<AppPage>
<DataBoundary>
<PermissionGate>
<FormContainer>
<EntityEditor />
</FormContainer>
</PermissionGate>
</DataBoundary>
</AppPage>
Кожны элемент мае чыткая мета.
Кожны слой мае задокументаваную прычыну свайго існавання.
Але як толькі ў чымсь выйшаюць проблемы, разработчыку даводзіцца у своёй галаве перабудаваць весь стак, прычаму не прабываючы нават дасягнуць самага коду функцыі.
Такая перабудова карае як з точки зору канцэптуальных зусіль, так і з точкі зору выдаткова часу.
Універсальныя компаненты часта ўступаюць у спару з найбольшымі проблемамі
Аднам з простыяў шляхоў да складнасі фронтэнду є стварэнне компаненты, якая мае падпіраць усе будучыя вимаганні.
Усё пачынаецца проста:
<Button />
Потым ён трохі расте:
<Button variant="primary" />
Потым продовжае расширвацца:
<Button
variant="primary"
size="large"
loading
icon={...}
permission="admin"
analyticsEvent="save"
confirm
/>
У канцы застаецца ўсё тое, што тэхнічна вялікай часткай вже не ўважаецца кантаком.
Это мініатюрная палатка для адраджэння будзь-якіх дзеянняў.
Команда адчувае ся продуктыўна, калі новыя кантакі тепер можна склadaць за дапамой настройкі заместо ўсё новай реалізацыі.
Але настройкі ўсё рава являюцься кодам, незалежна ад таго, чым яны выглядаюць.
У дзеякіх аспектах гэта ўжо горш, таму што настройкі заслоняюць рэальны прайом керавання.
Прочытайце двадцать ліній простага коду, і вы зможаце точна разумець, што відбываецца.
Прачытайце двадцать ліній настройкі, і вам можа знадобіцца дакладна расследаваць реалізацыю компонента, праследаваць парсер настройкі, выявляць стандартныя значэння і з’ясаваць, якія опцыі таямніча взаімаўпрацоўваюць аднам з другім.
Экспліцытны код практычна быў заменены на вокабуляр.
Такій лексыкантнай базе можа быць практычна вялікая сіла.
Але яна таксама можа ператворыцца на дыялект, які ніхто не хоча падтрымваць.
„Абсалютна падтрымка“ — адлов
Абсалютная падтрымка зазвычай ёст тая найсильнейшая прабаўка, яку людзі прыводзяць для дадавання абстракцыі.
„Можа, нам гэта знадобіцца пазней.“
„Верагодна, па ходу з’явіцца ўсё больш варыянтав.“
„Гэта можа быць падыскана ў іншых частках прыстрою.“
„Давайце проста стварым яго у загальнаму выглядзе з самага пачатку.“
Інодзе гэты інстынкт ўсё-такі правы. Але значна чащэ проста няма ўсіх неабходных вядомасцей, каб знать.
Проблема заключаецца ў тым, што будзь-яка абстракцыя паводлівае за сабой прыпускі ўжо на стадіў стварэння. Чырпанне занадта рана означае фіксаванне архітектурных выбораў пры тым, калі вы яшчэ не разумеете, што саме ствараеце. А калі іншыя часткі кодавой базы пачнуць завісець ад гэтай абстракцыі, змяніць курс стане дужа дорога.
Самэ гэтая прычына змушвае сказаць, што раннее чырпанне ўскладнюе справу, ніж можа здавацца. Дуплікатываны код зазвычай лёгка перерабіць, калі толькі будзе час. Абаротная абстракцыя, з іншай боку, часта распростраńваецца на іншыя элементы.
Уявіце сабе тры компоненты, якія выглядаюць падобна. Вы можете нараз чакаць з рашэнням прыбліжнасці. Якщо з часам з’явіцца справжній спакульнацый шаблон, тады яго можна выявіць. Але якщо вы сразу перейдзеце да загальнай абстракцыі, кожны будучы варіант викорыстоўвання будзе прымушаны паслужыць тым прыпускам, якія вы зробілі спачатку.
У такі моменты абстракцыя перестае быць зручнаю прыемай і становіцца абмежэннем. Тое, што мяў на мете падрыхтаваць усуненне павторэння, ў канечнасці робіць змены складнейшымі, чым гэта было б у разе простага дуплікацыі.
Хорашыя абстракцыі зазвычай нарадзяюцца з болю
Наймоцнейшыя абстракцыі часта не плануюцца заздалегідь. Їх адкрываюць.
Команда болей чым раз рэалізуе адно і тое ж поведанне. У канечнасці вони з’являюць, якія часты ўсё-такі ідэнтычныя, а якія проста выглядаюць схожа на першы поглед. Яны розумеюць, чым на самай справе разлічаецца. Ляжыш толькі тады вони выделяюць стабільную, спакульнае ядро.
Гэта стварае набліжна моцнейшую базу. Этот практык можна описаць так:
Дуплікацыя ведае да павторэння, павторэння — да розумежвання, а розумежванне — да абстракцыі.
Аднак команды фронтэнда часта выбіраюць іншы пат.
Магчымас прыводзіць безпосередня да абстракцыі, потым да налаштавання, а пасля — да плутанні.
Першы падход захопляе больш часу на самы спачатку. Але за весь періяд жыцця проекта ён зазвычай є быстрэйшы в цяласці, таму што абстракцыя, якая ствараецца, відбівае знання, якія команда фактычна атрымала за дапамою дазнаўання.
Не весь дублірацыя трэба адмахнуць
Гэта некомфортная правда для разработчыкаў, адколі дублірацыя за звычай выглядае як памылка. Але інодзе дублірацыя є правильным рашэнням.
Спачатку, два компоненты выкорыстоўваюць практычна ідэнтычную логіку верыфікацыі. Якщо гэтая логіка складаецца толькі з пяці рэйсоў, а кожны компонент падчыняецца разным бізнес-правілам, тады залішыць код у дубльованым варыянте можа быць розумнейшым выборам.
Чаму? Таму што ўтриманне іх окалечна спакоюе местныя падходы. Людзі, які працуюць з адным з компонентаў пазней, можу налаштаваць яго працэю без пабоювання, што вони випадкова зіпсуюць другі.
Дуплікаваны код непрама кажа: гэтыя два элементы зараз проста супадаюць.
Абстракцыя кажа ўжо значна сильней: гэтыя два элементы павінны быць ідэнтычныя, і яны должны зміняцца разам у будучыні.
Гэта ўжо суперша большая тэза. Яе трэба выкорыстоўваць толькі тады, калі вы сапраўды верыце, што гэта правда.
Абстракцыі павінны мець маленькі API
Практычны паказнік — гэта: сколькі на самай працы трэба выучыць, каб можна было правільна викорыстоўваць даную абстракцыю?
Якщо гэты список продовжвае растаць, верагодна, вы бачыце, як абстракцыя ператвараецца на фрэймворк.
Хорашая абстракцыя маскіе складнасць. Паўзліва абстракцыя маскіе рашэнні. Гэта звучыць падобна, але гэта зовсім не тое ж самае.
Ояўнеліквалярна спроектаваны API можа выглядаць так проста:
const user = useUser(id);
Паўзліва версія пачынае дадаваць флагі і опцыі, і ў канцэ настае ў такім вачынку:
const user = useUser(id, {
cache: true,
normalize: true,
permissions: true,
optimistic: false,
retry: 3,
suspense: false,
transform: customTransform,
mode: "editor",
});
У певны момент абстракцыя перестае спрасцаваць ўсё. Яна проста пераносіць первінную проблему ў об’ект налаштавання. Такі перыяд варта спрыймаць як апазнаку проблемы.
Командам фронтэнду патрабуецца бюджет на абстракцыі
Команды вже рэгулярна гаворяць пра бюджеты на выконванасць, розмер пакетаў, колькісць бядаў і інфраструктуру. Варта дадаць да гэтага списку і бюджет на абстракцыі.
Мэтай не ўсегда яўляецца меньшая колькісць абстракцый у абсалютных цыфрах. Мэтай є старанне падтрымваць колькісць абстракцый у межах таго, што команда можа з лёгкасцю запам’ятаваць.
Перш чым дадзіць новую абстракцыю, корисна падзець калькі пытанняў.
Сколькі разоў вялікае гэта патэрн уже з’являўся? Якшто адказ — аднае раз, не трэба яго ўжо абстрагаваць. Якшто два разы, спрыйміце гэта як прычыну для падазру, а не для дзеяння. П’ять разоў вже можа лягчаць як справжнія доказы.
Далей: чы праваляюцца гэтыя раштункі з тых сабе прычын? Тое, што яны выглядаюць падобна, — гэта недастатнія практычная прычына. Два элементы можуць выглядаць майже ідэнтычная, пры тым эвалюючы па абсолютна разных шляхах з розных бізнес-прычынаў; у такім случае ўзяць іх разам як адну абстракцыю можа быць памылкай.
На завершанне: чы гэтая абстракцыя даслівна спроствае ўседзённыя ситуацыі? Не якуюсь гіпотэтычную будучую ситуацыю — а тыя, з якімі людзі сталквуюцца ўсё час. Якшто для викорыстання абстракцыі трэба ўсё час перачытаць яе дакументацыю, верагодна вы просто заменілі адну проблему на болей вялікую.
Найлепшы код фронтэнду частаца ўсоўны
У развіцці програмнага забавства існуе сваяя іерархія статусаў. Хыткая абстракцыя выглядае впечатліваючая больш, чым тры простыя компаненты. Гэнерычная система здаецца болей архітектурна значным элементам, чым простая функцыя. Фрэймворк, які можна перадаць знову і знову, выглядае болей професыянальным, чым кантэйнер з дублюючымся кодам.
Але працоўныя системы не награджаюць код за тое, што ён выглядае сложным. Яны награджаюць код, які разработчыкі можуць без апасення мяніць.
Найцэннейшы код фронтэнду зазвычай ёсць саме тым усоўным. Калі вы ачыняеце компанент, вы можаце адразу пабачыць, звядзе ўзьязначаюцца яго даны. Вы можаце точна пабачыць, што выканаецца, калі корыстнік чыгне ў якой-небудзь элемент. Вы можаце безпасна мяняць структуру коду. Вы можаце праследаваць, як перадаюцца даны. Вы можаце без проблем знайсці вызов API.
Вам не патрэбна быць вынушаным запам’ятаваць внутрэшную філасофію дизайна команды толькі для таго, каб вылечыць баг. Гэта не знак незроўнасці інжынерных рашэнняў — гэта знак хорашых інжынерных рашэнняў.
Абстракцыя павінна скасаваць неабходнасць думкі, а не яе падвышыць
Абстракцыя існуе не для таго, каб код выглядаў як можна болей перадаўны. Яе мета — зробіць систему простэйшай для розумення. Гэтая стандарт, які команды фронтэнду павінны ставіць кожной абстракцыі.
Якщо абстракцыя дазволяе дзесяці компонентам разделяць складную поведэнчнасць, не вымагаючы, каб кожны разработчык розумеў, як гэтая поведэнчнасць рэалізуецца, яна выпалняе свою функцыю. А якщо ж кожны разработчык павінен выучыць складны API толькі каб налаштаваць аднаго простага компонента, абстракцыя, верагодна, працуе проты вас.
Гэта разліка мае значэнне, таму што работа з фронтэндам ўжо сама па сабе є складной. Браузеры ўскладненыя, інтэрфейсы корыстувача таксама ўскладненыя. Синхронізацыя стану є складной задачай, так сама як і забезпечэнне доступнасці та выкаання. Неякое дадаткова складнасць не трэба ствараць проста для таго, каб архітектура на паперы выглядала болей супэрнаўзданай.
Наступны этап развіцця фронтэнду, верагодна, не будзе вынікнуць з адкрыцтва ўсё новага шару абстракцыі. Ён выйдзе з павышэння майстэрнасці ў ведаманні таго, калі не трэба яго ствараць.
Інжынеры, якія выдзеляюцца, не павінны быць тымі, хто можа спроектаваць найболей розгледзеную систему компонентав, якія можна перадаць далей. Це буду тыя, хто можа паглядзець на проблему і правільна адначасова ацэніць, чы рэальна ўзагалі патрэба ў такой системе.
Інодзе правыя абстракцыя — цэх адна функцыя. Інодзе — кампанент. Інодзе — проста добра названая модуль. А інодзе — гэта пяць рядкоў дублюючага коду, які кожны з команды можа прачытаць і адразу зрозумець.
Разлічэнне гэтых ситуацыяў — гэта сама навык, які варта развіваць.
Спадневаная літэратура
- Дзесяць архітектурных навыкаў, якія дапамагаюць падтрымваць кодбазы фронтэнду працездатнымі гадамі — Якраз тут апісваюцься структурныя навыкі, такія як оптымізацыя для можлівасці выдалення, чысты прайом перадачы даных і ізоляцыя бізнес-логікі, якія дапамагаюць кодбазам застаўацца працездатнымі праз гады змян.