بازگشت به مهندسی مدرن و کار با هوش مصنوعی · فصل ۵ — Shell، Workspace، Lockfile و Build Closure · 22
Monorepo و Workspace Architecture
میتواند «Monorepo و Workspace Architecture» را در یک workspace محدود طوری پیکربندی کند که dependency/toolchain state قابل تکرار و قابل بررسی باشد.
خلاصهٔ درس
میتواند «Monorepo و Workspace Architecture» را در یک workspace محدود طوری پیکربندی کند که dependency/toolchain state قابل تکرار و قابل بررسی باشد.
خروجی این درس
میتواند «Monorepo و Workspace Architecture» را در یک workspace محدود طوری پیکربندی کند که dependency/toolchain state قابل تکرار و قابل بررسی باشد.
پیشنیازها
وقتی درخت پوشهها دربارهٔ معماری دروغ میگوید
یک repository را باز میکنی و چهار مسیر میبینی: apps/web، packages/ui، packages/config و experiments/demo. هر چهار مسیر شبیه package هستند و حتی experiments/demo یک manifest معتبر دارد. اگر فقط tree را ببینی، نتیجهٔ طبیعی این است که هر چهار عضو یک workspace هستند. اما workspace declaration فقط سه مسیر اول را عضو کرده و experiments/demo را عمداً بیرون نگه داشته است. همزمان یک config در سطحی قرار گرفته که با مالک واقعیاش همخوان نیست.
مسئلهٔ این درس انتخاب ابزار محبوب یا دفاع از monorepo نیست. خروجی تو باید Workspace Boundary Map v1 باشد: یک نقشهٔ قابل audit که چهار چیز را جدا میکند: مرز repository، membership صریح workspace، هویت package و dependency edgeهای declared، و ownership/scope پیکربندی. هر نتیجه باید به evidence قابل مشاهده متصل باشد و هر چیزی که evidence کافی ندارد UNKNOWN بماند.
قاعدهٔ اصلی: همپوشانی مکانی، عضویت معماری نیست. وجود یک folder یا manifest نشان میدهد یک node در repository وجود دارد؛ اما membership، dependency و config ownership هر کدام authority جدا دارند.
سه مسیر یادگیری، یک معیار evidence
در مسیر Foundation ابتدا واژهها را روشن میکنی: repository، workspace member، package identity، declared dependency و config owner. سپس از declarationها یک baseline map میسازی. در مسیر Repair فقط misconception خراب را جدا میکنی؛ مثلاً اگر membership را درست میخوانی ولی edgeها را از نزدیکی پوشهها حدس میزنی، repair روی manifest dependencyها متمرکز میشود.
در Experienced / Fast Path walkthrough کوتاه میشود، اما artifact، mutation، oracle و residual uncertainty حذف نمیشوند. تجربه با pnpm، Cargo یا ابزارهای دیگر مجوز این نیست که tree را authority فرض کنی.
قبل از اجرا سه چیز را بنویس: چه claimی میخواهی ثابت کنی، authority آن claim کدام فایل یا declaration است، و چه observation مستقلی میتواند حدس تو را رد کند.
مدل ذهنی: چهار گراف را یکی نکن
برای تحلیل workspace چهار لایه را جدا نگه دار:
- Repository graph: چه pathهایی زیر مرز نسخهگذاریشده وجود دارند. این لایه فقط containment را نشان میدهد.
- Workspace selection: کدام packageها طبق configuration عضو coordination boundary هستند و کدام pathها exclude یا خارج از scope هستند.
- Package/dependency graph: package identity و dependency edgeها از manifestها میآیند، نه از adjacency پوشهها. دو package کنار هم میتوانند هیچ dependency مستقیمی نداشته باشند.
- Configuration ownership graph: یک config ممکن است root-scoped، package-local یا tool-specific باشد. قرارگرفتن فایل در root بهتنهایی universal inheritance را ثابت نمیکند؛ scope را باید از قرارداد همان config/tool یا fixture خواند.
این چهار لایه میتوانند overlap داشته باشند اما معادل نیستند. workspace membership هم deployment topology را ثابت نمیکند و هم reproducible build را تضمین نمیکند. این درس فقط architecture map را میسازد؛ lockfile، cache و closure در درسهای بعدی بررسی میشوند.
Workspace Boundary Map v1 را از declaration بساز
برای هر node حداقل این ستونها را ثبت کن: repo_path، node_kind، declared_member، membership_evidence، package_identity، declared_dependencies، config_owner، scope، evidence_state و notes.
سه برچسب evidence را دقیق نگه دار:
OBSERVED: مستقیماً از فایل/خروجی fixture قابل مشاهده است؛INFERRED: نتیجهای است که از observationها استنتاج شده ولی خودش declaration مستقیم نیست؛UNKNOWN: evidence لازم موجود نیست یا contradiction حل نشده است.
ترتیب کار: ابتدا repository tree را inventory کن، بعد workspace declaration را بخوان، سپس package manifestها را جداگانه parse کن، بعد config ownership را ثبت کن. اگر از tree شروع کنی و در همان لحظه membership هم حدس بزنی، observation و inference با هم مخلوط میشوند.
چرا «same repo» و «same workspace» یک گزاره نیستند
در pnpm، مستندات رسمی فعلی workspace را با configuration ریشهای تعریف میکنند و workspace: را برای محدودکردن resolve به package محلی workspace توضیح میدهند. این یک implementation مشخص است، نه فرمت جهانی workspace. در Cargo نیز workspace مجموعهای از packageهای member است؛ members و exclude membership را شکل میدهند و بخشی از state مثل Cargo.lock و output directory در سطح workspace مشترک است. این مثال دوم نشان میدهد construct یکسان میتواند syntax و رفتار خاص خودش را داشته باشد.
پس معماری را از brand شروع نکن. سؤال معماری این است: «authority انتخاب member چیست؟» سپس سؤال package topology: «کدام manifest کدام dependency را declare کرده؟» و بعد سؤال ownership: «این config برای چه scopeای authority دارد؟»
وجود package.json یا Cargo.toml در یک directory فقط میگوید آن مسیر package-like است؛ membership باید از workspace authority استخراج شود. همینطور نام ui یا مجاورت web و ui dependency edge ایجاد نمیکند. edge وقتی دفاعپذیر است که manifest یا source authority آن را declare کند.
مثال کارشده: یک non-member معتبر و یک config اشتباهجا
در fixture پایه، workspace.json سه member را declare میکند: apps/web، packages/ui و packages/config. مسیر experiments/demo یک package.json معتبر دارد، اما در exclude است. بنابراین repository node بودن آن OBSERVED است، package-like بودن آن نیز OBSERVED است، ولی workspace member بودنش false است؛ نه بهخاطر نام پوشه، بلکه بهخاطر declaration.
در manifest apps/web dependency محلی روی @demo/ui declare شده و packages/ui dependency روی @demo/config دارد. این edgeها از manifest میآیند. اگر directoryها را جابهجا کنی ولی manifestها ثابت بمانند، topology declared هنوز همان است.
config دیگری با owner اعلامشدهٔ packages/ui داخل apps/web گذاشته شده است. این mismatch باید در map بهعنوان contradiction ثبت شود تا زمانی که ownership rule resolve شود. «فایل پیدا شد» مساوی «مالکیت درست است» نیست.
چهار میانبُر که map را خراب میکنند
«هر package-like folder عضو workspace است.» repair: member set را از declaration استخراج کن و non-member را صریح ثبت کن.
«درخت پوشه همان dependency graph است.» repair: edgeها را فقط از manifest declaration بساز و adjacency را صرفاً containment نگه دار.
«همهٔ memberها یک deployment unit هستند.» repair: deployment را OUT_OF_SCOPE علامت بزن؛ workspace coordination دربارهٔ source/tooling boundary است، نه الزاماً release topology.
«Monorepo یعنی build reproducible است.» repair: فقط Workspace Boundary Map را تحویل بده و stateهای lockfile/cache/build closure را به مراحل بعدی واگذار کن.
برای مهندس باتجربه: map را به repo واقعی وصل کن، بدون overclaim
در repo واقعی ممکن است چند workspace، generated package، nested tooling یا config inheritance پیچیده وجود داشته باشد. روش ثابت میماند: authority هر claim را پیدا کن، node/edge را با evidence label کن، contradiction را پنهان نکن و unknown را بهزور fill نکن.
اگر ابزار سازمانی package graph میسازد، خروجی آن را بهعنوان observation وارد کن، نه حقیقت مطلق. برای یک edge مهم به manifest یا source-of-truth قابل review برگرد. اگر config inheritance tool-specific است، claim را به همان tool/version محدود نگه دار.
هدف این درس ایجاد یک architecture evidence layer است که TOL-002 تا TOL-006 بتوانند روی آن lockfile، task graph، environment و closure را بررسی کنند.
Monorepo یک trade-off است، نه امتیاز کیفیت
یک monorepo میتواند تغییر cross-package، policy مشترک و visibility dependencyها را سادهتر کند. در مقابل، graph بزرگتر، coupling سازمانی، config scope پیچیدهتر و blast radius تغییرات میتواند افزایش یابد. هیچکدام بهتنهایی حکم «بهتر» یا «بدتر» نمیدهند.
multi-repo میتواند ownership و release boundary را جداتر کند، ولی coordination و cross-repo change را پرهزینهتر کند. انتخاب architecture خارج از scope این درس است؛ کاری که اینجا باید انجام دهی این است که topology موجود را بدون تبلیغ برای یک سبک، درست map کنی.
Lab: ابتدا baseline، سپس mutation
fixture را با setup_fixture.py بساز. network، credential یا privilege لازم نیست. inspect_repository.py فقط evidence خام از workspace declaration، manifestها و configها میدهد؛ نتیجهٔ نهایی را برایت تصمیم نمیگیرد. identity محدود runtime/fixture را ثبت کن و اگر build یا install اجرا نشده، build state را UNKNOWN_NOT_OBSERVED نگه دار.
در baseline، Workspace Boundary Map را پر کن و برای هر member/non-member evidence path بنویس. سپس سه mutation را جدا اجرا کن: یک member را اضافه/حذف کن، config را از root یا package به scope دیگری منتقل کن، و یک dependency edge را عوض کن. بعد از هر mutation باید فقط بخشهایی از map تغییر کنند که authority آنها تغییر کرده است.
در transfer fixture نام pathها و member patternها متفاوتاند. sequence آمادهای وجود ندارد. قبل از هر probe بنویس کدام field map قرار است تغییر کند و چه observationی نتیجه را falsify میکند.
حداقل evidence قابل دفاع
یک Workspace Boundary Map وقتی قابل review است که membership هر node به declaration trace شود، package identity و edgeها به manifest trace شوند، config ownership/scope evidence داشته باشد، non-memberها و unknownها حذف نشده باشند و mutation response قابل بازتولید باشد.
اگر declared state و observed output تناقض دارند، conclusion نده؛ contradiction را ثبت و probe کوچکتری طراحی کن. یک command سبز فقط همان command را ثابت میکند، نه کل architecture. تصمیم نهایی باید bounded باشد: چه چیزی ثابت شد، چه چیزی فقط inferred است و چه چیزی هنوز unknown مانده است.
Assessment: تشخیص، تصمیم، اجرا و انتقال
AS1 — Diagnosis: یک map را میبینی که experiments/demo را صرفاً بهخاطر manifest عضو workspace گرفته است. کوچکترین boundary خطا و evidence ردکننده را مشخص کن.
AS2 — Decision: repository tree و یک command موفق در اختیار داری، اما workspace declaration یا manifest dependencyها کامل نیستند. تصمیم ACCEPT / REJECT / DEFER بده و residual risk را بنویس.
AS3 — Application: baseline fixture را inspect کن، Workspace Boundary Map v1 را کامل کن و با حداقل دو mutation نشان بده map بر اساس authority تغییر میکند، نه بر اساس نام پوشه.
AS4 — Transfer: روی transfer fixture بدون sequence hint، member set، dependency edgeها و config ownership را بازسازی کن و مشخص کن کدام نتیجه هنوز اثبات نشده است.
ابعاد critical عبارتاند از boundary correctness، evidence traceability و mutation robustness. متن زیبا بدون این سه dimension pass کامل نیست.
مرز source و freshness
ادعاهای tool-specific این درس به مستندات رسمی جاری محدود شدهاند. مستندات pnpm که در 2026-09-08 بررسی شدند، workspace ریشهای و workspace: protocol را بهعنوان رفتار pnpm توضیح میدهند. مستندات Cargo که همان روز بررسی شدند، workspace memberها، members/exclude و state مشترک workspace مانند Cargo.lock و output directory را توضیح میدهند.
این درس syntax یک ابزار را به همهٔ ecosystemها تعمیم نمیدهد. اگر tool semantics تغییر کند یا review date برسد، claimهای current باید دوباره با source رسمی همان ابزار بررسی شوند. Workspace Boundary Map v1 یک artifact آموزشی برای evidence discipline است، نه استاندارد اجباری صنعت.
ارزیابی
پاسخنامهی این درس در بستهی آموزش وجود ندارد؛ خودت جواب بده و صادقانه علامت بزن. «نیاز به مرور» درس را به صف تکرار میفرستد.
AS1 diagnosis
A map labels experiments/demo as a workspace member because it contains package.json. Diagnose the smallest failing boundary and name evidence that would decide membership.
AS2 decision
You have a repository tree and a green command but incomplete workspace declarations. Decide ACCEPT, REJECT, or DEFER for the full boundary map and state residual risk.
AS3 application
Complete the baseline Workspace Boundary Map and run at least two targeted mutations. Explain exactly which map fields change and why.
AS4 transfer
Reconstruct the map on the transfer fixture without sequence hints. State what is observed, inferred, and still unproven.
ملاکهای ارزیابی (rubric)
Critical dimensions are non-compensatory.
- Boundary correctness — critical: membership is derived from workspace authority; deployment and closure are not silently claimed.
- Evidence traceability — critical: each material conclusion points to a declaration/manifest/config observation or is
UNKNOWN. - Mutation robustness — critical: the map changes only where the mutated authority changes.
- Transfer and residual risk: the method works on the changed fixture and remaining uncertainty is explicit.
A polished explanation does not compensate for failure on a critical dimension.
منابع و تازگی
مطالعهٔ موارد (case studies)
فایلهای آزمایشگاه
- با
python setup_fixture.py --case baselinefixture را بساز. - پوشهٔ
workspace/را بررسی وpython inspect_repository.pyرا اجرا کن. - خروجیهای
environment_identityوobservation_boundaryرا درsubmission_notes.mdثبت کن. هیچ environment variable یا credential را dump نکن. - فایل
workspace_boundary_map.csvرا پر کن. membership را از نام پوشه حدس نزن. برای هر نتیجهٔ مهم evidence فایل/command بده؛ reviewer نتیجه را با oracle مخفی مقایسه میکند. - قبل از هر mutation reset کن و هر بار فقط یک mutation را با
mutate_fixture.pyاجرا کن. - ثبت کن چه چیزی تغییر کرد، کدام authority تغییر کرد، چه چیزی هنوز unknown است و کدام observation مستقل میتواند نتیجهات را رد کند.
- برای transfer،
python setup_fixture.py --case transferرا اجرا و بدون sequence آماده map را بازسازی کن.
اگر build/install اجرا نشده است، observed build state را UNKNOWN_NOT_OBSERVED نگه دار؛ evidence معماری static را به ادعای reproducibility تبدیل نکن.
مرز ایمنی: fixture کاملاً local است؛ network، credential، privilege، dump گستردهٔ environment یا تغییر مخرب host لازم نیست.
رسانه و اسلایدهای درس
فایلهای آموزشی (دانلود)