C# Geeks (.NET)
412 subscribers
152 photos
4 videos
152 links
Download Telegram
#AI_Software_Engineering
مدتی قبل فکر می‌کردم برای کار کردن با AI حتماً باید یک فرآیند جدید، یک چارچوب پیچیده و یک روش توسعه کاملاً متفاوت داشته باشیم.Prompt Engineering. Workflowهای عجیب.
قوانین سخت برای استفاده از AI.
اما کم‌کم یک سؤال در ذهنم شکل گرفت:
نکند بخشی از این پیچیدگی‌ها را خودمان ساخته‌ایم، فقط چون هنوز نمی‌دانیم چطور با این ابزار جدید کنار بیاییم؟
ما سال‌ها برای اینکه سیستم‌ها قابل کنترل باشند، ساختار ایجاد کردیم.
Architecture.
Design Pattern.
Code Review.
Testing.
Documentation.
همه این‌ها ارزشمند هستند.
اما شاید بخشی از آن‌ها، پاسخی به محدودیت‌های گذشته بوده‌اند.
امروز یک AI Agent می‌تواند در چند دقیقه کاری را انجام دهد که قبلاً ساعت‌ها زمان می‌برد. Refactor کردن یک بخش بزرگ از سیستم.
پیدا کردن Dependencyها.
نوشتن Test.
پیشنهاد تغییرات.
حتی آماده کردن یک Pull Request.
اینجا یک سؤال مهم مطرح می‌شود:
آیا ما باید AI را مجبور کنیم مثل یک Developer قدیمی کار کند؟
یا باید یاد بگیریم چطور سطح تعامل خودمان با سیستم‌ها را بالاتر ببریم؟
چون شاید در آینده، ارزش یک مهندس کمتر در این باشد که:
«چقدر سریع کد می‌نویسی؟»
و بیشتر در این باشد که:
«آیا می‌دانی چه چیزی باید ساخته شود؟»
اما یک نکته هنوز تغییر نکرده. AI می‌تواند تغییر ایجاد کند.
اما مسئولیت تصمیم‌گیری هنوز با انسان است.
چون یک سیستم اشتباه، اگر با AI سریع‌تر ساخته شود...
فقط سریع‌تر به یک مشکل بزرگ تبدیل می‌شود.
شاید آینده برنامه‌نویسی این نباشد که انسان‌ها کمتر کد بنویسند.
شاید آینده این باشد که انسان‌ها کمتر درگیر جزئیات شوند و بیشتر روی تفکر مهندسی تمرکز کنند.
چون ابزارها همیشه تغییر می‌کنند.
اما توانایی تشخیص یک تصمیم درست از یک تصمیم اشتباه،
همیشه ارزشمند باقی می‌ماند.
🚦 چه زمانی در ASP.NET Core باید از CancellationTokenSource استفاده کنیم؟

یکی از قابلیت‌هایی که از NET 4. به این طرف وارد فریمورک شد، Cooperative Cancellation است.
قبل از آن، برای متوقف کردن Threadها معمولاً از APIهایی مانند Thread.Abort() استفاده می‌شد؛ APIهایی که می‌توانستند Thread را در هر نقطه‌ای متوقف کنند و باعث ناپایداری برنامه شوند.
به همین دلیل مایکروسافت مدل جدیدی را معرفی کرد:
هیچ عملیاتی نباید به زور متوقف شود؛ خود عملیات باید تصمیم بگیرد که چه زمانی متوقف شود.

به این مدل Cooperative Cancellation گفته می‌شود.
💡 سه بازیگر اصلی در Cancellation
مستندات NET. سه جزء اصلی را معرفی می‌کنند:
1️⃣ CancellationTokenSource
این کلاس مسئول ارسال درخواست لغو است.
using var cts = new CancellationTokenSource();

2️⃣ CancellationToken
این ساختار فقط وضعیت لغو را نگه می‌دارد.
CancellationToken token = cts.Token;

هرگز خودش عملیات را متوقف نمی‌کند.
3️⃣ عملیاتی که Token را دریافت می‌کند
مثلاً:
HttpClient
EF Core
Task.Delay
Stream
File APIs
Parallel APIs
همگی Token را دریافت می‌کنند.
await Task.Delay(
TimeSpan.FromSeconds(10),
token);

اگر درخواست لغو ارسال شود، خود Task.Delay تصمیم می‌گیرد عملیات را متوقف کند.
🏗 جریان کار چگونه است؟
CancellationTokenSource

│ Cancel()

CancellationToken


HttpClient / EF Core / Task / ...


OperationCanceledException

نکته مهم:
ءCancellationTokenSource هیچ Task یا Threadی را Kill نمی‌کند.
فقط اعلام می‌کند:
"اگر هنوز مشغول کار هستی، لطفاً متوقف شو."

این دقیقاً همان چیزی است که Microsoft از آن با عنوان Cooperative Cancellation یاد می‌کند.
🎯 حالا سؤال اصلی:
در ASP.NET Core چه زمانی باید خودمان CancellationTokenSource بسازیم؟

سناریو اول: Timeout اختصاصی
فرض کنید فراخوانی یک سرویس پرداخت نباید بیشتر از ۵ ثانیه طول بکشد.
using var cts =
new CancellationTokenSource(
TimeSpan.FromSeconds(5));

await gateway.PayAsync(
request,
cts.Token);

بعد از ۵ ثانیه:
cts.Cancel()

به صورت خودکار اجرا می‌شود.
طبق مستندات، این یکی از رایج‌ترین کاربردهای CancellationTokenSource است.

سناریو دوم: لغو دستی عملیات
فرض کنید کاربر دکمه Cancel را فشار می‌دهد.
cts.Cancel();

از این لحظه تمام عملیات‌هایی که این Token را دریافت کرده‌اند متوجه درخواست لغو می‌شوند.

سناریو سوم: ترکیب چند Token
در ASP.NET Core معمولاً این Token را دارید:
HttpContext.RequestAborted

این Token زمانی Cancel می‌شود که:
مرورگر بسته شود. Client ارتباط را قطع کند. حالا فرض کنید علاوه بر آن، می‌خواهید Timeout هم داشته باشید.
مایکروسافت پیشنهاد می‌کند:
using var timeout =
new CancellationTokenSource(
TimeSpan.FromSeconds(10));

using var linked =
CancellationTokenSource
.CreateLinkedTokenSource(
HttpContext.RequestAborted,
timeout.Token);

در این حالت اگر هر کدام از Tokenها Cancel شوند، عملیات نیز Cancel خواهد شد.

اشتباه رایج
در بسیاری از پروژه‌ها می‌بینیم:
public async Task Handle()
{
using var cts =
new CancellationTokenSource();
...
}

بدون هیچ دلیل مشخصی.
در حالی که ASP.NET Core خودش Token مناسب را در اختیار شما قرار داده است:
public async Task<IActionResult> Create(
CancellationToken cancellationToken)

اگر فقط می‌خواهید عملیات در صورت لغو Request متوقف شود، همان Token را به تمام لایه‌ها پاس دهید.
ساخت CancellationTokenSource جدید، ارتباط عملیات با Request اصلی را قطع می‌کند.

⚠️ نکته‌ای که خیلی‌ها نمی‌دانند
ءCancellationTokenSource از IDisposable پیروی می‌کند.
مایکروسافت صراحتاً توصیه می‌کند بعد از اتمام کار آن را Dispose کنید.
using var cts =
new CancellationTokenSource();

⚠️ یک باور اشتباه
بعضی‌ها تصور می‌کنند:
Cancel()

یعنی Task فوراً متوقف می‌شود.
اما مستندات دقیقاً برعکس این را می‌گویند.
بعد از ارسال درخواست لغو، این خود عملیات است که باید:
token.ThrowIfCancellationRequested();

را بررسی کند یا APIای که Token را دریافت کرده، به آن واکنش نشان دهد.
به همین دلیل به این مدل می‌گویند:
Cooperative Cancellation

نه

Forced Cancellation.
#تحلیل_و_طرز_تفکر (Engineering Mindset)
یک سؤال هست که مهندس‌های باتجربه بیشتر از بقیه از خودشان می‌پرسند:
«اگر من شش ماه دیگر از این تیم بروم، چه اتفاقی برای این سیستم می‌افتد؟»
اگر جواب این باشد که:
فقط خودم می‌دانم این بخش چطور کار می‌کند.
فقط خودم می‌توانم آن را Deploy کنم.
فقط خودم می‌توانم باگش را پیدا کنم.
فقط خودم می‌دانم چرا این تصمیم را گرفته‌ایم.
شاید مسئله، مهارت بالا نباشد.
شاید سیستم، بیش از حد به یک نفر وابسته شده است.
یکی از نشانه‌های بلوغ مهندسی این نیست که خودت بتوانی همه مشکلات را حل کنی.
این است که دیگران هم بتوانند بعد از تو سیستم را ادامه دهند.
به همین دلیل، مستندسازی، Code Review، Naming مناسب، تست و ثبت تصمیم‌های معماری فقط برای امروز نیستند.
همه‌ی آن‌ها برای روزی هستند که شخص دیگری باید بدون حضور تو، روی همان سیستم کار کند.
یک مهندس خوب، سیستم می‌سازد.
یک مهندس بالغ، سیستمی می‌سازد که به خودش وابسته نباشد.
چون در نهایت،
بهترین کد، کدی نیست که فقط نویسنده‌اش آن را بفهمد.
بهترین کد، کدی است که نبودِ نویسنده‌اش، تیم را متوقف نکند.
#تصمیم‌های_مهندسی (Engineering Decisions)
یکی از سخت‌ترین تصمیم‌های مهندسی، انتخاب بین درست بودن و در دسترس بودن است.
فرض کنید Database اصلی شما از دسترس خارج شده است.
یک Replica دارید که چند ثانیه از دیتای اصلی عقب‌تر است.
حالا دو انتخاب دارید.
انتخاب اول:
کاربر را منتظر نگه دارید یا حتی درخواست را Fail کنید، تا مطمئن شوید داده‌ای که نمایش می‌دهید کاملاً به‌روز است.
انتخاب دوم:
درخواست را از Replica پاسخ دهید.
کاربر سرویس را دریافت می‌کند...
اما شاید اطلاعاتی را ببیند که چند ثانیه قدیمی است.
کدام تصمیم درست است؟
اگر در حال نمایش تعداد لایک یک پست باشید،
احتمالاً چند ثانیه اختلاف اهمیتی ندارد.
اما اگر موجودی حساب بانکی یا تعداد باقی‌مانده بلیت یک کنسرت را نمایش می‌دهید،
همان چند ثانیه می‌تواند یک فاجعه ایجاد کند.
به همین دلیل، مهندسان باتجربه هیچ‌وقت نمی‌پرسند:
«ءConsistency بهتر است یا Availability؟»
بلکه می‌پرسند:
«در این Domain، هزینه‌ی کدام اشتباه بیشتر است؟»
چون هیچ معماری‌ای وجود ندارد که در هر شرایطی، هم بیشترین Availability را داشته باشد و هم قوی‌ترین Consistency را.
هر تصمیم، یک Trade-off است.
و ارزش یک مهندس، در انتخاب تکنولوژی نیست.
در درک هزینه‌ی انتخاب‌ها است.
چون در مهندسی نرم‌افزار،
بهترین تصمیم، تصمیمی نیست که هیچ هزینه‌ای نداشته باشد.
بهترین تصمیم، تصمیمی است که هزینه‌ی درست را بپردازد.
Forwarded from thisisnabi.dev [Farsi]
8. Leaderless replication
—-
1. Designing Data Intensive Applications
- Replication [6]


/three-lens-tutor Leaderless replication


@thisisnabi_dev
#باور_غلط_یا_واقعیت؟
باور غلط

«هرچه معماری تمیزتر باشد، سیستم بهتر است.»
واقعیت

معماری تمیز، همیشه معماری مناسب نیست.
گاهی یک تیم، هفته‌ها زمان صرف می‌کند تا:
همه چیز Interface داشته باشد.
هر کلاس فقط یک مسئولیت داشته باشد. Dependencyها کاملاً از هم جدا شوند.
همه چیز "طبق اصول" باشد.
نتیجه؟
کدی که از نظر تئوری فوق‌العاده است...
اما توسعه‌دهنده جدید برای پیدا کردن یک منطق ساده باید از میان ۱۲ فایل عبور کند.
گاهی آن‌قدر روی تمیز بودن معماری تمرکز می‌کنیم که فراموش می‌کنیم هدف اصلی چیست.
کم کردن هزینه‌ی تغییر.
اگر یک معماری:
فهم سیستم را سخت‌تر کند،
ءDebug کردن را طولانی‌تر کند،
ءOnboarding اعضای جدید را دشوار کند،
و هر تغییر کوچک را به ده‌ها فایل بکشاند،
شاید بیش از حد «تمیز» شده باشد.
معماری خوب، معماری‌ای نیست که بیشترین Pattern را داشته باشد.
معماری خوب، معماری‌ای است که حل مسئله را ساده‌تر کند، نه اینکه خودش به مسئله تبدیل شود.
💡 جمع‌بندی

بین «کد تمیز» و «سیستم قابل‌فهم» همیشه علامت مساوی وجود ندارد.
در مهندسی نرم‌افزار،
زیبایی معماری را با تعداد Patternها نسنج.
با سرعتی بسنج که تیم می‌تواند با اطمینان آن را تغییر دهد.
#فرمون_دادن
یک اشتباه رایج در تیم‌های نرم‌افزاری این است که فکر می‌کنیم برای سریع‌تر شدن، باید آدم‌های بیشتری را وارد یک کار کنیم.
پروژه عقب افتاده؟ آدم اضافه کن.
تسک زیاد شده؟Developer اضافه کن.
ءDeadline نزدیک است؟ تیم را بزرگ‌تر کن.
روی کاغذ منطقی به نظر می‌رسد.
اما نرم‌افزار مثل خط تولید کارخانه نیست که با اضافه کردن آدم، خروجی همیشه بیشتر شود.
فرض کنید یک تیم ۴ نفره روی یک Feature کار می‌کند.
حالا برای اینکه سریع‌تر تمام شود، ۶ نفر دیگر هم اضافه می‌شوند.
ناگهان باید:
جلسه‌های بیشتری برگزار شود. Context بیشتری منتقل شود. Code Reviewهای بیشتری انجام شود.
تصمیم‌های بیشتری هماهنگ شود.
و افراد بیشتری منتظر یکدیگر بمانند.
یعنی بخشی از زمانی که قرار بود صرف ساختن شود، صرف هماهنگ شدن می‌شود.
مشکل از آدم‌های جدید نیست.
مشکل این است که Complexity ارتباطی هم همراه آن‌ها رشد می‌کند.
برای همین، قبل از اینکه بگویی:
«آدم بیشتری اضافه کنیم.»
یک سؤال مهم‌تر بپرس:
«مشکل واقعاً کمبود آدم است یا کمبود تمرکز؟»
گاهی یک تیم کوچک که دقیقاً می‌داند چه چیزی باید بسازد، از یک تیم بزرگ که مدام در حال هماهنگ شدن است، سریع‌تر حرکت می‌کند.
در مهندسی نرم‌افزار، تعداد بیشتر، همیشه به معنی سرعت بیشتر نیست.
گاهی برای سریع‌تر شدن،باید به‌جای اضافه کردن آدم، موانع را کم کنیم.
🎯 ایده اصلی کتاب چیست؟
مهم‌ترین ایده کتاب این است که Engineering Management ادامه‌ی طبیعی Senior Software Engineering نیست؛ یک نقش متفاوت با مسئله‌ای متفاوت است.
وقتی Engineer هستی، بخش زیادی از خروجی تو مستقیماً از چیزهایی می‌آید که خودت می‌سازی:
Code → Feature → System → Result

اما وقتی Manager می‌شوی، دیگر قرار نیست خودت بیشترین کد را بنویسی.
خروجی تو بیشتر از این مسیر می‌آید:
People → Team → Environment → Engineering Output

بنابراین کتاب تلاش می‌کند به کسی که تازه وارد Management شده یاد بدهد چگونه از حالت «خودم انجام می‌دهم» به «شرایطی ایجاد می‌کنم که تیم بتواند انجام دهد» تغییر کند.

📚 کتاب چه چیزهایی را آموزش می‌دهد؟

1. 🧭 ورود به نقش Manager
🧠 2. اول خودت را مدیریت کن
👥 3. مدیریت افراد
🎯 4. ءDelegation؛ یکی از مهم‌ترین مهارت‌ها
🔥 5. ءMicromanagement
🗣 6. ءFeedback و Performance
🧑‍🏫 7. ءCoaching و Mentoring
🏗 8. ساختن یک Team خوب
📈 9. رشد شغلی افراد
🧩 10. فقط تیم خودت مهم نیست

📌 در یک جمله

اگر بخواهم کتاب را خیلی خلاصه کنم:
این کتاب به تو یاد نمی‌دهد چگونه Developerهای بیشتری مدیریت کنی؛ به تو یاد می‌دهد چگونه محیطی بسازی که Developerها بتوانند بهتر کار کنند، رشد کنند و خروجی بهتری به‌عنوان یک تیم داشته باشند.
#تحلیل_و_طرز_تفکر (Engineering Mindset)
گاهی یک سؤال ساده می‌تواند کیفیت یک طراحی را مشخص کند:
«اگر این بخش سیستم فردا خراب شود، چه چیزی باید بتواند بدون آن ادامه دهد؟»
خیلی از سیستم‌ها برای حالت سالم طراحی می‌شوند.
ءDatabase در دسترس است.
ءRedis سالم است.
ءMessage Broker کار می‌کند.
ءExternal API پاسخ می‌دهد.
ءNetwork پایدار است.
همه‌چیز طبق انتظار پیش می‌رود.
اما Production دقیقاً جایی است که این فرض‌ها شروع به شکستن می‌کنند.
ءDatabase ممکن است ۳۰ ثانیه کند شود.
ءRedis ممکن است از دسترس خارج شود.
یک Message ممکن است دوبار Deliver شود.
یک External API ممکن است Timeout کند.
و Network ممکن است Packet Loss داشته باشد.
اینجاست که تفاوت بین یک سیستم معمولی و یک سیستم Resilient مشخص می‌شود.
سیستم خوب فقط نمی‌گوید:
«اگر همه‌چیز سالم باشد، چه اتفاقی می‌افتد؟»
بلکه می‌پرسد:
«اگر یکی از وابستگی‌های من خراب شود، دقیقاً چه چیزی باید همچنان کار کند؟»
مثلاً اگر Notification Service از دسترس خارج شد،
آیا ثبت سفارش هم باید Fail شود؟
اگر Analytics Service Down شد،
آیا کاربر باید نتواند وارد سیستم شود؟
اگر Recommendation Service پاسخ نداد، آیا صفحه محصول باید Error بدهد؟
پاسخ این سؤال‌ها را نمی‌توان با یک Pattern آماده داد.
باید از Business Requirement بیاید.
به همین دلیل، Resilience فقط اضافه کردن Retry و Circuit Breaker نیست.
اول باید مشخص کنی:
کدام شکست‌ها قابل تحمل‌اند و کدام‌ها نیستند.
بعد برایشان طراحی کنی.
چون در سیستم‌های واقعی، خرابی Exception نیست.
بخشی از شرایط عادی سیستم است که باید برایش طراحی شده باشی.