Дзесять архітектурных прычын, якія дапамагаюць падтрымваць базы коду фронтэнду працездатнымі гады.
Адказвае пра структурныя прычыны, такія як оптымізацыя для можлівасці выдалення, чысты праймап дадзейнаў і ізаляванне бізнес-логікі, якія спакойваюць кодавую базу ў стане, які дазволяе яе падтрымваць пра годзінны перыяд змян.
Кожная база коду фронтэнда, яка прыжывае довольна дзялейчас, зрэшты дзеліцца на два адналежнага регіёны.
Першы можна назваць Зонай апакоўкі.
Это хаотычная сумешчанка калектароў статаусу, падправленых хуків жыцёвага циклу, хутрых, але незрозумелых глобальных абстракцый, напаловыя експерыменты і функцый-памочнікаў, напісаных летаў назад, якія ніхто вядома не можа ўжо цалкам адказаць.
Ніхто не хоча падходзіць да яго.
Калі ў тую частку прыкладнення падае запит на новую функцыю, каманда не ацэнівае сложнасць задачы на адной падставе таго, насколькі складна сама робота.
Яны ацэніваюць яе на адной падставе таго, насколькі рызыкована выглядае праця з гэтым кодам.
Змена, якая мала бы зайняць два дні, ператвараецца на задачу, якая трэба выконваць две недзелі, таму што всі ведаюць, што большая часоўка пайдзе на тэставанне регрэсій, а не на стварэнне.
А ёсць і другі регіён.
Назваце яго Стабільным фундаментам.
Это модулі, створаныя багато гадоў раней, якія тыха перазсталі калькі канфрэймворкаў, перадзялы дизайна, змены напрамку продукту і змены ў кераванні інжынерамі.
Яны практычна ніколі не вызвалі перыядоў.
У спосабе ў якім яны напісаны немаў нічага ўзначальнаяго.
А калі да команды прыўязываецца новы чалавек, ён можа ачысці адны з гэтых файлоў, зразумець, што ён робіць, не патрабаваючы інструкцый, і апрацаваць першы pull request за дзень-два.
Гэта тое, на што варта звернуць увагу.
Код, які перазстае, зазвычай не ёсць найсучаснейшым кодам у системе.
Ён зазвычай ёсць найпростейшым.
Амальнаваленые інжынеры ведаюць, што програмнае забезпечэнне ніколі не застаёцца ў тых умовах, пад якімі было створана.
Трэбаванні зменяюцца.
Команды перарганізуюцца.
Залежнасці зміняюцца.
Канфрэймворкі розвиваюцца.
Компаніі зміняюць стратэгію.
Людзі пакідаюць компанію.
Новыя інжынеры прыходзяць без жадных пояснэнняў пра тое, чаму ўсё было створана такім спосабам.
Таму пытанне, якое варта задаць, не ёсьць:
"Насколькі чыста выглядае гэтая дизайн-раскладка зараз?"
А ёсьць:
"Насколькі це будзе дорога — зменіць гэта чераз пяць гадоў?"
Наступныя — это структурныя прыкметы, якія дазволяюць досягнуць такой трываласці.
1. Оптымізаваць для можлівасці выдалення, а не падзейнасці
Большасць керавання архітектурой сфокусавана на падзейнасці.
Зробіце вашы компоненты падзейнымі.
Ствараце універсальныя сервісы.
Дадзіце можлівасці расшырэння.
Проектаваць архітектуры плагінаў.
Напісце абстракцыі для рэалізацый, якія вы ўсё ще не створылі.
Падзейнасць мае свое месца.
Але існуе ўсё жа адна якосць, яка часта мае большое значэнне для продукту, які постоянна развіваецца:
Някім лёгка ўсё гэта выдаліць.
Функцыяны не ўсталяваныя.
Їх заменяюць.
Їх перабудовваюць.
Їх аднаўляюць у іншыя функцыяны.
Інодзе компанія проста втрачае інтерес да ўсьго гэтаго.
Уявіце сабе проект на React, які строга арганізаваны па типах файлоў:
src/
components/
BillingTable.tsx
UserModal.tsx
SubscriptionCard.tsx
hooks/
useBillingData.ts
useUserData.ts
useSubscription.ts services/
billingApi.ts
userApi.ts
subscriptionApi.ts
На першы погляд, гэта выглядае апранута.
Кожны тип файлу мае свой назначаны папаку.
Але заўважкі, якшы компанія пасля вісемнаццаці месцаў прыме рашэнне зовсама адмовіцца ад процесу выкладзення рахункоў.
Дзе на самай працы знаходзіцца ўсё, што стосуецца выкладзення рахункоў?
Патрэбна будзе шукаць у калькі разных дырэктарыяў.
Вы знаходзіце і выдаліце BillingTable.tsx.
Потым знаходзіце useBillingData.ts.
Потым — калькіяванні типаў, напісаныя спецыяльна для выклікання рахункоў.
Потым — функцыя-памочнік, яку выклікае толькі система выклікання рахункоў.
Потым — шаблон стылю.
Потым — выклік API.
Потым — прылад для тэставання.
Потым — хук, які спачатку быў хукам для выклікання рахункоў, але пазней быў перайменаваны.
Этая функцыя ўжо не ёсць частью продукту, але яе заўшчэдкі ўсё ж распаўзліся па всім кодавым базе.
Самэ так з часам накапліваецца «мертвы» код.
Арганізацыя па функцыях робіць межы значна болей зрозумелымі:
src/
features/
billing/
components/
BillingTable.tsx
hooks/
useBillingData.ts
services/
billingApi.ts
types.ts
index.ts
Тепер выклікання рахункоў мае адну чыстую тэриторію.
Якщо компанія выключае гэтую функцыю, першы крок є простым:
src/features/billing/
Удаліць папку.
Тады TypeScript пакажае всё іншае, што ўсё яшчэ залежыць ад нее.
Это набліжэнне да залежнасцяў є значна лепшым для керавання.
Магчымасць удалення — гэта адна з форм падтрымкі коду
Модуль стае прыемней для адміністрацыі, калі можна адразу з’ясаваць, дзе знаходзяцца його абавязкі.
Гэта ў частыні прычыны, чаму структуры папак, заснованыя на функціях, часта выклікаюцься пад час размов пра масштабаванне великіх кодавых базаў на React. Команды, якія працуют над значнымі заўважэннямі, часта прымаюць рашэнне пра спольную размешчання саме таму, што гэта зменшае вплыв будзь-яых змян.
Мета не ў тым, каб прагнуць ідеальна арганізаванага дрэва папак.
Мета — магчымасць быстро адпаведзець на такой запит:
„Якщо гэтае заўважэнне зникне завтра, што мне патрэбна будзе выдаліць?“
Якщо на гэты запыт важка адпаведзець, значыць, межы вашых заўважэнняў, верагодна, занадта вялікія.
2. Не ператвараюце shared/ на каштанку для смету
Існуе ўзгодная пастка, якая часта з’являецца, калі команды пераходзяць на архітектуру, заснованую на функцыях.
Усё, што явнаўсіба не належыць да аднаго функцыяналу, падае ў shared/.
Чырвоны месцы пазней стае ў такім вачынку:
shared/
utils/
helpers/
common/
services/
components/
hooks/
types/
І ў тым моманті shared/ таямна становіць палову всей базы коду.
Это стварае сваю сабе проблему з взаімазв’язкамі.
Хорашыя правілы, якімі трэба керавацца:
Код павінен перайсці ў спяльную папку тады, калі калькі функцыяналу дзейсна выкарыстоўваюць адну й тую ж концэпцыю, а не тады, калі нельга ўхаць, куды яго падаць.
Універсальны компонент кантакту паслужыць гарная частка ў системе дизайну.
Кліент аутэнтыкацыі можа разумна знаходзіцца ў спяльным шаре інфраструктуры.
Памочны прыбор для форматавання дат таксама можа быць спяльным.
Але функцыя на кшталт:
calculateEnterpriseRenewalDiscount()
практычна напэўна належыць таму функцыяналу, які выкарыстоўвае тое конкрэтнае правіло бізнесу.
Стараюцца не падаваць у спрагу перакладваць элементы ў глобальныя папкі лишэнне, каб дрэва каталога выглядалі апранейша.
Аднародны код не ўзлегкай доступнасці, адтолькі кожная функцыя, якая з яго выкарыстоўваецца, стае потэнцыяльным залежным элементам.
Чым больша становіцца папка shared/, тым сложней з’ясаваць, хто на самай працоўвае за адны конкрэтны функцыянам.
3. Ствараць захоўнія адаптэры для зовнішняйх залежнасцей
Існуе вялікая верыгоднасць, што ваша прыкладна програма будзе продавацца работаць дужа дыяўно пасля таго, як калькі з ёё залежнасцей зникнуць.
Сёння вы можете выкарыстоўваць Axios, а завтра перайсці на вбудованы fetch.
Сёння вы можете викорыстоўваць адного прадавца аналітыкі, а за калькі гадоў ваша компанія можа перайсці на іншага.
Сёння вы можете інтеграцыяваць бібліятэку аутентыкацыі, а пазней новыя вакуфы безпекі можу змусіць вас перайсці на іншую.
Шкодзябны спосаб стварэння элементаў — это імпорт гэтых зовнішняях пакетаў безпасцерожна ў дзесяткі компанентаў.
import axios from 'axios';
import { trackMixpanelEvent } from 'mixpanel-browser';
export function CheckoutCard() {
const handlePurchase = async () => {
await axios.post('/api/checkout', payload); trackMixpanelEvent('checkout_completed');
};
}
У гэты момент слой UI мае прымітнае розумеўне таго, які самэ HTTP-кліент і які постаўчык аналітыкі вы вжываеце. Якщо такі падход застосаваць да сорак пяці компанентаў, замена постаўчыка перастае быць аддзеленым заданнем — гэта стае зменай, якая распростраńваецца па всім репазітарыі.
Слой гранічных умоваў дапамагае зменяць залежнасці. Напрыклад:
// src/shared/lib/analytics.ts
import mixpanel from 'mixpanel-browser';export const analytics = {
trackCheckoutCompleted(
orderId: string,
amount: number
) {
mixpanel.track('checkout_completed', {
orderId,
amount,
});
},
};
Компанент тепер вярбуецца з канцэптам, які быў визначаны вашай сабея прыемленае:
analytics.trackCheckoutCompleted(orderId, amount);
У яго няма жаднае інформацыі пра тое, чыі інструмент знаходзіцца пад спадом — Mixpanel, PostHog, Segment чы іншы. Якщо змінюецца постаўчык, кантракт, на які рэгулюецца ваша прыемленае, можа застацца абсалютна тым самым.
Але не абстрагуйце все
Гэты момант такі ж важны, як і пярэдні.
Закаменаванне залежнасці ў адаптэры не є сама па сабе хорашым дизайнам. Якщо вы ствараеце спецыяльны інтерфейс для кожной маленькай бібліятэкі, яку викорыстоўваеце, вы можетэ запісаць больш коду, чым сама бібліятэкі мае.
Настоямы вопыт, які трэба задаць:
«Чы будзе дорага пазней заменіць гэтую залежнасць, чы рызыкаваць, якшо дазволіць ёй распространяцца без контролю па всім кодавым базе?»
Якшо адказ «так», тады інвестыцыя ў адаптэр вартая. Якшо ні, тады проста вызыванне залежнасці безпосередня, верагатываю, будзе простыя і падходячая альтернатыва.
Высокі ранг інжынера не значыць накладвання абстракцый на ўсё, чым вы касаецеся. Цэў значыць ставленне меж самэ там, дзе іх прыняццё неабходна, каб пазней не пацярпець наследків.
4. Адкрыты поток дадзеных кращы за магію
Адзін з найшырэйшых спосабоў стварыць плутанне ў базе коду — гэта замаскаваць тое, адкуль на самай працоўцы беруцца значэнні.
Глобальныя эмітары запускаў змаганняў ёсць класычны прыклад.
eventBus.emit('USER_UPDATED', {
id: user.id,
});
Этае рэчыце паведамляе пра тое, што запускаў змаганняў выкліканы. У яй няма жадных паведамленняў пра тое, хто яго слухае.
Можна быць шукаць у всій базе коду і ў канечнай лінію знайсці нешта на кшталт:
eventBus.on('USER_UPDATED', handler);
Але можа існаваць трохі разныя прыемнікі. Адзін з іх могаў быць даданы ўчора. Іншы можа мутаваць глобальны стан. Трэці можа выкліквати запускаў змаганняў аналітыкі. Раптам рэальная дзеяньне, якае выклікае тая первыня функцыя, рассейваецца па всім прыкладненні, а не застаецца ў адным месцы.
Тепер паўпоручыце гэта з кантрактом, які чыста і ясна сказаны:
interface UserCardProps {
user: User;
onUserRoleChange: (
userId: string,
newRole: Role
) => Promise<void>;
}
Хутчэй, компанент адкрыта прызнае, якія дзеянні ён падтрымлівае, а родны элемент адкрыта прызнае, што выканаецца, калі гэта дзеянне запускаецца. Прыток дадзейнаў ёсць видны на старонцы.
Так, гэта болей деталізавана, чым запуск анонімнага з’явлена. Але гэта дадатковая деталізацыя ўсё ж корисна, адколі яна залечыць працёздатны шлях у кодзе.
Калі хтось, не вакуумны ў компанент, ачынае файл, я он мусіць можаць адпавесці на тры пытанні без неабходнасці шукаць у рэсте репазітарыя:
Звядзе гэтыя дадзейны?
Зазвычай гэта ўласті (props), параметры маршруту, хук або якаясь чытра вядомая шара доступу да дадзейнаў.
Што можа яго змяніць?
Видны вызов функцыі, мутацыя, дзеянне аб явны апдэйт стану.
Шта выканаецца, калі корыстнік адрабоўвае гэта дзеянне?
Це явный вызов функцыі, чыя рэалізацыю можна праследаваць.
Чым меньша частка працы захаваная, тым лёгчэ было розумець весь система.
5. Зберагаеце логіку бізнесу пазнаходзячыся за межамі жыцёвых циклаў фрэймворка
Фрэймворкі не ўсёвечныя элементы — гэта адна з найболей надзеяных прыпускаў, якія можна робіць пад час працы над фронтэндам.
Лячна React сама пачала перабудовы: класовыя компоненты сталі менш популярнымі, хукі перарадзілі спосаб структуравання логіки з станам, а Create React App у багатох проектах быў заменены на такія інструменты, як Vite чыць натыўныя рэштаванні фрэймворка. Рэндарынг са сервера і новейшыя падходы да маршрутацыі зменілі спосаб, яким команды падходзяць да запрашання данных і вызначэння межаў прыкладнення.
Фрэймворк, які вы зараз викорыстоўваеце, можа змаўчыць абсалютна не сэрца таго, што будзе стандартам за пяць гадоў. У тым часе вашы бізнес-правілы павінны продаўжваць працаваць незалежна.
Возьмімо расчытак податкаў як прыклад. Нежысткая версія ховае асалодную логіку ўнутрь React hook:
export function useTaxCalculator(
cartItems: CartItem[]
) {
const [tax, setTax] = useState(0);
useEffect(() => {
let calculated = 0; // 60 lines of tax calculation,
// rounding rules,
// country logic,
// exemptions... setTax(calculated);
}, [cartItems]); return tax;
}
Такім чынам ваш расчытак податкаў стае прычастным да React. Як толькі трэба яго працаваць, патрэбна сетапаваная среда React. Выклік з боку сервернай дзеяння стае незграбным, а ўведэнне яго ў Web Worker таксама. Перанесенне на іншы фрэймворк UI стае дужа складным завадам.
Кращы падход — аддзеліць гэтыя два аспекты:
export function calculateTax(
cartItems: CartItem[],
countryCode: string
): number {
// Pure business logic
return totalTax;
}
Тады шар React проста выклікае яго:
const tax = calculateTax(cartItems, countryCode);
За дапамою такой структуры логіка, яка насправды мае значэнне, не мае жадных ведамаў пра React. Яна можа выконваліцца ў будзь-ям сераўе. Яе можна тэставаць за дапамою звычных тэстаў на елементы. Процес сервера можа выкарыстоўваць яе безпосередна. Кроме таго, яна застаёцца працэснаю пасля міграцыі на іншую рамку карэспантнага інтерфейсу без патрэбы ў перапісве.
Рамкі карэспантнага інтерфейсу патрабуюцься толькі на зовнішняй ступені
Прыемны спосаб разумець гэта — за дапамою схемы з шарамі:
┌──────────────────────────────┐
│ UI Layer │
│ React / Next.js │
├──────────────────────────────┤
│ Application Logic │
├──────────────────────────────┤
│ Domain Logic │
│ Pure TypeScript │
├──────────────────────────────┤
│ Infrastructure │
│ APIs / DB / Vendors / SDKs │
└──────────────────────────────┘
Чым ближэй фрагмент коду знаходзіцца да центру, тым менш я он должен залежаць ад конкрэтной рамкі карэспантнага інтерфейсу чы бібліятэкі прадавца.
Гэта не значыць, што кожны проект на React выклікае патрэбу ў повнай наладзе „Чыстай архітэктуры“.
Гэта значыць, што трэба чыткая адчувальнасць да таго, якія часткі вашага коду є справжна спецыфічныя для React, а якія часткі выражаюць вашы фактычныя бізнес-правіла.
Это два разныя катагорыі, і самэ тады, калі яны спрыяваюцца як адна, пачынаюцься проблемы.
6. Што робіць, каб не ператварыць хукі на міні-застосункі
Этот патэрн часта з’яўляецца ў кодавых базах React.
Зазвычай усё пачынаецца невінавата:
function useUser() {
// fetch user
}
Потым, з часам, додаецца ўсё больш вакалентаў.
function useUser() {
// fetch user
// loading state // error handling // permissions // analytics // transformations // caching // retry logic // business rules // notifications // feature flags
}
Чырз канічнае часа тое, што спачатку было простым хукам, таямніча ператварылася на застосунак з 500 рэядоў, які хаваецца за іменем функцыі з невінаватым выглядам.
Хукі дзейсна ўжытковыя.
Але хук не павінен стаць месца для кожной проблемы лиш таму, што ён мае зручны доступ да стану та эфектаў React.
Кращы падход — калі хук делегуе завданні меншым, спрямованным часткам:
function useUser() {
const user = useUserQuery();
const permissions =
calculatePermissions(user.data); return {
user: user.data,
permissions,
isLoading: user.isLoading,
};
}
З такой структурой хук стае шарам кіравання, які з’ѐднае всё разам.
Цэльва архітектура больш не запакована ў адну функцыю.
Этыя межы значна лёгкія для падтрымкі з часам.
7. Запісвайце дакументы архітектурных рашэнняў, а не бесканцовыя Вікі
Аднам з найпашыльнейшых выкарыстоўванняя архітектуры ёсць зовсім не хаотычны код.
Это згублена інфармацыя.
Такое зазвычай выглядае так: разработчык прымеў неочвічлёвае рашэння. Рашэнне ўсуненне, і всі члены команды на той момент разумеюць прычыны яго. Потым гэты чалавек пераходзіць да іншых задач.
Чырвоны месцы, новы інжынер знаходзіць гэтае нестандартнае рашэнне і думае:
"Чаму мы гэта робим так? Павінна існаваць болей чыстая альтэрнатыва."
Тады ён перапісвае яго, неведаючы зноў стварыўшы тую ж самую проблему, якой прагледзелася спачаткуе рашэнне.
Як прыклад можна взяць панель керування, створаную на аднойчыных паданнях сервера заместо веб-сокетаў.
Без пазарунку історыі, новы разрабоўчы можа справядліва выйсці да заключэння:
"WebSockets — гэта сучаснейшы стандарт. Давайце перайдзем на яго."
Але первічная команда могла выбраць SSE самэўсёлкава, таму што багато корпаратыўных кліянтаў выкарыстоваюць строгі корпоратыўныя проксі, якія неправяма обробляюць з’ўязкі WebSockets.
Гэтыя прычыны застаюцца невідомымі, якщо адзірвацься толькі на самы код.
Самэ гэтае працэнне і є тым, што павінны заполніць Архітектурныя записы прынятых рашэнняў.
Напрыклад:
# ADR 003: Use Server-Sent Events for Dashboard Feeds
## ContextOur dashboard requires real-time metric updates.We evaluated WebSockets and Server-Sent Events.## DecisionWe chose Server-Sent Events because:1. Communication is strictly server-to-client.
2. SSE uses standard HTTP infrastructure.
3. Browser reconnection is supported natively.
4. The solution works reliably within our enterprise network environment.## ConsequencesIf we later require client-to-server
bi-directional streaming, we should
re-evaluate this decision.
Калі такі запис ўжо існуе, наступныі інжынер не павінен занова аналізаваць прычыны з нуля.
Яні можа адразу пабачыць прычыну.
Проста
/docs/adr/
Фольдар у вашай базе дадзеных можа зберагчы гады інстытуцыйяльных ведаў, якія інакш пайшлі бы разам з тым, хто пакінуў команду.
Документавацыя рашэнняў, а не всьго
Вам не трэба падтрымваць вялікі, сотні сторанак вікі.
У большасці случаў сам код павінен быць дастаткова ясным, каб паспяшыць што ён робіць.
Дакладна тыя аспекты, якія трэба задокументаваць, — это разумовыя праграмы, якія код не можа выразіць сам:
- чаму была выбрана адпаведная тэхналогія
- чаму была адкладзена болей зрозумелая альтэрнатыва
- чаму вообща існуюць нестандартныя абмежэння
- чаму насправды ўсё ж такі трэба выкарыстоўваць спосаб рашэння, які здаецца непатрэбным
Документавацыя становіць сэнс тады, калі ёй удаёца зафіксаваць контекст, які інакш зникнуў бы разам з людзьмі, якія яго мелі.
8. Дзяржаўны дизайн для інжынера, які прыходзіць пасля вас
Гэта можа быць простейшы критэрый для архітектуры, якая мае працаваць дзяржаўна.
Уявіце сцэнарый, калі, пачынаючы з завтрашньага дня, усі, хто зараз разумее внутршню працэздатнась системы, адразу ж пакінуць компанію.
Чы сможа новая команда продаваць яе працэю?
Якщо ваша чыстая адказвае «нет», гэта не павінна прымусова значыць, што у вас не хапае разработчыкаў. Гэта значыць, што система мае скрытую залежнасьць ад знанняў конкрэтных людзей.
Система, падбіраная для дзяржаўнай працы, должна сама даваць можлівасьць адкрыць яе важлівыя характэрыстыкі.
Новы чалавек должен магчымаць адкрыць репазітарый і паступова знаходзіць адказы на такія запитанні:
- Дзе ў кодавой базе знаходзіцца гэта конкрэтная функцыя?
- Кальки модуль адпаведае за гэтыя характэрыстыкі?
- Звідку на самай працэ ўзялыся гэтыя данні?
Самэй гэтага ясныя межы такі важлівыя.
Новак не павінен выучваць всю історыю компаніі, каб толькі зрозумець, што робіць код.
Сама база коду павінна несці з сабой достатню частку гэтай історыі.
9. Зменшыць радыус наследкавання змян
Хорашы спосаб ацэнкі архітэктуры — падсчытаць, сколькі файлоў змушана змяніць простая змена.
Уявіце сабе просты запит на функцыю: хтось прасіць аб кнопцы, якая дазволіў бы корыстувачам экспортуваць звіт пра оплаты у формате CSV.
У суцэльна з’еднанай системе, каб задовольніць гэты запит, можа знадобіцца рэдагаваць файлы, распакованыя па:
components/
hooks/
services/
utils/
types/
global state/
shared helpers/
Інжынер можа змушаны быць працаваць з дзесяткамі файлоў, каб толькі запусцыць адну кнопку.
Пораўняйце гэта з правільна структураваным функцыяналам:
features/
billing/
components/
hooks/
services/
utils/
У такім случае тая ж змяна можа застацца практычна выключна ў сабеўскай папцы функцыяналу вырахоўвання рахункаў.
Самэ гэта маюць на увазе людзі, калі говоряць пра зменшэнне радыуса удару змены.
Маленькі радыус удару дае вам:
- Менш регрэсій
- Простыя перагляды коду
- Шырэйшая выдача результатаў
- Менш канфліктав падчас з’еднання коду
- Простыя тэсты
- Безпечнейшыя процесы рефакторингу
Для досягнення гэтага не трэба складной архітектуры. Патрабуюцца межы, якія насправдзе відпаваюць таму, як продукт розвиваецца на практыцы.
10. Паставіць канец оптымізаванню па схеме архітектуры
Чароўная схема архітектуры все ўсё можа скрыць кодавую базу, з якою вельмі клопатна працаваць.
Вы можете пазырzyć кожны з гэтых пунктав:
- следаванне прынцыпам чыстай архітэктуры
- застосоўванне прынцыпаў дизайна SOLID
- правільная інверсія залежнасцей
- абрабатка даступу да дадзэнняў па шаблонам репазітарыяў
- створэнне об’ектаў па шаблонам фабрык
- з’яеднанне элементаў за дапамогою запуска ўпадакоў
- шаруванне кальколька шароў абстракцыі ўзаемна
і тады простая функцыя можа ператварыцца на мнагадзённую працу.
Архітэктура павінна скасаваць складнасць, а не яе адбіваць. Якщо ваш шар архітэктуры включае больш паняў, чым сам продукт, значы ў чымсьці ўсё пашло не так.
Часта наймоцнейшая архітэктура — гэта тая, пра яю ніхто не стараецца гаворыць, таму што інжынеры можаць проста прачытаць код і яго выконваць. У практыцы гэта можа выглядаць так:
features/
billing/
checkout/
accounts/
разам з простым:
shared/
ui/
lib/
Плюс колька функцыяў, якія займаюцца чыстаю бізнес-логікай.
Нічага з гэтага не здаецца выключна вражаючым. Але якщо пасля чатырох гадоў ён застаецца працэспрывным і мае сэнс, значы ён выпаловае самэ тое, каму павінна служыць архітектура.
Чаго на самай працы оптымізуюць старшыя інжынеры
Старшыя інжынеры не обавязкова ствараюць болей сложны код. Разлік заключаецца ў наборы пытанняў, якія яны ставяць пры його напісанні.
Менш апытны інжынер можа запытацца:
"Як мне зробіць гэта відновлюваным?"
Старшы інжынер у замен запытаецца:
"Чакаецца ўзагалі, каб гэта было відновлюваным?"
Менш апытны інжынер можа запытацца:
"Як мне абстрагаваць гэта?"
Старшы інжынер у замен запытаецца:
«Какую конкрэтную проблему павінна рашыць гэта абстракцыя?»
Менее апэктыўны інжынер можа запытацца:
«Дзе павінна знаходзіцца гэта функцыя-вспамога?»
А вышэйшы інжынер у замене запытаецца:
«Калі насправды ёсць власнік гэтага аспекту працы?»
Менее апэктыўны інжынер можа запытацца:
«Як нам падготавіцца да будучых трэбаванняў?»
А вышэйшы інжынер у замене запытаецца:
«Якая з будучых змян можа стаць прычыной для дадзення такой складнасі ўжо зараз?»
І, можа, найболей значны вопыт:
«Як будзе выглядаць гэты код, калі чалавек, які яго напісаў, пайдzie далей?»
Што стосуецца гэтага пытання, то самэўсёлік тут і пачынаецца дзейство далекага прыгляду ў інжынерыі.
Узагальненне: Надтрымківы код часта выглядае незначным
Код, які продовжае працаваць эфектыва пасля гадоў змян, рэдка калі ёсць кодам, створаным на найновейшай платформе, з самым хітрым шаблонам дизайна чыя з найэлегантнейшымі абстракцыямі. Це зазвычай проста код з чысткімі межамі і прагматычнымі, неяскравымі рашэннямі — такі, калі іншы інжынер можа ачысці репазітарый і зрозумець, што там вядзецца, не патрабуючы знаходзіць таго, хто яго спачатку напісаў.
Основныя прынцыпы є простымі:
- Арганізаваць па функцыях і власніку. Функцыі должны быць лёгкія да знаходжэння, а калі настане час — лёгкія да выдалення.
- Важнейша ўмогацэйнае выдаленне, а не максымаўзаванне паўтарнага вжывання. Не кожны фрагмент логіки заслуговае на тое, каб стаць спяльной абстракцыяй.
Найвышая прамяга, якую можа атрымаць база коду, — гэта не:
"Гэтая архітектура ўражаюча хыткая."
Это:
"Я розумею гэта."
Таму што чырвоны годзінамі першыя інжынеры, верагатна, вядзець далей. Фрэймворк, верагатна, будзе іншы. Дизайн змяніцца. Продукт развіцца. Сама прадпрыемственая дзеяльнасць можа выглядаць зовсім не так, як сёння.
Але калі межы застаюцца чыстымі, логіка застаецца простай, а праблемы, якія лежачы за ключовымі рашэннямі, заносяцца куды-небудзь, код можа продовжваць развівацца разам з усім іншым навакола яго.
Гэта тое, як на самай працоўны програмны апработак выглядае.
Спадні матэрыялы
- Тры шаблоны TypeScript, якія павышаюць архітектуру React-прыемоў — Дазволяе дазнацца, як шаблоны Repository, Observer і Builder выкарыстоўваюць систему типаў TypeScript для стварэння чыстейшых і лягчэй керованых кодавых базаў для React і Next.js.
- Дзесяць частаўпавяючыхся прывычак у JavaScript, якія таямніча падрыхтавляюць адзін у адзін вашу кодавую базу — Апісвае дзесяць распашчытых проблем у JavaScript і TypeScript, ад слабкай роўнасці да мутацыі стану, і паказвае безпечнейшыя шаблоны для замены кожной з іх.