وقتی یک Design System هم در Figma و هم در Code توسعه پیدا میکند، معمولاً تلاش میکنیم ساختار این دو تا جای ممکن به هم نزدیک باشد. اگر در Figma یک Button داریم، انتظار داریم در Code هم Component متناظر آن وجود داشته باشد. همین انتظار درباره Input، Checkbox، Modal و بسیاری از Componentهای پایه منطقی است، چون Designer و Developer با مفاهیم مشترکی کار میکنند و هرچه این مفاهیم قابل ردیابیتر باشند، فاصله میان Design و Implementation کمتر میشود.اما این هماهنگی میتواند یک فرض اشتباه هم ایجاد کند: هر چیزی که در Figma به Component تبدیل شده، باید در Code هم Component مستقلی داشته باشد.این فرض زمانی مسئلهساز میشود که فراموش کنیم Component شدن یک چیز در Figma و Code الزاماً با هدف یکسانی انجام نمیشود. گاهی در Figma چیزی را Component میکنیم فقط چون Designer بارها آن را میسازد و میخواهیم سرعت طراحی بیشتر شود، تغییرات راحتتر مدیریت شوند یا احتمال ایجاد Variationهای ناخواسته کاهش پیدا کند. اما اضافه کردن همان چیز به Code Library میتواند تصمیم بسیار بزرگتری باشد، چون از آن لحظه یک API عمومی ایجاد کردهایم که محصولات مختلف ممکن است به آن وابسته شوند.بنابراین سؤال اصلی این نیست که «آیا این Component در Figma وجود دارد؟»، بلکه باید بپرسیم «آیا پشت این Component یک Abstraction مشترک در سطح محصول وجود دارد که ارزش تبدیل شدن به یک Contract پایدار در Code را داشته باشد؟»
تکرار در Design همیشه به معنی نیاز به Abstraction در Code نیست
فرض کنید در انتهای بسیاری از Formها ترکیب مشابهی داریم: یک Button اصلی برای Submit، یک Button ثانویه برای Cancel و فاصله مشخصی میان آنها. چون Designerها دائماً این ترکیب را استفاده میکنند، در Figma یک Component به نام Form Actions میسازیم.این تصمیم در Design Library کاملاً قابل دفاع است. Designer دیگر مجبور نیست هر بار Buttonها را کنار هم قرار دهد، Spacing را تنظیم کند و ساختار را دوباره بسازد.اما آیا وجود Form Actions در Figma به این معنی است که تیم Front-end هم باید <FormActions> ایجاد کند؟برای پاسخ باید ببینیم چه چیزی واقعاً در این ساختار مشترک است. اگر تنها اشتراک چند Button و یک Layout ساده باشد، شاید Composition کردن Componentهای موجود در Code کافی باشد. در این حالت ساخت Component جدید نهتنها مسئله مهمی را حل نمیکند، بلکه API دیگری به سیستم اضافه میکند که از این به بعد باید نگهداری شود.مسئله زمانی روشنتر میشود که محصولات جدید وارد شوند. یک محصول ممکن است سه Action داشته باشد، محصول دیگر فقط یک Action داشته باشد، در Mobile ممکن است Buttonها زیر هم قرار بگیرند و در یک Workflow دیگر Actions باید Sticky باشند. اگر تلاش کنیم تمام این تفاوتها را داخل <FormActions> نگه داریم، بهمرور Propها و Exceptionهای بیشتری به Component اضافه میشوند.در این نقطه چیزی که در Figma برای کاهش کار تکراری Designer ساخته شده بود، در Code تبدیل به تعهدی برای Design System شده است.بنابراین Reuse بهتنهایی دلیل کافی برای Componentization نیست. تکرار میتواند نشانهای باشد که ارزش بررسی دارد، اما هنوز باید بفهمیم دقیقاً چه چیزی در حال تکرار است: ظاهر، Layout، Behavior یا یک تصمیم مشترک محصول؟
شاید لازم باشد بین Authoring Reuse و Product Abstraction تفاوت بگذاریم
یکی از راههای مفید برای نگاه کردن به این مسئله، جدا کردن دو نوع نیاز است.بعضی Componentها برای Authoring Reuse ارزشمند هستند. یعنی وجودشان کمک میکند Designer سریعتر طراحی کند، انتخابهای محدودتر و واضحتری داشته باشد و مجبور نباشد ساختارهای پرتکرار را دوباره بسازد.در مقابل، بعضی Componentها یک Product Abstraction را نمایندگی میکنند. اینجا دیگر فقط ظاهر تکرارشونده نداریم؛ چند محصول واقعاً به Behavior، State، Accessibility Requirement یا Contract مشترکی وابستهاند.برای مثال، یک Page Header ممکن است در Figma Component بسیار مفیدی باشد، چون Designer تقریباً در تمام صفحات به نقطه شروع مشابهی نیاز دارد. اما اگر Header در محصولات مختلف Content Model، Responsive Behavior و Actionهای متفاوتی داشته باشد، وجود Component در Figma الزاماً دلیل کافی برای ساخت <PageHeader> عمومی در Code نیست.در جهت عکس هم همین اتفاق میتواند بیفتد. ممکن است تیم Engineering یک Primitive برای Focus Management یا Positioning داشته باشد که چند Component مختلف به آن وابستهاند. این Abstraction از نظر Code بسیار ارزشمند است، اما Designer هیچ تصمیم مستقیمی درباره آن نمیگیرد و دلیلی ندارد چنین چیزی در Assets Panel فیگما ظاهر شود.بنابراین شاید بهتر باشد بهجای تلاش برای یکسان کردن تعداد Componentها، دو سؤال مستقل بپرسیم: آیا Designer نیاز دارد این Abstraction را مستقیماً استفاده کند؟ و آیا محصولات باید به یک Implementation یا Behavior مشترک از آن وابسته باشند؟پاسخ این دو سؤال همیشه یکسان نیست.
هزینه Componentization در Figma و Code یکسان نیست
این تفاوت زمانی مهمتر میشود که هزینه تصمیم را بررسی کنیم.اگر Componentی در Figma ایجاد کنیم و بعداً متوجه شویم Abstraction مناسبی نبوده، اصلاح آن قطعاً هزینه دارد؛ ممکن است Instanceهای زیادی داشته باشیم یا مجبور شویم بخشی از Library را تغییر دهیم. اما وقتی Componentی وارد Code Library عمومی میشود، نوع تعهد متفاوت است.محصولات میتوانند به API آن وابسته شوند، تغییر API ممکن است Breaking Change ایجاد کند، نسخههای قبلی شاید برای مدتی نیاز به Support داشته باشند و حذف Component هم احتمالاً به Migration نیاز خواهد داشت. علاوه بر این، Documentation، Testing و Accessibility آن هم باید همراه با تغییرات آینده سیستم نگهداری شوند.به همین دلیل Threshold برای Component شدن در Code لزوماً نباید با Threshold ساخت Component در Figma برابر باشد.Design Library میتواند Componentهایی داشته باشد که صرفاً برای راحتتر کردن Authoring ایجاد شدهاند، اما Code Library برای ایجاد یک API عمومی میتواند Evidence بیشتری بخواهد. تفاوت تعداد Componentهای این دو Library در چنین شرایطی الزاماً نشانه عقب بودن یکی از آنها نیست؛ ممکن است نتیجه طبیعی تفاوت هزینه Commitment باشد.
سؤال مهمتر این است که چه چیزی را میخواهیم مشترک نگه داریم
فرض کنید یک ساختار در ۲۰ صفحه تکرار شده است. در نگاه اول شاید این تکرار دلیل خوبی برای Component کردن آن به نظر برسد، اما وقتی Contextها را بررسی میکنیم متوجه میشویم که رفتار این ساختار در هر محصول متفاوت است.اینجا سؤال مفیدی وجود دارد:اگر فردا این Component تغییر کند، آیا انتظار داریم تمام Consumerها همان تغییر را دریافت کنند؟اگر پاسخ مثبت باشد، احتمالاً با یک Abstraction واقعی روبهرو هستیم. مثلاً تغییر Accessibility Behavior یک Checkbox منطقی است که به تمام Consumerهای آن منتقل شود، چون بخشی از Contract خود Component است.اما اگر هر محصول باید بتواند ساختار را مستقل از دیگری تغییر دهد، احتمالاً اشتراک موجود سطحیتر است. ممکن است چند محصول در حال حاضر Layout مشابهی داشته باشند، اما این شباهت الزاماً به معنی وجود یک تصمیم System-level نیست.این تفاوت اهمیت زیادی دارد، چون Componentization فقط درباره Reuse نیست؛ درباره ایجاد Coupling هم هست.وقتی چند محصول از یک Component مشترک استفاده میکنند، درواقع تصمیم گرفتهایم بخشی از آینده آنها را به هم متصل کنیم. تغییر Component میتواند همه Consumerها را تحت تأثیر قرار دهد. بنابراین قبل از ساخت Abstraction باید مطمئن شویم این Coupling همان چیزی است که واقعاً میخواهیم.
API طراحی هم نباید مستقیماً API کد را تعیین کند
این مسئله فقط در سطح Component نیست و در Properties هم خودش را نشان میدهد.فرض کنید Designer در Figma برای یک Component Propertyای به نام Show icon دارد. این Property برای تجربه طراحی بسیار مناسب است؛ Designer با یک Toggle مشخص میکند Icon نمایش داده شود یا نه.اما این الزاماً به معنی نیاز Code به Propای مثل showIcon=true نیست. ممکن است API مناسبتر این باشد که وجود icon خودش مشخص کند آیا Icon باید Render شود.اگر تمام Propertyهای Figma را مستقیماً به Props تبدیل کنیم، ممکن است Architecture کد را براساس Convenienceهای ابزار طراحی شکل دهیم. عکس این مسئله هم درست است؛ Developer ممکن است برای مدیریت Implementation به Propها یا Primitiveهایی نیاز داشته باشد که هیچ تصمیم مفیدی برای Designer ایجاد نمیکنند و انتقال آنها به Figma فقط Complexity فنی را وارد تجربه طراحی میکند.خود Figma نیز در Code Connect فرض نمیکند Design Properties و Code Props همیشه رابطه ۱:۱ دارند. این ابزار امکان Mapping میان Properties طراحی و API واقعی Code را فراهم میکند و حتی یک Design Component میتواند به Implementationهای مختلف متصل شود.بنابراین Design ↔ Code Alignment لزوماً به معنی Structural Equality نیست.
پس چه زمانی یک Figma Component باید در Code هم Component شود؟
برای تصمیمگیری میتوانیم بهجای نگاه کردن صرف به وجود Component در Figma، چهار عامل را بررسی کنیم.عامل اول Reuse است. باید بفهمیم چیزی که تکرار شده صرفاً Layout و ظاهر است یا یک مسئله مشترک واقعی در محصولات مختلف وجود دارد.عامل دوم Behavior است. اگر Component State، Interaction، Accessibility Requirement یا Logic مشترکی دارد، دلیل قویتری برای داشتن Abstraction مستقل در Code داریم.عامل سوم Coupling است. باید بپرسیم آیا واقعاً میخواهیم Consumerهای مختلف با تغییر این Component با یکدیگر تغییر کنند یا هر Product باید بتواند مسیر مستقل خودش را داشته باشد.عامل چهارم Commitment است. آیا Abstraction به اندازه کافی پایدار شده که بخواهیم API آن را برای Consumerهای مختلف Support کنیم و در آینده هزینه Versioning، Migration و Deprecation آن را بپذیریم؟این چهار عامل را میتوانیم بهعنوان یک مدل پیشنهادی ببینیم:Reuse × Behavior × Coupling × Commitmentاگر فقط Reuse بالا باشد، شاید یک Figma Component کافی باشد. اما هرچه Behavior مشترک، نیاز به Coupling و آمادگی برای Commitment بیشتر شود، دلیل قویتری برای تبدیل شدن آن به Component عمومی Code داریم.هدف نباید برابر کردن تعداد Componentها باشددر یک Design System بالغ ممکن است تعداد Componentهای Figma و Code دقیقاً برابر نباشد و این الزاماً مشکل نیست.ممکن است Design Library چند Component برای سادهتر کردن Authoring داشته باشد که هیچ API مستقلی در Code ندارند. از طرف دیگر، Codebase ممکن است Primitiveها و Abstractionهایی داشته باشد که برای مدیریت Behavior و Accessibility ضروریاند، اما Designer هیچوقت مستقیماً با آنها کار نمیکند.آنچه اهمیت دارد این نیست که بتوانیم بگوییم:«۱۰۰٪ Componentهای Figma معادل Code دارند.»سؤال مفیدتر این است:«آیا Componentهایی که یک Contract مشترک محصول را نمایندگی میکنند، بین Design و Code قابل ردیابی هستند؟»اگر پاسخ مثبت باشد، دو Library میتوانند Structure متفاوتی داشته باشند و همچنان بخشی از یک Design System واحد باشند.
جمعبندی
هر چیزی که در Figma Component شده، الزاماً نباید در Code هم Component مستقلی داشته باشد. گاهی Component فقط برای کاهش کار تکراری Designer ساخته شده و گاهی واقعاً Behavior و Contract مشترکی را نمایندگی میکند که باید در محصولات مختلف حفظ شود.اشتباه از جایی شروع میشود که Componentization را یک دستور Sync ببینیم، نه یک تصمیم معماری.قبل از اینکه یک Figma Component را به Code Library منتقل کنیم، بهتر است بررسی کنیم چه چیزی واقعاً تکرار شده، آیا Behavior مشترکی وجود دارد، آیا میخواهیم Consumerهای آینده به یک تصمیم مشترک وابسته باشند و آیا ارزش این Abstraction به اندازهای هست که هزینه نگهداری API آن را بپذیریم.در نهایت شاید مهمترین سؤال این نباشد که:«چرا این Component در Figma هست ولی در Code نیست؟»بلکه این باشد:«اگر این Component وارد Code شود، دقیقاً چه تصمیمی را میخواهیم برای تمام Consumerهای آینده مشترک کنیم؟»اگر پاسخ روشنی برای این سؤال نداریم، احتمالاً هنوز Abstraction مناسبی برای Code پیدا نکردهایم؛ فقط چیزی پیدا کردهایم که Reuse کردنش در Figma مفید است.
کامنتها
…