Monorepo va Turborepo: qachon kerak, qachon ortiqcha
Jamoada ikkinchi loyiha paydo bo'lganda birinchi texnik savol shu bo'ladi: kodni qayerga qo'yamiz? Alohida repo ochamizmi yoki hammasini bitta joyda saqlaymizmi? Men bu savolga ikkala tomondan ham javob berib ko'rganman: alohida repolarda versiya moslashdan charchaganman, monorepoda esa noto'g'ri sozlangan cache tufayli CI'ni soatlab kutganman. Bu maqolada monorepo qachon real foyda berishini, qachon shunchaki ortiqcha murakkablik ekanini va Turborepo bilan ishlashning amaliy asoslarini ko'rib chiqamiz.
Monorepo nima — va nima emas
Monorepo — bir nechta mustaqil ilova va paketning bitta git repositoriyada yashashi. E'tibor bering: mustaqil. apps/web va apps/api alohida build qilinadi, alohida deploy bo'ladi, alohida versiyaga ega bo'lishi mumkin. Faqat kod bir joyda turadi.
Buni monolit bilan adashtirish keng tarqalgan xato. Monolit — arxitektura haqidagi qaror: butun tizim bitta ilova sifatida ishlaydi va deploy qilinadi. Monorepo esa kodni tashkil qilish haqidagi qaror. Monorepoda beshta mikroservis saqlashingiz mumkin, polyrepoda esa monolit yozishingiz mumkin — bu ikki o'q bir-biriga bog'liq emas.
Qachon foyda beradi
Uch yildan beri ish loyihalarida monorepo ishlataman va foyda uch joyda aniq seziladi.
- Umumiy tiplar va util paketlar. Frontend va backend bitta
typespaketidan foydalanadi. API kontrakt o'zgarsa, TypeScript ikkala tomonda ham darhol xato ko'rsatadi — production'da emas, kompilyatsiya paytida. - Atomar o'zgarishlar. Yangi endpoint va uni ishlatadigan UI bitta PR'da keladi. Ikki repo bilan bu ikkita PR, ikkita review va "avval backend merge bo'lsin, keyin frontend" degan raqsga aylanadi.
- Bitta CI va bitta standart. Lint qoidalari, test konfiguratsiyasi, dependency versiyalari — hammasi bir joyda. "Bizning repo'da ESLint 8, ularnikida 9" degan muammo yo'qoladi.
Qachon ortiqcha
Monorepo — bepul emas. Tooling sozlash, cache'ni tushunish, paket chegaralarini himoya qilish — bularning hammasi vaqt oladi. Quyidagi holatlarda bu narx o'zini oqlamaydi:
- Bitta app bor. Umumiy kod bo'lmasa, bo'lishadigan narsa ham yo'q. Oddiy repo yetarli.
- Jamoa juda kichik. Bir-ikki kishi bitta loyihada ishlasa, monorepo yechadigan koordinatsiya muammosi hali mavjud emas.
- Paket chegaralari noaniq. Qaysi kod qaysi paketga tegishli ekanini bilmasangiz, monorepo bu chalkashlikni hal qilmaydi — uni rasmiylashtirib, ko'paytiradi. Avval domenni tushunib oling, keyin bo'ling.
Turborepo asoslari
Turborepo — monorepo ichidagi tasklarni (build, lint, test) to'g'ri tartibda va imkon qadar cache'dan ishga tushiradigan orkestrator. Butun konfiguratsiya turbo.json faylida yashaydi:
{
"$schema": "https://turbo.build/schema.json",
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", ".next/**", "!.next/cache/**"]
},
"lint": {},
"test": {
"dependsOn": ["build"],
"inputs": ["src/**", "test/**"]
},
"dev": {
"cache": false,
"persistent": true
}
}
}Eng muhim qator — "dependsOn": ["^build"]. ^ belgisi "avval mening dependency'larim build bo'lsin" degani: web paketi uiga bog'liq bo'lsa, turbo build avval uini, keyin webni build qiladi. test esa ^siz buildga bog'langan — bu "o'zimning build'im tugagach test qil" degani.
Lokal cache esa Turborepo'ning asosiy sehri. Har bir task uchun turbo hash hisoblaydi: paketning source fayllari (inputs), dependency'lar hash'i, ishlatiladigan env o'zgaruvchilar va task konfiguratsiyasi. Shu hash bo'yicha .turbo papkasida natija saqlanadi. Keyingi safar hash o'zgarmagan bo'lsa, turbo build'ni ishlatmaydi — tayyor outputs va terminal loglarini shunchaki qaytarib beradi. Terminalda FULL TURBO yozuvini ko'rsangiz, hamma narsa cache'dan kelgan bo'ladi.
Remote cache: jamoa uchun asosiy foyda
Lokal cache faqat sizning mashinangizda ishlaydi. Remote cache esa shu hash'larni umumiy serverda saqlaydi — Vercel'da yoki self-hosted variantda:
npx turbo login
npx turbo linkEndi hamkasbingiz ui paketini build qilgan bo'lsa, siz git pulldan keyin turbo build yozganingizda o'sha natijani serverdan olasiz — qayta build qilmaysiz. CI'da bu yanada seziladi: pull request'da faqat o'zgargan paketlar build bo'ladi, qolgani cache'dan keladi. Bizning loyihada o'rtacha CI vaqti taxminan ikki barobar qisqargan — ayniqsa faqat bitta kichik paketga tegadigan PR'larda farq keskin.
Amaliy tuzilma
Mana men ishlatadigan tipik tuzilma:
apps/
web/ # Next.js frontend
api/ # NestJS backend
packages/
ui/ # umumiy React komponentlar
config/ # eslint, tsconfig, prettier presetlari
types/ # API kontraktlari, zod sxemalarPaketlar workspace: protokoli orqali bog'lanadi:
{
"name": "@acme/web",
"dependencies": {
"@acme/ui": "workspace:*",
"@acme/types": "workspace:*"
}
}Tuzilmaning o'zi yetarli emas — bog'lanish qoidalari kerak. Menikilar oddiy:
apps/*paketlarga bog'lanadi, lekin hech bir paketapps/*ga bog'lanmaydi.types— eng pastki qatlam, u hech narsaga bog'lanmaydi.uifaqattypesga bog'lanishi mumkin.configkod export qilmaydi, faqat presetlar beradi.
Bu qoidalarni ESLint'ning import cheklovlari bilan mustahkamlab qo'ysangiz, chegaralar hujjatda emas, kodda yashaydi.
Uchraydigan tuzoqlar
- Circular dependency.
uipaketitypesdan,typesesa qandaydir sabab bilanuidan import qilsa, turbo dependency grafini qura olmaydi va build sinadi. Yechim — qat'iy bir yo'nalishli qatlamlar: pastki qatlam yuqoridagini hech qachon bilmaydi. - "Hamma narsa shared" kasalligi. Har bir yangi funksiya darhol umumiy paketga ko'chiriladi, natijada
sharedga har tegilganda yarim repo qayta build bo'ladi. Mening qoidam: kod kamida ikki joyda kerak bo'lmaguncha, u yashagan joyida qoladi. - Noto'g'ri `inputs`. Bu eng xavfli tuzoq, chunki jimgina ishlaydi. Agar build kod generatsiyasiga yoki env faylga bog'liq bo'lsa-yu, ular
inputsda ko'rsatilmagan bo'lsa — turbo eski cache'ni qaytaradi va siz eski kod bilan deploy qilasiz. Teskarisi ham bor:inputsjuda keng bo'lsa, README o'zgarishi ham cache'ni bekor qiladi. Env o'zgaruvchilarnienvmaydonida, umumiy fayllarniglobalDependenciesda aniq yozing.
Xulosa: qaror daraxti
Qisqa qilib, o'zim ishlatadigan qaror mantiqi:
- Bitta app, umumiy kod yo'q — oddiy repo, monorepo haqida o'ylamang.
- Ikki yoki undan ko'p app umumiy tiplar va komponentlardan foydalanadi — monorepo + Turborepo, bu eng kuchli holat.
- App'lar ko'p, lekin ular orasida umumiy kod deyarli yo'q va jamoalar mustaqil ishlaydi — polyrepo ham yomon tanlov emas.
- Monorepo tanladingizmi — birinchi kundan paket chegaralarini yozib qo'ying va ularni lint bilan himoyalang.
Monorepo — vosita, maqsad emas. U koordinatsiya muammosini hal qiladi; muammo hali yo'q bo'lsa, yechim ham shart emas.