یکی از اشتباه ترین تصور هایی که اوایل مسیر داشتم این بود که فکر میکردم هر مسئلهای باید یک راه حل درست داشته باشد.
یک جواب.
یک تصمیم صحیح.
یک انتخاب واضح.
اما هرچه بیشتر در پروژه های واقعی کار کردم، بیشتر فهمیدم که بسیاری از تصمیم های مهندسی این گونه نیستند.
مثلاً:
آیا باید Monolith بمانیم یا به سمت Microservice برویم؟
آیا باید Cache اضافه کنیم؟
آیا باید سیستم را Rewrite کنیم؟
آیا باید این Feature را Generic طراحی کنیم؟
جالب است که برای همه این سوال ها میتوان مثال هایی پیدا کرد که پاسخ «بله» درست باشد.
و مثال هایی که پاسخ «خیر» درست باشد.
همان جا بود که فهمیدم بخش بزرگی از مهندسی نرم افزار، پیدا کردن پاسخ درست نیست.فهمیدن سوال درست است.
چون خیلی وقت ها قبل از اینکه راه حل اشتباهی انتخاب کنیم،
در حال حل کردن مسئله اشتباهی هستیم.
ساعت ها روی Performance کار میکنیم، در حالی که گلوگاه اصلی جای دیگری است.
معماری را پیچیده میکنیم، در حالی که مشکل واقعی فرآیند های تیم است.
سیستم را Scale میکنیم، در حالی که مسئله از یک Query اشتباه شروع شده است.
شاید به همین دلیل است که مهندسان باتجربه، معمولاً زودتر از بقیه راه حل ارائه نمیدهند.
آنها مدت بیشتری روی فهمیدن مسئله وقت میگذارند.
چون میدانند انتخاب بهترین راه حل برای یک مسئله اشتباه،
هنوز هم یک اشتباه است.
یک جواب.
یک تصمیم صحیح.
یک انتخاب واضح.
اما هرچه بیشتر در پروژه های واقعی کار کردم، بیشتر فهمیدم که بسیاری از تصمیم های مهندسی این گونه نیستند.
مثلاً:
آیا باید Monolith بمانیم یا به سمت Microservice برویم؟
آیا باید Cache اضافه کنیم؟
آیا باید سیستم را Rewrite کنیم؟
آیا باید این Feature را Generic طراحی کنیم؟
جالب است که برای همه این سوال ها میتوان مثال هایی پیدا کرد که پاسخ «بله» درست باشد.
و مثال هایی که پاسخ «خیر» درست باشد.
همان جا بود که فهمیدم بخش بزرگی از مهندسی نرم افزار، پیدا کردن پاسخ درست نیست.فهمیدن سوال درست است.
چون خیلی وقت ها قبل از اینکه راه حل اشتباهی انتخاب کنیم،
در حال حل کردن مسئله اشتباهی هستیم.
ساعت ها روی Performance کار میکنیم، در حالی که گلوگاه اصلی جای دیگری است.
معماری را پیچیده میکنیم، در حالی که مشکل واقعی فرآیند های تیم است.
سیستم را Scale میکنیم، در حالی که مسئله از یک Query اشتباه شروع شده است.
شاید به همین دلیل است که مهندسان باتجربه، معمولاً زودتر از بقیه راه حل ارائه نمیدهند.
آنها مدت بیشتری روی فهمیدن مسئله وقت میگذارند.
چون میدانند انتخاب بهترین راه حل برای یک مسئله اشتباه،
هنوز هم یک اشتباه است.
یکی از اشتباهاتی که در بسیاری از پروژههای NET. دیده میشود، استفاده از "throw" برای خطاهای قابل انتظار (Expected Errors) است.
فرض کنید کاربر میخواهد سفارشی ثبت کند، اما موجودی کالا کافی نیست.
❌ این یک Exception نیست:
✅ این یک نتیجهی قابل انتظار از منطق کسبوکار است:
چرا؟
🔹 ساختن Exception شامل ایجاد Stack Trace است که هزینهی CPU و حافظه دارد.
🔹 پرتاب و Catch کردن Exception مسیر اجرای برنامه را کندتر میکند.
🔹 درواقع Exception برای اتفاقات غیرمنتظره طراحی شده است، نه اعتبارسنجی قوانین کسبوکار.
قاعدهای که همیشه از آن استفاده میکنیم:
• موجودی کافی نیست.
• کاربر پیدا نشد.
• اعتبارسنجی ناموفق بود.
• سفارش قبلاً پرداخت شده است.
• قطع شدن Database
• خطای شبکه
• باگ برنامه
• شرایطی که هرگز نباید رخ دهند
به این ترتیب:
✔️ کد خواناتر میشود.
✔️ جریان اجرای برنامه شفافتر است.
✔️ از هزینهی غیرضروری Exceptionها جلوگیری میشود.
✔️ و Performance در سناریوهای پرتکرار بهتر خواهد بود.
پس Result برای کنترل جریان عادی برنامه است و Exception برای شرایط استثنایی.
فرض کنید کاربر میخواهد سفارشی ثبت کند، اما موجودی کالا کافی نیست.
❌ این یک Exception نیست:
throw new OutOfStockException();
✅ این یک نتیجهی قابل انتظار از منطق کسبوکار است:
return Result.Failure(Errors.Product.OutOfStock);
چرا؟
🔹 ساختن Exception شامل ایجاد Stack Trace است که هزینهی CPU و حافظه دارد.
🔹 پرتاب و Catch کردن Exception مسیر اجرای برنامه را کندتر میکند.
🔹 درواقع Exception برای اتفاقات غیرمنتظره طراحی شده است، نه اعتبارسنجی قوانین کسبوکار.
قاعدهای که همیشه از آن استفاده میکنیم:
✅ Expected Error → "Result"
• موجودی کافی نیست.
• کاربر پیدا نشد.
• اعتبارسنجی ناموفق بود.
• سفارش قبلاً پرداخت شده است.
❌ Unexpected Error → "throw"
• قطع شدن Database
• خطای شبکه
• باگ برنامه
• شرایطی که هرگز نباید رخ دهند
به این ترتیب:
✔️ کد خواناتر میشود.
✔️ جریان اجرای برنامه شفافتر است.
✔️ از هزینهی غیرضروری Exceptionها جلوگیری میشود.
✔️ و Performance در سناریوهای پرتکرار بهتر خواهد بود.
پس Result برای کنترل جریان عادی برنامه است و Exception برای شرایط استثنایی.
نرمافزار هیچوقت «تمامشده» نیست. و دقیقاً به همین دلیل است که باید در مسیر، موفقیتهای کوچک را جشن گرفت.
همیشه یک نسخهی جدید وجود دارد. یک باگ دیگر. یک فیچر دیگر در بکلاگ. خط پایان فقط جابهجا نمیشود — اصلاً وجود ندارد. پس اگر تا امروز به خودت گفتهای «وقتی همهچیز آرام شد جشن میگیرم»، باید بپذیری که این آرامش احتمالاً هیچوقت نمیرسد.
من دیدهام افرادی که اجازه دادهاند یک یا دو سال از زندگی کاریشان در یک حالت کاریِ بیوقفه و فرسایشی محو شود و آن را «طبیعی» بنامند. خودم هم این را تجربه کردهام. یک روز نگاه میکنی و میبینی بخش بزرگی از مسیر شغلیات گذشته، بدون اینکه حتی لحظهای توقف کرده باشی تا احساس خوبی نسبت به آن داشته باشی.
جشن گرفتن موفقیتهای کوچک ضعف نیست. اینکه یک فیچر را منتشر کردهای، تیم را از یک گلوگاه خارج کردهای، یا یک تست ناپایدار را بالاخره حذف کردهای — اینها را باید ببینی و به رسمیت بشناسی. این بخشی از ماندن در این مسیر برای طولانیمدت است، بدون اینکه آرام و بیصدا فرسوده شوی.
کار قرار نیست متوقف شود تا به تو تبریک بگوید. پس این کار را باید خودت برای خودت بسازی.
همیشه یک نسخهی جدید وجود دارد. یک باگ دیگر. یک فیچر دیگر در بکلاگ. خط پایان فقط جابهجا نمیشود — اصلاً وجود ندارد. پس اگر تا امروز به خودت گفتهای «وقتی همهچیز آرام شد جشن میگیرم»، باید بپذیری که این آرامش احتمالاً هیچوقت نمیرسد.
من دیدهام افرادی که اجازه دادهاند یک یا دو سال از زندگی کاریشان در یک حالت کاریِ بیوقفه و فرسایشی محو شود و آن را «طبیعی» بنامند. خودم هم این را تجربه کردهام. یک روز نگاه میکنی و میبینی بخش بزرگی از مسیر شغلیات گذشته، بدون اینکه حتی لحظهای توقف کرده باشی تا احساس خوبی نسبت به آن داشته باشی.
جشن گرفتن موفقیتهای کوچک ضعف نیست. اینکه یک فیچر را منتشر کردهای، تیم را از یک گلوگاه خارج کردهای، یا یک تست ناپایدار را بالاخره حذف کردهای — اینها را باید ببینی و به رسمیت بشناسی. این بخشی از ماندن در این مسیر برای طولانیمدت است، بدون اینکه آرام و بیصدا فرسوده شوی.
کار قرار نیست متوقف شود تا به تو تبریک بگوید. پس این کار را باید خودت برای خودت بسازی.
🚫 چه زمانی نباید از Design Patternها استفاده کنیم؟
یکی از بزرگترین اشتباهات برنامهنویسها این است که فکر میکنند هر مسئلهای باید با یک Design Pattern حل شود.
در حالی که بسیاری از Patternها برای حل مشکلات پیچیده طراحی شدهاند، نه برای پیچیدهتر کردن کدهای ساده.
گاهی یک متد ساده یا یک
if دقیقاً همان چیزی است که نیاز دارید. 👇1️⃣ Adapter
❌ زیادهروی است وقتی:
فقط قرار است یک یا دو نوع داده را تبدیل (Map) کنید.
✅ بهجای آن:
یک Mapper ساده یا Helper Method بنویسید.
2️⃣ Decorator
❌ زیادهروی است وقتی:
فقط میخواهید یک Validation یا Log ساده اضافه کنید.
✅ بهجای آن:
از Pipeline، Middleware یا حتی منطق مستقیم استفاده کنید.
3️⃣ Facade
❌ زیادهروی است وقتی:
زیرسیستم شما API واضح و سادهای دارد.
✅ بهجای آن:
مستقیماً همان Service را فراخوانی کنید.
4️⃣ Abstract Factory
❌ زیادهروی است وقتی:
فقط یک یا دو نوع شیء میسازید.
✅ بهجای آن:
از Constructor یا Factory Method ساده استفاده کنید.
5️⃣ Strategy
❌ زیادهروی است وقتی:
رفتار فقط چند حالت ساده دارد.
✅ بهجای آن:
یک
if/else یا switch کاملاً کافی است.6️⃣ Builder
❌ زیادهروی است وقتی:
شیء فقط چند Property اختیاری دارد.
✅ بهجای آن:
از Object Initializer یا پارامترهای Optional استفاده کنید.
7️⃣ Factory Method
❌ زیادهروی است وقتی:
منطق ساخت شیء بسیار ساده است.
✅ بهجای آن:
از
new یا یک Helper استفاده کنید.8️⃣ Chain of Responsibility
❌ زیادهروی است وقتی:
جریان اجرای شما کوتاه و ثابت است.
✅ بهجای آن:
یک Pipeline ساده از متدها بسازید.
9️⃣ Template Method
❌ زیادهروی است وقتی:
فقط بخش کوچکی از الگوریتم تغییر میکند.
✅ بهجای آن:
از Delegate یا یک متد مشترک استفاده کنید.
🔟 Bridge
❌ زیادهروی است وقتی:
فقط یک بُعد تغییر در سیستم دارید.
✅ بهجای آن:
ءComposition یا یک Interface ساده کافی است.
1️⃣1️⃣ Command
❌ زیادهروی است وقتی:
عملیات ساده هستند و نیازی به Queue، Undo یا Retry ندارند.
✅ بهجای آن:
مستقیماً متد موردنظر را صدا بزنید.
1️⃣2️⃣ State
❌ زیادهروی است وقتی:
فقط چند State ساده دارید.
✅ بهجای آن:
یک
enum همراه با switch استفاده کنید.1️⃣3️⃣ Proxy
❌ زیادهروی است وقتی:
فقط یک Wrapper کوچک نیاز دارید.
✅ بهجای آن:
یک Helper یا Wrapper ساده بنویسید.
1️⃣4️⃣ Observer
❌ زیادهروی است وقتی:
فقط یک یا دو Receiver دارید.
✅ بهجای آن:
از Callback یا فراخوانی مستقیم متد استفاده کنید.
1️⃣5️⃣ Composite
❌ زیادهروی است وقتی:
قرار نیست با Itemها و Groupها رفتار یکسانی داشته باشید.
✅ بهجای آن:
منطق List و Item را جدا نگه دارید.
1️⃣6️⃣ Visitor
❌ زیادهروی است وقتی:
مدل دائماً تغییر میکند و تعداد عملیات کم است.
✅ بهجای آن:
ءPattern Matching یا
switch انتخاب بهتری است.1️⃣7️⃣ Prototype
❌ زیادهروی است وقتی:
کپی گرفتن از اشیاء ساده است.
✅ بهجای آن:
از Copy Constructor یا Mapper استفاده کنید.
1️⃣8️⃣ Flyweight
❌ زیادهروی است وقتی:
مصرف حافظه مشکل اصلی سیستم نیست.
✅ بهجای آن:
از Objectهای معمولی و Cache هدفمند استفاده کنید.
1️⃣9️⃣ Interpreter
❌ زیادهروی است وقتی:
قوانین سیستم کم و ثابت هستند.
✅ بهجای آن:
از Parser ساده یا Configuration Table استفاده کنید.
2️⃣0️⃣ Singleton
❌ زیادهروی است وقتی:
فقط یک سرویس مشترک میخواهید.
✅ بهجای آن:
آن را بهصورت Singleton در DI Container ثبت کنید، نه اینکه الگوی Singleton را پیادهسازی کنید.
2️⃣1️⃣ Mediator
❌ زیادهروی است وقتی:
فقط چند سرویس محدود با هم تعامل دارند.
✅ بهجای آن:
از فراخوانی مستقیم Service به Service استفاده کنید.
🎯 جمع بندی
ءDesign Patternها ابزار هستند، نه هدف.
بهترین معماری، معماریای نیست که بیشترین Pattern را داشته باشد؛ بلکه معماریای است که سادهترین راهحل ممکن را برای مسئلهی واقعی انتخاب کند.
همانطور که Martin Fowler میگوید:
"Any fool can write code that a computer can understand. Good programmers write code that humans can understand." 💡گاهی بهترین Pattern، استفاده نکردن از Pattern است. 😉
Forwarded from tech-afternoon (Amin Mesbahi)
گاهی سرویس Identity اول پیام AccessRequested رو دریافت میکنه، اما هنوز پیام EmployeeCreated رو پردازش نکرده. و خطا میده که کارمند رو پیدا نکرده. بهترین پاسخ از نظر شما کدومه؟
Final Results
15%
احتمالا مشکل از بروکر است، چون ترتیب پیامها را رعایت نشده.
13%
بهتره تا پیامها رو synchronous کنیم تا دیگه این مشکل پیش نیاد.
65%
وابستگی پنهان بین eventهاست؛ consumer نباید فرض کنه eventهای وابسته همیشه به ترتیب مورد انتظار میان.
3%
- باید Outbox رو حذف کنیم، چون باعث تاخیر در publish شده.
4%
کافیه تا یک sleep چند ثانیهای قبل از پردازش AccessRequested بگذاریم.
Please open Telegram to view this post
VIEW IN TELEGRAM
تا حالا دقت کردهای چرا بعضی کتابخانهها اینقدر آرام هستند؟
نه به خاطر اینکه کسی آنجا کتاب نمیخواند.
به خاطر اینکه هر کتاب، جای مشخصی دارد.
اگر دنبال کتابی باشی، لازم نیست از ده نفر سؤال بپرسی.
لازم نیست حدس بزنی کدام قفسه مناسب است.
همه چیز بر اساس یک نظم مشخص پیدا میشود.
حالا یک پروژه نرمافزاری را تصور کن که بعد از چند سال توسعه، هر کلاس میتواند هر چیزی را صدا بزند.
هر ماژول به هر ماژول دیگری وابسته است.
برای تغییر یک قابلیت ساده، باید پنج بخش مختلف را بررسی کنی.
پروژه هنوز کار میکند.
اما دیگر شبیه کتابخانه نیست.
بیشتر شبیه انباری است.
انباری که همه چیز داخل آن هست.
اما پیدا کردن هر چیزی، زمان میبرد.
جالب اینجاست که هیچ پروژهای از روز اول انباری نبوده است.
فقط هر بار یک وابستگی کوچک اضافه شده.
یک میانبر دیگر.
یک دسترسی مستقیم دیگر.
تا جایی که مرزها از بین رفتهاند.
در مهندسی نرمافزار،معماری فقط برای این نیست که سیستم امروز کار کند.
برای این است که شش ماه بعد،هنوز بدانی هر چیز دقیقاً باید کجا باشد.
نه به خاطر اینکه کسی آنجا کتاب نمیخواند.
به خاطر اینکه هر کتاب، جای مشخصی دارد.
اگر دنبال کتابی باشی، لازم نیست از ده نفر سؤال بپرسی.
لازم نیست حدس بزنی کدام قفسه مناسب است.
همه چیز بر اساس یک نظم مشخص پیدا میشود.
حالا یک پروژه نرمافزاری را تصور کن که بعد از چند سال توسعه، هر کلاس میتواند هر چیزی را صدا بزند.
هر ماژول به هر ماژول دیگری وابسته است.
برای تغییر یک قابلیت ساده، باید پنج بخش مختلف را بررسی کنی.
پروژه هنوز کار میکند.
اما دیگر شبیه کتابخانه نیست.
بیشتر شبیه انباری است.
انباری که همه چیز داخل آن هست.
اما پیدا کردن هر چیزی، زمان میبرد.
جالب اینجاست که هیچ پروژهای از روز اول انباری نبوده است.
فقط هر بار یک وابستگی کوچک اضافه شده.
یک میانبر دیگر.
یک دسترسی مستقیم دیگر.
تا جایی که مرزها از بین رفتهاند.
در مهندسی نرمافزار،معماری فقط برای این نیست که سیستم امروز کار کند.
برای این است که شش ماه بعد،هنوز بدانی هر چیز دقیقاً باید کجا باشد.
#مهندس_فکر_کن
قسمت:4️⃣
🎯مصاحبهکننده:
ءCache Stampede چیست؟
فرض کن یک کلید در Cache داری:
و TTL آن 5 دقیقه است.
در لحظهای که این کلید منقضی میشود، همزمان 5000 درخواست برای همان محصول میآید.
اتفاقی که میافتد:
همه همزمان Cache Miss میخورند.
سپس همه به Database میروند.
در نتیجه Database Overload، افزایش شدید Latency ،احتمال Down شدن سرویس.
به این حالت Cache Stampede میگویند.
حالا بگو ببینم برای حل این مشکل چه راهکار هایی میدی؟🤔
قسمت:4️⃣
🎯مصاحبهکننده:
ءCache Stampede چیست؟
فرض کن یک کلید در Cache داری:
Product:123
و TTL آن 5 دقیقه است.
در لحظهای که این کلید منقضی میشود، همزمان 5000 درخواست برای همان محصول میآید.
اتفاقی که میافتد:
Client 1 ----\
Client 2 -----\
Client 3 -------> Cache Miss
Client 4 -----/
Client 5 ----/
...
5000 Requests
همه همزمان Cache Miss میخورند.
سپس همه به Database میروند.
5000 Query
↓
Database
در نتیجه Database Overload، افزایش شدید Latency ،احتمال Down شدن سرویس.
به این حالت Cache Stampede میگویند.
حالا بگو ببینم برای حل این مشکل چه راهکار هایی میدی؟🤔
Forwarded from Learning With M
This media is not supported in your browser
VIEW IN TELEGRAM
دستور مصرف: هر صبح، حداقل یک بار بلافاصله بعد از بیدار شدن از خواب
پ.ن: الان تو کدوم ظرف برنجیم؟
یا شاید ظرف ها؟
پ.ن: الان تو کدوم ظرف برنجیم؟
یا شاید ظرف ها؟
Forwarded from thisisnabi.dev [Farsi]
کتاب خوب بخونیم.
این کتابه کمی گم هست و کم دیدم در موردش صحبت کنن، ولی واقعا کتاب خوبی هست. دیشب توی میت های 10x developer معرفیش کردم، گفتم اینجا هم برای شما هم بذارمش شاید کسی علاقه داشت بخوندش.
کلا این سری کتاب های pragmatic خیلی ارزش خوندن دارن و نگاه آدم رو نسبت به موضوعی که قرار هست مطرح کنن، تغییر می دن.
@thisisnabi_dev
این کتابه کمی گم هست و کم دیدم در موردش صحبت کنن، ولی واقعا کتاب خوبی هست. دیشب توی میت های 10x developer معرفیش کردم، گفتم اینجا هم برای شما هم بذارمش شاید کسی علاقه داشت بخوندش.
کلا این سری کتاب های pragmatic خیلی ارزش خوندن دارن و نگاه آدم رو نسبت به موضوعی که قرار هست مطرح کنن، تغییر می دن.
@thisisnabi_dev
گاهی فکر میکنیم دلیل اینکه یک توسعهدهنده بعد از چند سال بهتر میشود، این است که تکنولوژیهای بیشتری یاد گرفته است.
اما همیشه اینطور نیست.یک اتفاق مهمتر هم میافتد.
کمکم یاد میگیرد به هر مشکلی جواب ندهد.
اوایل مسیر، وقتی یک مشکل میبینی، اولین واکنش این است:
«باید سریع یک راهحل پیدا کنم.»
اما بعد از چند سال، واکنش تغییر میکند.
اول میپرسی:
«اصلاً مطمئنیم این همان مسئلهای است که باید حل شود؟»
جالب است که در خیلی از پروژهها، ساعتها صرف حل کردن مسئلهای میشود که از ابتدا، مسئله اصلی نبوده است.
ءQuery را بهینه میکنیم.بعد میفهمیم گلوگاه، شبکه بوده است.
ءCaching اضافه میکنیم.بعد مشخص میشود مشکل از طراحی درخواستها بوده است.
ءService جدید مینویسیم.بعد میبینیم با حذف یک وابستگی، کل مسئله حل میشد.
به مرور متوجه میشوی ارزش یک مهندس، فقط به تعداد راهحلهایی که بلد است نیست.
به تعداد مسئلههایی است که قبل از شروع، درست تشخیص میدهد. چون در مهندسی نرمافزار،اشتباهترین جواب، جواب اشتباه به یک سؤال درست نیست.
جواب کاملاً درست به یک سؤال اشتباه است.
اما همیشه اینطور نیست.یک اتفاق مهمتر هم میافتد.
کمکم یاد میگیرد به هر مشکلی جواب ندهد.
اوایل مسیر، وقتی یک مشکل میبینی، اولین واکنش این است:
«باید سریع یک راهحل پیدا کنم.»
اما بعد از چند سال، واکنش تغییر میکند.
اول میپرسی:
«اصلاً مطمئنیم این همان مسئلهای است که باید حل شود؟»
جالب است که در خیلی از پروژهها، ساعتها صرف حل کردن مسئلهای میشود که از ابتدا، مسئله اصلی نبوده است.
ءQuery را بهینه میکنیم.بعد میفهمیم گلوگاه، شبکه بوده است.
ءCaching اضافه میکنیم.بعد مشخص میشود مشکل از طراحی درخواستها بوده است.
ءService جدید مینویسیم.بعد میبینیم با حذف یک وابستگی، کل مسئله حل میشد.
به مرور متوجه میشوی ارزش یک مهندس، فقط به تعداد راهحلهایی که بلد است نیست.
به تعداد مسئلههایی است که قبل از شروع، درست تشخیص میدهد. چون در مهندسی نرمافزار،اشتباهترین جواب، جواب اشتباه به یک سؤال درست نیست.
جواب کاملاً درست به یک سؤال اشتباه است.
کانال یک برنامه نویس تنبل
🔥لینوکس
🔥برنامه نویسی فرانتاند
🔥برنامه نویسی بکاند
🔥اخبار تکنولوژی و...
@TheRaymondDev
🔥لینوکس
🔥برنامه نویسی فرانتاند
🔥برنامه نویسی بکاند
🔥اخبار تکنولوژی و...
@TheRaymondDev
در یکی از پروژه ها، قرار بود یک قابلیت جدید به سیستم اضافه کنیم.
یکی از اعضای تیم گفت:
«اگر از همین الان این بخش را Generic طراحی کنیم، بعدا برای چند سناریوی دیگر هم قابل استفاده است.»
پیشنهاد جذابی بود. شروع کردیم به طراحی. Interface اضافه شد. چند لایه Abstraction ایجاد شد. تنظیمات مختلف اضافه شدند.
قابلیتی که قرار بود در دو روز آماده شود، نزدیک دو هفته زمان برد.
چند ماه بعد، برگشتیم و نگاهی به آن بخش انداختیم.
از تمام سناریو هایی که برای آینده پیش بینی کرده بودیم، فقط همان سناریوی اول استفاده شده بود.
بقیه هیچ وقت اتفاق نیفتادند.آن روز یک درس مهم گرفتم.
بزرگ ترین خطر در طراحی نرم افزار، همیشه «طراحی ضعیف» نیست.
گاهی «طراحی برای آینده ای است که هنوز وجود ندارد.»
از آن به بعد، هر وقت وسوسه میشوم چیزی را بیش از نیاز امروز Generalize کنم، از خودم یک سوال میپرسم:
«اگر این نیاز هیچ وقت به وجود نیاید، آیا باز هم حاضر بودم هزینه امروز را پرداخت کنم؟»
خیلی وقتها جواب، «نه» است.
و همان «نه»، باعث میشود سیستم ساده تر بماند.
چون تجربه به من یاد داد که آینده، همیشه آنطور که ما طراحی میکنیم پیش نمیرود.
اما پیچیدگی ای که امروز وارد سیستم میکنیم، همین امروز شروع به گرفتن هزینه میکند.
یکی از اعضای تیم گفت:
«اگر از همین الان این بخش را Generic طراحی کنیم، بعدا برای چند سناریوی دیگر هم قابل استفاده است.»
پیشنهاد جذابی بود. شروع کردیم به طراحی. Interface اضافه شد. چند لایه Abstraction ایجاد شد. تنظیمات مختلف اضافه شدند.
قابلیتی که قرار بود در دو روز آماده شود، نزدیک دو هفته زمان برد.
چند ماه بعد، برگشتیم و نگاهی به آن بخش انداختیم.
از تمام سناریو هایی که برای آینده پیش بینی کرده بودیم، فقط همان سناریوی اول استفاده شده بود.
بقیه هیچ وقت اتفاق نیفتادند.آن روز یک درس مهم گرفتم.
بزرگ ترین خطر در طراحی نرم افزار، همیشه «طراحی ضعیف» نیست.
گاهی «طراحی برای آینده ای است که هنوز وجود ندارد.»
از آن به بعد، هر وقت وسوسه میشوم چیزی را بیش از نیاز امروز Generalize کنم، از خودم یک سوال میپرسم:
«اگر این نیاز هیچ وقت به وجود نیاید، آیا باز هم حاضر بودم هزینه امروز را پرداخت کنم؟»
خیلی وقتها جواب، «نه» است.
و همان «نه»، باعث میشود سیستم ساده تر بماند.
چون تجربه به من یاد داد که آینده، همیشه آنطور که ما طراحی میکنیم پیش نمیرود.
اما پیچیدگی ای که امروز وارد سیستم میکنیم، همین امروز شروع به گرفتن هزینه میکند.
📚 مسیر یادگیری یک برنامهنویس NET. در هر سطح
بیشتر توسعهدهندهها نمیدانند در هر مرحله از مسیر حرفهای خود باید روی چه موضوعاتی تمرکز کنند. به همین دلیل ممکن است سالها تجربه داشته باشند، اما هنوز برای موقعیتهای Senior یا Architect آماده نباشند.
اگر بخواهم مسیر یادگیری یک برنامهنویس NET. را به چهار مرحله تقسیم کنم، این نقشه راه را پیشنهاد میدهم:
1️⃣ Junior Developer
در این مرحله هدف، یادگیری اصول برنامهنویسی و ساخت پروژههای واقعی است.
✅ C# Syntax, Types & OOP
✅ Collections & Basic LINQ
✅ Async/Await Basics
✅ Exception Handling
✅ Building Basic APIs with Controllers & EF Core
✅ Configuration with appsettings.json
✅ Git Basics & IDE Debugging
2️⃣ Middle Developer
در این مرحله دیگر فقط کدنویسی مهم نیست؛ باید یاد بگیرید چرا یک راهحل از راهحل دیگر بهتر است.
✅ Advanced LINQ, IQueryable vs IEnumerable
✅ Async/Await, CancellationToken & Deadlocks
✅ EF Core Migrations, Relationships & Query Performance
✅ SOLID Principles & Common Design Patterns
✅ Clean Architecture & Layered Architecture
✅ Authentication & Authorization
✅ Middleware, Filters & Model Binding
✅ Validation, Mapping & Dependency Injection
✅ Scheduling & Caching
✅ Structured Logging
✅ Integration Testing
✅ Docker & Containerization
3️⃣ Senior Developer
در این سطح تمرکز روی طراحی سیستمهای قابل اعتماد، مقیاسپذیر و قابل نگهداری است.
✅ Performance, Memory & Allocation Profiling
✅ Concurrency, Threading & Thread Safety
✅ Distributed Systems Fundamentals
✅ RabbitMQ, Kafka & Azure Service Bus
✅ Resilience with Polly (Retry & Circuit Breaker)
✅ OpenTelemetry (Metrics, Tracing & Observability)
✅ CQRS & Event-Driven Architecture
✅ Database Transactions & Concurrency Control
✅ Security Best Practices (OWASP & Secret Management)
✅ CI/CD Pipelines & Cloud Deployment
✅ Domain Modeling & DDD Tactical Patterns
✅ Code Review & Mentoring
✅ Trade-off Analysis & Technology Selection
✅ Modular Monolith & Service Boundaries
4️⃣ Software Architect
در این مرحله دیگر مسئله فقط نوشتن کد نیست؛ بلکه طراحی سیستمهایی است که سالها قابل توسعه و نگهداری باشند.
✅ System Design (Scalability, Availability & Reliability)
✅ Monolith vs Modular Monolith vs Microservices vs Serverless
✅ Strategic DDD (Bounded Context & Context Mapping)
✅ Consistency, Idempotency & Saga Pattern
✅ Data Architecture, Event Sourcing & Polyglot Persistence
✅ CAP Theorem & PACELC Trade-offs
✅ Zero Trust Security & Identity
✅ Non-Functional Requirements (NFRs) & Quality Attributes
✅ ADRs (Architecture Decision Records) & C4 Model
✅ Migration & Modernization Strategy
✅ Standards, Guardrails & Governance
✅ Business-Driven Architecture
✅ Conway's Law & Team Topologies
✅ Stakeholder Communication & Trade-off Analysis
✅ Cloud Cost Optimization
✅ Platform Engineering & Developer Experience (DevEx)
✅ Disaster Recovery & Business Continuity
✅ Multi-Tenancy & Data Isolation Patterns
🎯 نکته مهم
این مسیر یک نردبان نیست که بعد از رسیدن به مرحله بعدی، مطالب قبلی را کنار بگذارید.
هرچه جلوتر میروید، عمق دانشتان در موضوعات قبلی بیشتر میشود و مفاهیم جدیدی به آن اضافه میکنید.
یک Architect خوب، هنوز هم #C را عمیق میشناسد؛ فقط نگاهش از «نوشتن کد» به «طراحی سیستم» تغییر کرده است.
🛡 اگر تیمت از اشتباه کردن جلوی تو میترسد، از قبل سقف رشدش را مشخص کردهای.
من به Psychological Safety به چشم یک واژهی مد روز نگاه نمیکنم. وقتی مسئول هدایت یک تیم هستی، این یکی از ملموسترین وظایفی است که بر عهده داری.
افراد باید بتوانند ایدهای را امتحان کنند، اشتباه کنند و دوباره بلند شوند؛ بدون اینکه نتیجهاش تحقیر شدن باشد. 🤝
نکتهای که همیشه سعی میکنم فراموش نکنم این است:
ساختن اعتمادبهنفس یک نفر، به زمان، انرژی و تلاش زیادی نیاز دارد؛ اما نابود کردنش، گاهی فقط به یک واکنش اشتباه نیاز دارد.
یک برخورد نامناسب جلوی اعضای تیم، میتواند اعتمادی را که ماهها برای ساختنش زحمت کشیدهای، در چند ثانیه از بین ببرد. 💔
بهعنوان یک رهبر، چه بخواهی و چه نخواهی، تو فرهنگ تیم را شکل میدهی.
واکنش تو به یک اشتباه، مخصوصاً زمانی که در مقابل دیگران اتفاق میافتد، به تمام اعضای تیم این پیام را میدهد که:
«آیا اینجا واقعاً جای امنی برای ریسک کردن و یاد گرفتن هست یا نه؟»
این به معنی نبود مسئولیتپذیری (Accountability) نیست.
اشتباه کردن با تحقیر شدن، دو مفهوم کاملاً متفاوت هستند.
وقتی افراد بدانند که میتوانند بدون ترس از سرزنش یا تحقیر شکست بخورند، حاضر میشوند ریسکهای هوشمندانهای انجام دهند که باعث پیشرفت تیم و محصول میشود. 🚀
اما اگر احساس امنیت نداشته باشند، همیشه محافظهکارانه عمل میکنند، از ایدههای جدید فاصله میگیرند و فقط کاری را انجام میدهند که مطمئن هستند بابتش مورد قضاوت قرار نمیگیرند.
در نهایت، کیفیت یک تیم را فقط مهارتهای فنی اعضایش تعیین نمیکند؛ بلکه محیطی که رهبر آن تیم میسازد نیز به همان اندازه مهم است.
❓ اگر مسئول هدایت یک تیم هستید، وقتی یکی از اعضای تیمتان اشتباه میکند، اولین واکنش شما چیست؟
من به Psychological Safety به چشم یک واژهی مد روز نگاه نمیکنم. وقتی مسئول هدایت یک تیم هستی، این یکی از ملموسترین وظایفی است که بر عهده داری.
افراد باید بتوانند ایدهای را امتحان کنند، اشتباه کنند و دوباره بلند شوند؛ بدون اینکه نتیجهاش تحقیر شدن باشد. 🤝
نکتهای که همیشه سعی میکنم فراموش نکنم این است:
ساختن اعتمادبهنفس یک نفر، به زمان، انرژی و تلاش زیادی نیاز دارد؛ اما نابود کردنش، گاهی فقط به یک واکنش اشتباه نیاز دارد.
یک برخورد نامناسب جلوی اعضای تیم، میتواند اعتمادی را که ماهها برای ساختنش زحمت کشیدهای، در چند ثانیه از بین ببرد. 💔
بهعنوان یک رهبر، چه بخواهی و چه نخواهی، تو فرهنگ تیم را شکل میدهی.
واکنش تو به یک اشتباه، مخصوصاً زمانی که در مقابل دیگران اتفاق میافتد، به تمام اعضای تیم این پیام را میدهد که:
«آیا اینجا واقعاً جای امنی برای ریسک کردن و یاد گرفتن هست یا نه؟»
این به معنی نبود مسئولیتپذیری (Accountability) نیست.
اشتباه کردن با تحقیر شدن، دو مفهوم کاملاً متفاوت هستند.
وقتی افراد بدانند که میتوانند بدون ترس از سرزنش یا تحقیر شکست بخورند، حاضر میشوند ریسکهای هوشمندانهای انجام دهند که باعث پیشرفت تیم و محصول میشود. 🚀
اما اگر احساس امنیت نداشته باشند، همیشه محافظهکارانه عمل میکنند، از ایدههای جدید فاصله میگیرند و فقط کاری را انجام میدهند که مطمئن هستند بابتش مورد قضاوت قرار نمیگیرند.
در نهایت، کیفیت یک تیم را فقط مهارتهای فنی اعضایش تعیین نمیکند؛ بلکه محیطی که رهبر آن تیم میسازد نیز به همان اندازه مهم است.
❓ اگر مسئول هدایت یک تیم هستید، وقتی یکی از اعضای تیمتان اشتباه میکند، اولین واکنش شما چیست؟