Forwarded from tech-afternoon (Amin Mesbahi)
اگر "بد" نمایانگر اصطکاک عملیاتیه، "زشت" نمایانگر ریسک سیستمیه. دادههای سالهای ۲۰۲۴ و ۲۰۲۵ به بحرانی قریبالوقوع در قابلیت نگهداری و امنیت نرمافزار اشاره میکنن...
جنبهی «زشت» ماجرا اینه که نتیجهی نهایی استفاده از هوش مصنوعی مولد بهشدت وابسته به بلوغ فنی و انضباط تیمه. اگر تیمی فرهنگ کدنویسی سالم، معیارهای کیفی و فرایندهای بازبینی روشن نداشته باشه، برای استفاده از GenAI دستورالعمل «فکر شده» و متناسب با نیازها و استعداد تیم نداشته باشه؛ AI میتونه هرجومرج ایجاد کنه یا هرجومرج موجود رو تشدید کنه. توی برخی نظرسنجیها دیده شده که کارکنان احساس کردن بهرهوریشون با وجود هوش مصنوعی کاهش یافته!
بدهی فنی که قابل پرداخت نیست
پروفسور Armando Solar-Lezama استاد دانشگاه MIT میگه: "AI مثل یه کارت اعتباری جدیده که به ما اجازه میده بدهی فنی رو به روشهایی انباشته کنیم که هرگز قبلاً نتونسته بودیم."
مطالعه دانشگاه Carnegie Mellon روی ۸۰۷ ریپو GitHub که بین ژانویه ۲۰۲۴ تا مارچ ۲۰۲۵ که از Cursor استفاده کرده بودن، نشون میده که با وجود بهبودهای مدلهای AI (Sonnet، GPT و غیره)، الگوی کاهش کیفیت کد همچنان ادامه داره. حتی با ارتقای ابزارها، کیفیت کد مسیر خودش رو به سمت افول طی میکنه! دلایلی مثل زمان صرف زیاد برای آزمونوخطا با ابزار یا رفع خطاهای ناشی از اون رو میشه در نظر گرفت؛ و تفاوت نتایج بین شرکتهای مختلف (از افزایش کارامدی تا معضلات عمیق) نشون میده که صرف خریداری یا فعالسازی ابزار یا سرویس هوشمصنوعی تضمینی برای موفقیت نیست.
- نابودی دانش تیمی: باز هم مطالعات نشون میدن در ۱۶.۸٪ از چتهای ChatGPT، کد تولید شده به صورت دقیق (با تغییرات جزئی) توی پروژههای GitHub استفاده شدن. مشکل اینجاست: وقتی توسعهدهندهها کد AI رو بدون درک عمیق copy میکنن، expertise model توی تیم توسعه آسیب میبینه و Truck Factor (تعداد اعضای تیم که از دست دادنشون پروژه را میتونه نابود کنه، گاهی هم bus factor گفته میشه) بدتر میشه.
- معضل Context Collapse در آینده: اگه کدهایی که مدلهای آینده از روی اونها train میشن، پیچیدهتر و غیرقابل نگهداریتر بشه، خطر واقعی اینه که مدلهای جدیدتر این روندها رو به صورت نمایی تقویت و تشدید میکنن و کد بدتری تولید خواهند کرد؛ دلیلش هم اینه که از روی کدهای شلوغ و بیکیفیتی آموزش دیدهاند.
- مشارکتکننده دورهگرد: کدهای تولید شده توسط هوش مصنوعی شبیه کار یک پیمانکار کوتاهمدته: از نظر عملکردی در انزوا، صحیح، اما منفک از قراردادها و معماری سیستم کلی! این منجر به تکهتکه شدن (Fragmentation) سبک و منطق کد میشه.
- پارادوکس بهرهوری مهندسی: ترکیب "خوب" (سرعت) و "زشت" (ریزش/کیفیت) منجر به شکلگیری "پارادوکس بهرهوری مهندسی" شده. سازمانها شاهد افزایش چشمگیر خروجی (پولریکوئستها، ویژگیها) هستن، اما همزمان کاهش پایداری و افزایش هزینههای نگهداری رو تجربه میکنن. گزارش سال ۲۰۲۵ DORA از گوگل نشون داد که افزایش ۹۰ درصدی در پذیرش هوش مصنوعی با افزایش ۹ درصدی نرخ باگ و افزایش ۹۱ درصدی زمان بازبینی کد همبستگی داره (بدتر از گزارش DORA در سال ۲۰۲۴ که پیشتر در بخش افزایش باگ و کاهش پایداری قسمت اول اشاره کردم). زمان صرفهجویی شده در تایپ کردن کد، عملاً به مرحله بازبینی و دیباگ منتقل شده؛ با این تفاوت که هزینه این مرحله بالاتره، چون خوندن کد تولید شده سختتر از نوشتنشه.
- انباشت بدهی فنی: انباشت کدهای ضعیف ساختاری، که با پیچیدگی بالا (Cyclomatic Complexity) و تکرار زیاد مشخص میشن؛ بدهیای ایجاد میکنه که باید با بهره پرداخت بشه. Forrester پیشبینی میکنه که سال ۲۰۲۶، ۷۵٪ از شرکتها به دلیل تولید کد کنترلنشدهی هوش مصنوعی، با بدهی فنی "متوسط تا شدید" مواجه خواهند شد.
Please open Telegram to view this post
VIEW IN TELEGRAM
3️⃣ Publish/Subscribe Pattern 📣📡(بخش سوم)
ءPublish/Subscribe Pattern یک الگوی پیامرسانی غیرهمزمان است که در آن Publisherها پیامها یا Eventها را به یک Message Broker یا Event Bus مرکزی ارسال میکنند، بدون اینکه بدانند چه کسی مصرفکننده آنهاست.
در مقابل، Subscriberها علاقهمندی خود را به انواع خاصی از پیامها ثبت میکنند و بهصورت خودکار هنگام انتشار آنها را دریافت میکنند. این الگو منجر به یک Event-Driven Architecture با Coupling بسیار کم میشود.
🔍 تفاوت اصلی Publish/Subscribe با Point To Point Async Integration
ءPublish/Subscribe فرض میکند که چندین Subscriber میتوانند برای یک نوع Event وجود داشته باشند
ءPoint To Point Async Integration فرض میکند که فقط یک Subscriber برای هر نوع پیام وجود دارد
🔧 How it works:
🔹️ءPublisherها پیامها یا Eventها را به Topic یا Channelهای Message Broker ارسال میکنند بدون اینکه از Subscriberها اطلاعی داشته باشند
🔸️ءMessage Broker پیامها را دریافت، ذخیره و توزیع میکند
🔹️ءSubscriberها علاقهمندی خود را به Topic یا Event Type خاص ثبت میکنند
🔸️با انتشار یک پیام، Broker نسخهای از آن را به تمام Subscriberهای فعال ارسال میکند
🔹️چندین Subscriber میتوانند همزمان و مستقل یک پیام یکسان را پردازش کنند
🔸️این الگو از ارتباط One-to-Many پشتیبانی میکند
🔹️اضافه یا حذف Subscriberها نیازی به تغییر در Publisher ندارد
🔸️امکان Message Filtering وجود دارد تا هر Subscriber فقط پیامهای مرتبط را دریافت کند
✅ Benefits:
• ءDecoupling کامل بین Publisher و Subscriber
• ءScalability بالا با پردازش موازی پیامها توسط چند Subscriber
• مناسب برای Event-Driven Architecture
• افزودن قابلیتهای جدید فقط با اضافه کردن Subscriber جدید
• ءResilience بهتر؛ خطای یک Subscriber روی بقیه تأثیر ندارد
❌ Drawbacks:
• ءMessage Broker یک وابستگی مهم است و میتواند Single Point of Failure باشد
• ءDebugging و Tracing سختتر بهدلیل ماهیت غیرهمزمان
• چالشهای Eventual Consistency
• نیاز به طراحی دقیق برای Ordering پیامها و Duplicate Handling
🎯 Use cases:
🔹️سیستمهای Event-Driven که چند سرویس باید به یک Event واکنش نشان دهند
🔸️ءReal-time Notification (چت، داشبوردها، مانیتورینگ)
🔹️ءMicroservices برای همگامسازی دادهها از طریق Integration Events
🔸️سناریوهایی که سرویسهای جدید باید بدون تغییر Publisher به Eventها گوش دهند
🔹️ءWorkflowهایی که با یک Event چند مرحله در سرویسهای مختلف فعال میشوند
Forwarded from Sonora.Dev
🛠 حل مشکل Double Booking در سیستمهای رزرو
تمام پلتفرمهای رزرو مدرن با چالش Double Booking روبرو هستند: وقتی دو یا چند کاربر بهطور همزمان تلاش میکنند یک منبع محدود را رزرو کنند.
این مشکل، یک race condition است که میتواند اعتماد کاربر را نابود کند و برای سیستمهای پرترافیک، بحرانی است.
1️⃣ Pessimistic Locking
مکانیزم: قفل روی رکورد دیتابیس (SELECT ... FOR UPDATE)
مزایا: تضمین Consistency، جلوگیری از race condition
معایب: Throughput محدود، Deadlock Risk، مقیاسپذیری پایین
مناسب برای: Low-traffic / کمرقابت (مثل Web Check-in هواپیما)
2️⃣ Optimistic Locking
مکانیزم: بدون قفل، با استفاده از Versioning
مزایا: عملکرد خواندن بالا، افزایش concurrency
معایب: Conflict و Retry در High Contention، افزایش load روی DB
مناسب برای: Moderate traffic و منابع کمرقابت (رزرو هتل، رستوران)
3️⃣ In-Memory Distributed Locking
مکانیزم: Lock توزیعشده در Redis / In-Memory Cache
مزایا: کاهش فشار روی دیتابیس، High Concurrency، Low Latency
معایب: پیچیدگی زیرساخت، مدیریت crash و expiration، ریسک Lock ناتمام
مناسب برای: Popular events با 1K–10K RPS
4️⃣ Virtual Waiting Queue
مکانیزم: Async Queue + Backpressure + FIFO
مزایا:
محافظت از دیتابیس و cache در برابر surge
بهبود تجربه کاربری و fairness
مقیاسپذیری بسیار بالا (High Throughput)
معایب: پیچیدگی عملیاتی، نیاز به SSE یا WebSocket برای اطلاعرسانی
مناسب برای: Ultra High Traffic events (کنسرتها، فیلمهای بلاکباستر)
✅ جمعبندی فنی
هیچ راهحل واحدی برای همه سناریوها وجود ندارد
انتخاب معماری به الگوی ترافیک، سطح رقابت و محدودیت منابع وابسته است
سیستمهای High-Traffic باید Lock-free + Async + Fair Queue داشته باشند تا Tail Latency و double booking کنترل شود
Monitoring، Retry Policies و Backpressure، اجزای کلیدی در طراحی سیستم رزرو مقیاسپذیر هستند
#SystemDesign #DistributedSystems #Scalability #Concurrency #BackendArchitecture #HighTraffic #BookingSystems #Microservices #Queueing
تمام پلتفرمهای رزرو مدرن با چالش Double Booking روبرو هستند: وقتی دو یا چند کاربر بهطور همزمان تلاش میکنند یک منبع محدود را رزرو کنند.
این مشکل، یک race condition است که میتواند اعتماد کاربر را نابود کند و برای سیستمهای پرترافیک، بحرانی است.
1️⃣ Pessimistic Locking
مکانیزم: قفل روی رکورد دیتابیس (SELECT ... FOR UPDATE)
مزایا: تضمین Consistency، جلوگیری از race condition
معایب: Throughput محدود، Deadlock Risk، مقیاسپذیری پایین
مناسب برای: Low-traffic / کمرقابت (مثل Web Check-in هواپیما)
2️⃣ Optimistic Locking
مکانیزم: بدون قفل، با استفاده از Versioning
مزایا: عملکرد خواندن بالا، افزایش concurrency
معایب: Conflict و Retry در High Contention، افزایش load روی DB
مناسب برای: Moderate traffic و منابع کمرقابت (رزرو هتل، رستوران)
3️⃣ In-Memory Distributed Locking
مکانیزم: Lock توزیعشده در Redis / In-Memory Cache
مزایا: کاهش فشار روی دیتابیس، High Concurrency، Low Latency
معایب: پیچیدگی زیرساخت، مدیریت crash و expiration، ریسک Lock ناتمام
مناسب برای: Popular events با 1K–10K RPS
4️⃣ Virtual Waiting Queue
مکانیزم: Async Queue + Backpressure + FIFO
مزایا:
محافظت از دیتابیس و cache در برابر surge
بهبود تجربه کاربری و fairness
مقیاسپذیری بسیار بالا (High Throughput)
معایب: پیچیدگی عملیاتی، نیاز به SSE یا WebSocket برای اطلاعرسانی
مناسب برای: Ultra High Traffic events (کنسرتها، فیلمهای بلاکباستر)
✅ جمعبندی فنی
هیچ راهحل واحدی برای همه سناریوها وجود ندارد
انتخاب معماری به الگوی ترافیک، سطح رقابت و محدودیت منابع وابسته است
سیستمهای High-Traffic باید Lock-free + Async + Fair Queue داشته باشند تا Tail Latency و double booking کنترل شود
Monitoring، Retry Policies و Backpressure، اجزای کلیدی در طراحی سیستم رزرو مقیاسپذیر هستند
#SystemDesign #DistributedSystems #Scalability #Concurrency #BackendArchitecture #HighTraffic #BookingSystems #Microservices #Queueing
بهترین رهبران پر سروصدا نیستند.
برخی از مورد اعتمادترین افراد در جمع، کمترین صحبت را میکنند.
شما این نوع افراد را میشناسید:
🔹️حرفشان را عملی میکنند و به قول خود پایبندند
🔸️قبل از صحبت کردن با دقت گوش میدهند
🔹️برای دیگران فضا ایجاد میکنند بدون اینکه دنبال اعتبار باشند
🔸️آنها در میان آشوب، آرامش میآورند. نیازی ندارند قدرت یا نفوذ خود را نشان دهند.
آنها این اعتماد را به دست آوردهاند.
اغلب ما رهبر بودن را با کاریزما، دیدهشدن یا جسارت مرتبط میکنیم، اما قابل اعتماد بودن، تواضع و شایستگی، توان رهبر بودن را چند برابر میکند.
اگر میخواهید بدون داشتن عنوان، رهبری کنید، ابتدا با کسی باشید که دیگران بتوانند روی او حساب کنند، بهویژه زمانی که اوضاع پیچیده و نامنظم است.
برخی از مورد اعتمادترین افراد در جمع، کمترین صحبت را میکنند.
شما این نوع افراد را میشناسید:
🔹️حرفشان را عملی میکنند و به قول خود پایبندند
🔸️قبل از صحبت کردن با دقت گوش میدهند
🔹️برای دیگران فضا ایجاد میکنند بدون اینکه دنبال اعتبار باشند
🔸️آنها در میان آشوب، آرامش میآورند. نیازی ندارند قدرت یا نفوذ خود را نشان دهند.
آنها این اعتماد را به دست آوردهاند.
اغلب ما رهبر بودن را با کاریزما، دیدهشدن یا جسارت مرتبط میکنیم، اما قابل اعتماد بودن، تواضع و شایستگی، توان رهبر بودن را چند برابر میکند.
اگر میخواهید بدون داشتن عنوان، رهبری کنید، ابتدا با کسی باشید که دیگران بتوانند روی او حساب کنند، بهویژه زمانی که اوضاع پیچیده و نامنظم است.
4️⃣ Outbox Pattern 📤📦(بخش چهارم)
ءOutbox Pattern با ذخیرهکردن Eventها در یک جدول دیتابیس (Outbox) در همان Transaction تغییرات دادههای بیزینسی، انتشار قابلاعتماد Eventها را تضمین میکند.
سپس یک فرآیند جداگانه، Eventها را از Outbox خوانده و به Message Broker منتشر میکند.
به این شکل تضمین میشود که Eventها اگر و فقط اگر Transaction بیزینسی موفق باشد منتشر شوند.
🔧 How it works:
🔹️وقتی یک سرویس داده بیزینسی را تغییر میدهد، همزمان رکورد Event را در جدول Outbox و در همان Transaction دیتابیس درج میکند
🔸️ءTransaction دیتابیس، Atomicity بین تغییر داده و ایجاد Event را تضمین میکند
🔹️یک فرآیند Background یا Worker بهصورت مداوم جدول Outbox را برای Eventهای منتشرنشده بررسی میکند
🔸️این فرآیند Eventها را خوانده و به Message Broker ارسال میکند
🔹️بعد از انتشار موفق، Event بهعنوان پردازششده علامتگذاری یا از Outbox حذف میشود
🔸️اگر انتشار شکست بخورد، Event در Outbox باقی میماند و بهصورت خودکار Retry میشود
🔹️این الگو مشکل Dual-Write (ذخیره داده موفق، اما انتشار Event ناموفق) را حذف میکند
🔸️ءEventها حداقل یکبار (At-Least-Once) منتشر میشوند و Consumerها باید Duplicateها را مدیریت کنند
✅ Benefits:
• تضمین میکند Eventها فقط در صورت موفقیت Transaction بیزینسی منتشر شوند
• مشکل Dual-Write بین دیتابیس و Message Broker را بهطور کامل حذف میکند
• یک Audit Trail قابلاعتماد از تمام Eventهای سیستم در دیتابیس فراهم میکند
• امکان Event Replay و Recovery با نگهداشتن تاریخچه Eventها
❌ Drawbacks:
• ایجاد Eventual Consistency چون Eventها بلافاصله منتشر نمیشوند و Polling دارند
• نیاز به زیرساخت اضافی برای Outbox Processor و مانیتورینگ آن
• ایجاد سربار عملکردی بهدلیل نوشتنهای اضافی در دیتابیس و Polling
• نیاز به مدیریت Duplicate Eventها در Consumer (میتوان از InBox Pattern + Idempotence استفاده کرد)
🎯 Use cases:
🔹️ءMicroservices که باید انتشار Event بعد از تغییر دادهها را قطعاً تضمین کنند
🔹️سیستمهای Event Sourcing که هر تغییر وضعیت باید بهعنوان Event ثبت شود
🔹️هر سناریویی که Consistency بین سرویسها حیاتی است و از دست رفتن پیام غیرقابلقبول است
🧠 4️⃣2️⃣ سؤال مهم مصاحبه برای Software Architect
اگر قراره در نقش Software Architect مصاحبه بدی (یا مصاحبه بگیری)، این سؤالها فقط دانش فنی رو نمیسنجن؛
بلکه طرز فکر معماری، تصمیمگیری و تجربهی واقعی تو رو محک میزنن.
1️⃣ چطور بین Monolith، Modular Monolith و Microservices برای یک سیستم جدید تصمیم میگیری؟
2️⃣ ءTrade-off بین Layered Architecture، Vertical Slice و Hexagonal Architecture چیه؟
3️⃣ چطور اصل Principle of Least Surprise رو در طراحی کامپوننتها رعایت میکنی؟
4️⃣ موقع Scale کردن سیستم، چطور جلوی Accidental Complexity رو میگیری؟
5️⃣ چه زمانی Synchronous و چه زمانی Asynchronous Communication رو انتخاب میکنی؟
6️⃣ چطور Idempotent Operation طراحی میکنی وقتی سیستم Retry داره؟
7️⃣ در یک Workflow با throughput بالا، چطور از Race Condition جلوگیری میکنی؟
8️⃣ چه زمانی از Queue، چه زمانی از Stream و چه زمانی از Direct Call استفاده میکنی؟
9️⃣ نقش Saga Pattern در workflowهای طولانی چیه؟
🔟 چطور سیستم رو برای Exactly-once یا Effectively-once processing طراحی میکنی؟
1️⃣1️⃣ با Partial Failure در سیستمهای توزیعشده چطور برخورد میکنی؟
2️⃣1️⃣ چه Patternهایی به حفظ Consistency بین چند سرویس کمک میکنن؟
3️⃣1️⃣ چطور سیستمی طراحی میکنی که در برابر Failure وابستگیهای خارجی دوام بیاره؟
4️⃣1️⃣ رویکردت برای تشخیص و ایزوله کردن سرویسهای کند چیه؟
5️⃣1️⃣ چطور Caching Layer (L1/L2) طراحی میکنی که هم Stale Data نداشته باشه هم Thundering Herd ایجاد نکنه؟
6️⃣1️⃣ چه نشانههایی میگه سیستم به Vertical Scaling نیاز داره یا Horizontal Scaling؟
7️⃣1️⃣ چطور Read-heavy workload طراحی میکنی برای بیشترین throughput؟
8️⃣1️⃣ چه Patternهایی باعث کاهش Load دیتابیس میشن بدون ضربه زدن به Consistency؟
9️⃣1️⃣ با مشکل Hot Partition چطور برخورد میکنی؟
0️⃣2️⃣ سیستمهای با Write Volume بالا رو چطور مدیریت میکنی؟
1️⃣2️⃣ چطور بین Relational Database و Document Database انتخاب میکنی؟
2️⃣2️⃣ چه زمانی Distributed Transaction و چه زمانی Eventual Consistency؟
3️⃣2️⃣ چطور یک سیستم Audit-friendly طراحی میکنی بدون اینکه Performance نابود بشه؟
4️⃣2️⃣ استراتژیت برای Schema Evolution بدون Downtime چیه؟
🎯 جمعبندی
این سؤالها دنبال جواب کتابی نیستن.
مصاحبهکننده میخواد بدونه:
• چطور فکر میکنی
• چطور تصمیم میگیری
• چطور با Trade-offها کنار میای
اگه بتونی پشت هر جواب، تجربه یا منطق داشته باشی، یعنی واقعاً Architect هستی نه فقط عنوانش رو داری.
چگونه با GitHub Actions و NET. یک CI/CD Pipeline بسازیم
آیا میخواهید فرآیند توسعه نرمافزار خود را سادهتر کنید و چرخههای انتشار را سریعتر انجام دهید؟ 🚀
تصور کنید بتوانید با هر تغییر کد، برنامههای NET. خود را بهصورت خودکار build، test و deploy کنید.
با CI/CD میتوانید بهطور قابلتوجهی کارهای دستی را کاهش دهید و تمرکز بیشتری روی ساخت نرمافزار داشته باشید، در نتیجه انتشارهای سریعتر و قابلاعتمادتری خواهید داشت.
و شروع CI/CD هیچوقت به این اندازه آسان نبوده است.
ءGitHub Actions کاملاً رایگان و ساده برای استفاده هستند. ✅
بنابراین، در این مطلب موارد زیر را بررسی میکنیم:
• معرفی CI/CD و GitHub Actions
• ساخت pipeline برای build و test در NET.
• ساخت pipeline برای deployment روی Azure App Service
ءContinuous Integration و Continuous Delivery چیست؟
قبل از اینکه به GitHub Actions بپردازیم، سعی میکنم بهصورت خلاصه توضیح بدهم CI/CD چیست.
ءCI/CD روشی است برای افزایش دفعات تحویل قابلیتهای جدید، با اضافه کردن اتوماسیون به workflow توسعه نرمافزار.
ءContinuous Integration یا «CI» به فرآیند خودکار همگامسازی کد جدید با repository اشاره دارد. هر تغییر جدید در کد برنامه بلافاصله build، test و merge میشود.
ءContinuous Delivery یا Deployment یا «CD» به خودکارسازی بخش deployment از workflow اشاره دارد. زمانی که تغییری ایجاد میکنید که در repository merge میشود، این مرحله مسئول deploy کردن آن تغییرات روی محیط production (یا هر محیط دیگری) است.
ءContinuous Integration با GitHub Actions
اگر از GitHub استفاده میکنید، شروع Continuous Integration هیچوقت به این راحتی نبوده است.
میتوانید از GitHub Actions برای خودکارسازی pipeline مربوط به build، test و deployment استفاده کنید. میتوانید workflowهایی بسازید که هر commit روی repository شما را build و test کنند، یا زمانی که یک tag جدید ساخته میشود، deploy به production انجام دهند.
برای ساخت یک GitHub Action، شما یک workflow مینویسید که هنگام وقوع یک event خاص در repository اجرا شود. نمونهای از این eventها شامل commit روی branch اصلی، ایجاد یک tag یا اجرای دستی workflow است.
در ادامه یک workflow در GitHub Actions برای build و test یک پروژه NET. آورده شده است:
name: Build & Test 🧪
on:
push:
branches:
- main
env:
DOTNET_VERSION: '7.0.x'
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup .NET 📦
uses: actions/setup-dotnet@v3
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
- name: Install dependencies 📂
run: dotnet restore WebApi
- name: Build 🧱
run: dotnet build WebApi --configuration Release --no-restore
- name: Test 🧪
run: dotnet test WebApi --configuration Release --no-build
بیایید ببینیم اینجا دقیقاً چه اتفاقی میافتد 🔍
• تعریف یک event برای trigger کردن workflow ⚡️
• راهاندازی NET SDK. با نسخهای که از env.DOTNET_VERSION خوانده میشود 📦
• ءRestore، build و test کردن پروژه با استفاده از ابزار dotnet CLI 🧪🧱
میتوانید همین امروز این workflow را به repository گیتهاب خود اضافه کنید 🧑💻 و بهمحض commit کردن کد، بازخورد فوری دریافت کنید 🚀
وقتی اجرای workflow به دلیل خطای build یا شکست تستها fail شود ❌، یک ایمیل اعلان دریافت خواهید کرد 📧
ءContinuous Delivery به Azure با GitHub Actions ☁️
ءContinuous Integration نقطه شروع بسیار خوبی برای CI/CD است، اما ارزش واقعی زمانی مشخص میشود که فرآیند deployment را خودکار کنید 🤖
این سناریو را تصور کنید 👇
• شما تغییری در کد ایجاد میکنید ✏️
• ءcommit باعث trigger شدن pipeline deployment میشود 🔄
• چند دقیقه بعد، تغییرات شما در production در دسترس هستند 🌍
معمولاً موضوع کمی پیچیدهتر است، چون باید به پیکربندیها، migration دیتابیس و موارد دیگر هم فکر کنیم ⚙️🗄
اما سعی کنید تصویر کلی را ببینید 🧠
اگر برنامه خود را در cloud اجرا میکنید، مثلاً روی Azure ☁️، به احتمال زیاد یک GitHub Action آماده برای این کار وجود دارد که میتوانید از آن استفاده کنید.
در ادامه یک deployment pipeline آورده شده که من برای انتشار برنامهام روی Azure App Service استفاده میکنم 🚀
name: Publish 🚀
on:
push:
branches:
- main
env:
AZURE_WEBAPP_NAME: web-api
AZURE_WEBAPP_PACKAGE_PATH: './publish'
DOTNET_VERSION: '7.0.x'
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup .NET 📦
uses: actions/setup-dotnet@v3
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
- name: Build and Publish 📂
run: |
dotnet restore WebApi
dotnet build WebApi -c Release --no-restore
dotnet publish WebApi -c Release --no-build
--output '${{ env.AZURE_WEBAPP_PACKAGE_PATH }}'
- name: Deploy to Azure 🌌
uses: azure/webapps-deploy@v2
with:
app-name: ${{ env.AZURE_WEBAPP_NAME }}
publish-profile: ${{ secrets.AZURE_PUBLISH_PROFILE }}
package: '${{ env.AZURE_WEBAPP_PACKAGE_PATH }}'
این workflow شباهت زیادی به workflow قبلی دارد، با این تفاوتها 🔍
اضافه شدن مرحله publish و پیکربندی مسیر خروجی 📤
استفاده از اکشن azure/webapps-deploy@v2 برای deploy روی Azure ☁️
اگر نیاز دارید مقادیر حساس (secret) را بهصورت امن در workflowها استفاده کنید 🔐، میتوانید از GitHub secrets استفاده کنید.
شما میتوانید secrets را در GitHub تعریف کنید و بدون اضافه کردن آنها به source control، در actionها از آنها استفاده کنید.
در workflow مربوط به deployment، من از secrets.AZURE_PUBLISH_PROFILE برای دسترسی به publish profile مربوط به App Service استفاده میکنم 🔑
جمعبندی 🧩
ءContinuous Integration و Continuous Delivery میتوانند فرآیند توسعه شما را متحول کنند ⚡️ و سرعت انتشار تغییرات را بهشدت افزایش دهند 🚀
سعی کنید محاسبه کنید چقدر زمان صرف deployment میکنید ⏱️
تقریباً مطمئنم از میزان زمانی که میتوانید با خودکارسازی ذخیره کنید شگفتزده خواهید شد 😮
نکته خوب اینجاست که معمولاً pipelineهای build و deployment را یکبار راهاندازی میکنید ✅ و سپس در تمام طول عمر پروژه از مزایای آنها استفاده میکنید ♻️
ممنون که خوندید 🙏
امیدوارم مفید بوده باشه ✨
اگر هر مشکلی را برای تیمت حل کنی، در واقع رهبر نیستی. داری جلوی رشدشان را میگیری. 🚧🧠
حتی اگر در ظاهر اینطور به نظر نرسد.
این وسوسه کاملاً طبیعی است، مخصوصاً وقتی باتجربهای.
• مشکل را میبینی 👀
• راهحل را میدانی ✅
• و دلت میخواهد کمک کنی 🤝
اما اگر هر بار خودت وارد عمل شوی، این اتفاقها میافتد:
1️⃣ تیم به تو وابسته میشود
2️⃣ مهارت حل مسئله در آنها رشد نمیکند
3️⃣ بدون اینکه بفهمی، خودت تبدیل به گلوگاه سیستم میشوی ⛔️
بهجای آن، این رویکرد را امتحان کن 👇
راهنمایی بده، نه جواب آماده 🧭
بپرس: «به نظرت خودمون باید چیکار کنیم؟» 🤔
اجازه بده افراد (در حد معقول) تقلا کنند تا رشد کنند 💪
کانتکست و دید کلی بده، نه فقط تصمیم نهایی یا اقدام آماده 📚
حتی اگر در ظاهر اینطور به نظر نرسد.
این وسوسه کاملاً طبیعی است، مخصوصاً وقتی باتجربهای.
• مشکل را میبینی 👀
• راهحل را میدانی ✅
• و دلت میخواهد کمک کنی 🤝
اما اگر هر بار خودت وارد عمل شوی، این اتفاقها میافتد:
1️⃣ تیم به تو وابسته میشود
2️⃣ مهارت حل مسئله در آنها رشد نمیکند
3️⃣ بدون اینکه بفهمی، خودت تبدیل به گلوگاه سیستم میشوی ⛔️
بهجای آن، این رویکرد را امتحان کن 👇
راهنمایی بده، نه جواب آماده 🧭
بپرس: «به نظرت خودمون باید چیکار کنیم؟» 🤔
اجازه بده افراد (در حد معقول) تقلا کنند تا رشد کنند 💪
کانتکست و دید کلی بده، نه فقط تصمیم نهایی یا اقدام آماده 📚
ءServer-Sent Events در ASP.NET Core و NET 10. 📡🚀
بهروزرسانیهای Real-time دیگر یک قابلیت «خوب است داشته باشیم» نیستند.
بیشتر رابطهای کاربری مدرن انتظار دارند به نوعی جریان زندهای از داده را از سمت سرور دریافت کنند.
سالها در اکوسیستم NET. ، پاسخ پیشفرض برای این نیاز SignalR بوده است.
در حالی که SignalR فوقالعاده قدرتمند است، اما برای سناریوهای سادهتر، داشتن گزینههای دیگر هم بسیار مفید است ✨
با انتشار ASP.NET Core 10، بالاخره یک API بومی و سطحبالا برای Server-Sent Events (SSE) داریم 🎉
این قابلیت فاصله بین polling سادهی HTTP و WebSocketهای دوطرفه از طریق SignalR را پر میکند.
🤔 چرا SSE بهجای SignalR؟
ءSignalR یک ابزار بسیار قدرتمند است که WebSockets، Long Polling و SSE را بهصورت خودکار مدیریت میکند و یک کانال ارتباطی دوطرفه (Full-Duplex) فراهم میکند.
اما این قدرت، هزینههایی هم دارد:
• استفاده از یک پروتکل مشخص (Hubs)
• نیاز به کتابخانهی سمت کلاینت
• نیاز به Sticky Session یا Backplane (مثل Redis) برای مقیاسپذیری
ءSSE متفاوت است، چون:
➡️ یکطرفه (Unidirectional) است: مخصوص استریم داده از سرور به کلاینت
🌐 ءHTTP بومی است: فقط یک درخواست استاندارد HTTP با text/event-stream
🔄 ءReconnect خودکار دارد: مرورگرها بهصورت Native با API به نام EventSource اتصال مجدد را مدیریت میکنند
🪶 سبک و ساده است: بدون کتابخانههای سنگین سمت کلاینت یا منطق handshake پیچیده
✨ سادهترین Endpoint برای Server-Sent Events
زیبایی API جدید SSE در NET 10.، سادگی آن است.
میتوانید از Results.ServerSentEvents استفاده کنید تا یک جریان از رویدادها را از هر <IAsyncEnumerable<T برگردانید.
از آنجایی که IAsyncEnumerable نمایانگر یک جریان داده است که در طول زمان میرسد، سرور متوجه میشود که باید اتصال HTTP را باز نگه دارد، بهجای اینکه بعد از اولین «chunk» آن را ببندد 🔓
در اینجا یک مثال مینیمال از یک Endpoint برای SSE وجود دارد که ثبت سفارشها را بهصورت Real-time استریم میکند 📦📊
app.MapGet("orders/realtime", (
ChannelReader<OrderPlacement> channelReader,
CancellationToken cancellationToken) =>
{
// 1. ReadAllAsync یک IAsyncEnumerable برمیگرداند
// 2. Results.ServerSentEvents به مرورگر میگوید: «این اتصال را باز نگه دار»
// 3. به محض ورود دادهی جدید به Channel، داده به کلاینت Push میشود
return Results.ServerSentEvents(
channelReader.ReadAllAsync(cancellationToken),
eventType: "orders");
});🔍 وقتی کلاینت این Endpoint را صدا میزند چه اتفاقی میافتد؟
🔹️سرور هدر Content-Type: text/event-stream را ارسال میکند 📬
🔸️اتصال باز میماند و در حالت انتظار برای داده قرار میگیرد ⏳
🔹️به محض اینکه اپلیکیشن شما یک سفارش جدید داخل Channel قرار دهد:
🔸️ءIAsyncEnumerable آن آیتم را yield میکند
🔹️ءNET. بلافاصله آن را از طریق همان اتصال HTTP باز به مرورگر ارسال میکند ⚡️
این یک روش فوقالعاده بهینه برای پیادهسازی اعلانهای Push است، بدون سربار یک پروتکل stateful.
🧠 نکتهی پایانی
در این مثال، از Channel فقط بهعنوان یک ابزار استفاده شده است.
در یک اپلیکیشن واقعی، ممکن است:
یک Background Service داشته باشید 🛠
به یک صف پیام مثل RabbitMQ یا Azure Service Bus گوش دهید 📮
یا به تغییرات دیتابیس واکنش نشان دهید 🗄
و سپس رویدادهای جدید را داخل Channel قرار دهید تا کلاینتهای متصل آنها را مصرف کنند.
مدیریت رویدادهای ازدسترفته (Handling Missed Events) 🔄📡
ءEndpoint سادهای که همین الان ساختیم عالی است، اما یک ضعف مهم دارد:
تابآوری (Resilience) ندارد.
یکی از بزرگترین چالشها در استریمهای Real-time، قطع شدن اتصال است.
تا زمانی که مرورگر بهصورت خودکار دوباره وصل شود، ممکن است چندین رویداد ارسال شده و از دست رفته باشند 😕
برای حل این مشکل، SSE یک مکانیزم داخلی دارد:
هدر Last-Event-ID.
وقتی مرورگر reconnect میشود، این ID را دوباره برای سرور ارسال میکند.
در NET 10. میتوانیم از نوع <SseItem<T استفاده کنیم تا داده را بههمراه متادیتاهایی مثل ID و retry interval بستهبندی کنیم.
با ترکیب یک OrderEventBuffer سادهی درونحافظهای و مقدار Last-Event-ID که مرورگر ارسال میکند، میتوانیم رویدادهای ازدسترفته را هنگام reconnect دوباره ارسال کنیم 🔁
app.MapGet("orders/realtime/with-replays", (
ChannelReader<OrderPlacement> channelReader,
OrderEventBuffer eventBuffer,
[FromHeader(Name = "Last-Event-ID")] string? lastEventId,
CancellationToken cancellationToken) =>
{
async IAsyncEnumerable<SseItem<OrderPlacement>> StreamEvents()
{
// 1. بازپخش رویدادهای ازدسترفته از buffer
if (!string.IsNullOrWhiteSpace(lastEventId))
{
var missedEvents = eventBuffer.GetEventsAfter(lastEventId);
foreach (var missedEvent in missedEvents)
{
yield return missedEvent;
}
}
// 2. استریم رویدادهای جدید بهمحض ورود به Channel
await foreach (var order in channelReader.ReadAllAsync(cancellationToken))
{
var sseItem = eventBuffer.Add(order); // Buffer یک ID یکتا اختصاص میدهد
yield return sseItem;
}
}
return TypedResults.ServerSentEvents(StreamEvents(), "orders");
});فیلتر کردن Server-Sent Events بر اساس کاربر 👤🔐
ءSSE روی HTTP استاندارد ساخته شده است.
چون یک درخواست GET معمولی است، زیرساخت فعلی شما بدون تغییر کار میکند:
ءSecurity 🔐: میتوانید JWT را بهصورت عادی در هدر Authorization ارسال کنید
ءUser Context 👤: میتوانید به HttpContext.User دسترسی داشته باشید و استریم را بر اساس UserId فیلتر کنید
→ فقط دادههایی که متعلق به همان کاربر هستند ارسال میشوند
مثال یک Endpoint SSE که فقط سفارشهای کاربر لاگینشده را استریم میکند:
app.MapGet("orders/realtime", (
ChannelReader<OrderPlacement> channelReader,
IUserContext userContext, // کانتکست تزریقشده شامل اطلاعات کاربر
CancellationToken cancellationToken) =>
{
// UserId از JWT توسط IUserContext استخراج میشود
var currentUserId = userContext.UserId;
async IAsyncEnumerable<OrderPlacement> GetUserOrders()
{
await foreach (var order in channelReader.ReadAllAsync(cancellationToken))
{
// فقط دادههای متعلق به کاربر احراز هویتشده ارسال میشود
if (order.CustomerId == currentUserId)
{
yield return order;
}
}
}
return Results.ServerSentEvents(GetUserOrders(), "orders");
})
.RequireAuthorization(); // Authorization استاندارد ASP.NET Core⚠️ نکته مهم:
وقتی یک پیام داخل Channel نوشته میشود، به تمام کلاینتهای متصل broadcast میشود.
این رفتار برای استریمهای per-user ایدهآل نیست.
در محیط production، احتمالاً به راهکار قویتری نیاز دارید.
مصرف Server-Sent Events در JavaScript 🌐🧠
در سمت کلاینت، نیازی به نصب حتی یک پکیج npm هم ندارید 🙌 API بومی مرورگر یعنی EventSource تمام کارهای سنگین را انجام میدهد،
از جمله reconnect خودکار و ارسال Last-Event-ID.
const eventSource = new EventSource('/orders/realtime/with-replays');
// گوش دادن به event type مشخصشده در C#
eventSource.addEventListener('orders', (event) => {
const payload = JSON.parse(event.data);
console.log(New Order ${event.lastEventId}:, payload.data);
});
// وقتی اتصال برقرار میشود
eventSource.onopen = () => {
console.log('Connection opened');
};
// پیامهای عمومی (در صورت وجود)
eventSource.onmessage = (event) => {
console.log('Received message:', event);
};
// مدیریت خطا و reconnect
eventSource.onerror = () => {
if (eventSource.readyState === EventSource.CONNECTING) {
console.log('Reconnecting...');
}
};جمعبندی 🧩✨
ءSSE در NET 10. یک نقطهی تعادل عالی است برای بهروزرسانیهای ساده و یکطرفه مثل:
داشبوردها 📊
نوتیفیکیشنها 🔔
ءProgress barها ⏳
سبک است، مبتنی بر HTTP است و بهراحتی با middlewareهای امنیتی فعلی شما ایمن میشود.
با این حال، SignalR همچنان انتخاب قدرتمند و battle-tested برای ارتباطهای دوطرفهی پیچیده یا مقیاس بسیار بالا (با backplane) باقی میماند.
هدف جایگزینی SignalR نیست ❌
هدف این است که برای کارهای ساده، ابزار سادهتری داشته باشید 🛠
سبکترین ابزاری را انتخاب کنید که مشکل شما را حل میکند.
امیدوارم مفید بوده باشد.😊
ءSSE در NET 10. یک نقطهی تعادل عالی است برای بهروزرسانیهای ساده و یکطرفه مثل:
داشبوردها 📊
نوتیفیکیشنها 🔔
ءProgress barها ⏳
سبک است، مبتنی بر HTTP است و بهراحتی با middlewareهای امنیتی فعلی شما ایمن میشود.
با این حال، SignalR همچنان انتخاب قدرتمند و battle-tested برای ارتباطهای دوطرفهی پیچیده یا مقیاس بسیار بالا (با backplane) باقی میماند.
هدف جایگزینی SignalR نیست ❌
هدف این است که برای کارهای ساده، ابزار سادهتری داشته باشید 🛠
سبکترین ابزاری را انتخاب کنید که مشکل شما را حل میکند.
امیدوارم مفید بوده باشد.😊