#KilledByMicrosoft
Microsoft, like Google has also killed many projects over the years.
At the link below you can see the list of Microsoft Cemetery.
The last interesting option on this list is Skype for Businesses, which is still alive
But in 2025 it will join Microsoft Digital Cemetery, where you have to wonder what the hell Microsoft is thinking.
Have a look at:
https://killedbymicrosoft.info/
@fullStackDevs
Microsoft, like Google has also killed many projects over the years.
At the link below you can see the list of Microsoft Cemetery.
The last interesting option on this list is Skype for Businesses, which is still alive
But in 2025 it will join Microsoft Digital Cemetery, where you have to wonder what the hell Microsoft is thinking.
Have a look at:
https://killedbymicrosoft.info/
@fullStackDevs
#Redis
#RedisGraph
RedisGraph 2.8 is out:
RedisGraph is a high-performance, memory-first graph data structure for Redis
Major new features in RedisGraph 2.8
🔲 Richer graph model
▫️Multi-labeled nodes
🔲Enhanced querying capabilities
▫️Enhanced full-text search
▫️Supporting more Cypher constructs, functions, and operators
🔲Performance improvements
▫️Indexes over relationship properties
▫️Delta Matrices
▫️Controllable node creation buffer
Read more here : RedisGraph
Graph database using Redis Stack
@fullStackDevs
#RedisGraph
RedisGraph 2.8 is out:
RedisGraph is a high-performance, memory-first graph data structure for Redis
Major new features in RedisGraph 2.8
🔲 Richer graph model
▫️Multi-labeled nodes
🔲Enhanced querying capabilities
▫️Enhanced full-text search
▫️Supporting more Cypher constructs, functions, and operators
🔲Performance improvements
▫️Indexes over relationship properties
▫️Delta Matrices
▫️Controllable node creation buffer
Read more here : RedisGraph
Graph database using Redis Stack
@fullStackDevs
#Browser
#shortcuts
🎯 useful browser for software engineering
for example, write js.new in the browser's address bar, the JavaScript sandbox will open.
دامنه های مفید برای مهندسی نرم افزار
برای مثال، js.new را در نوار آدرس مرورگر بنویسید و اینتر کنید،sandbox جاوا اسکریپت باز خواهد شد یا مثلا github.new بزنید و لاگین باشید میره تو repository و new را باز میکنه براتون !
✏️ Daniele serfilippi
@fullStackDevs
#shortcuts
🎯 useful browser for software engineering
for example, write js.new in the browser's address bar, the JavaScript sandbox will open.
دامنه های مفید برای مهندسی نرم افزار
برای مثال، js.new را در نوار آدرس مرورگر بنویسید و اینتر کنید،sandbox جاوا اسکریپت باز خواهد شد یا مثلا github.new بزنید و لاگین باشید میره تو repository و new را باز میکنه براتون !
✏️ Daniele serfilippi
@fullStackDevs
#HTTPS
What is so important about HTTPS that, these days, most websites require it?
How does HTTPS work?
it will answer these questions in this video.
https://www.youtube.com/watch?v=67kItGjvRs0
https://blog.bytebytego.com/
@fullStackDevs
What is so important about HTTPS that, these days, most websites require it?
How does HTTPS work?
it will answer these questions in this video.
https://www.youtube.com/watch?v=67kItGjvRs0
https://blog.bytebytego.com/
@fullStackDevs
#frontend #css
Fancy Border Radius
an advanced css feature
upto 8 values for border-radius property
customize your fancy shapes in the link below :
🔗 https://9elements.github.io/fancy-border-radius/
@fullStackDevs
Fancy Border Radius
an advanced css feature
upto 8 values for border-radius property
customize your fancy shapes in the link below :
🔗 https://9elements.github.io/fancy-border-radius/
@fullStackDevs
#GenericAttribute
In C#, if you want to pass the type to an attribute, you can use the 'typeof' expression.
However, there is no way to constrain what types are allowed to be passed.
C# 11 allows generic attributes.
@fullStackDevs
In C#, if you want to pass the type to an attribute, you can use the 'typeof' expression.
However, there is no way to constrain what types are allowed to be passed.
C# 11 allows generic attributes.
@fullStackDevs
#json
#orderAttribute
In .NET 6, the JsonPropertyOrderAttribute has been added to System.Text.Json.
It allows controlling the serialization order of properties.
Previously, the serialization order was determined by reflection order.
✏️ Oleg Kyrylchuk
@fullStackDevs
#orderAttribute
In .NET 6, the JsonPropertyOrderAttribute has been added to System.Text.Json.
It allows controlling the serialization order of properties.
Previously, the serialization order was determined by reflection order.
✏️ Oleg Kyrylchuk
@fullStackDevs
Forwarded from Web Devs
#AOP
🔰AOP
🔹در این پست میخواهیم AOP رو مورد بررسی قرار دهیم.
در توسعه نرم افزار AOP یک مدل توسعه نرم افزار است که برای تکمیل OOP ( برنامه نویسی شی گرا) به وسیله جدا سازی concern ها اپلیکیشن برای رسیدن به ماژولاریتی استفاده میشود.
جداسازی concern ها با دسته بندی feature ها و behavior های اپلیکیشن به بخش های قابل مدیریت که هر کدام هدف خاصی دارن، نگهداری آنرا آسان تر میکند .
البته به کمک OOP امکان modularizing کردن concern ها به متد های مجزا و کلاس ها و package ها وجود دارد.
❇️ Aspect-Oriented Programming (AOP)
🔹یک تکنیک رایج برای ساخت راه کارهایی قابل استفاده در سراسر اپلیکیشن است که در طی توسعه نرم افزار، جداسازی منطق برنامه و تسک های قابل تکرار را تسهیل میکند.
مانند (input validation, logging, error handling, security)
🔸معماری یک اپلیکیشن اگر به خوبی طراحی شده باشد دارای لایه های متفاوت باید باشد و concern های مختلف در این معماری فقط در صورت نیاز باهم در ارتباط باشند.
▪️ فرض کنید یک اپلیکیشن loosely couple و maintainable طراحی کرده اید اما در میان راه نیازمند یه سری موارد هستید که ممکن است، نتوان آنها را در معماری به درستی جایگذاری کرد مانند :
▫️نیازمند سیستم authentication ای هستید برای بررسی احراز هویت کاربر قبل از هر کوئری به دیتا بیس
▫️ یا نیازمند است دیتاها قبل از insert در دیتابیس اعتبار سنجی شوند
▫️اپلیکیشن باید برای عملیات های حساس و مهم logging را انجام دهد.
▫️اپلیکیشن باید دارای یک سیستم debugging log جهت بررسی ok بودن عملیات ها باشد.
▫️اپلیکیشن باید دارای یک سیستم بررسی پرفورمنس باشد تا عملکرد عملیات های اپلیکیشن را بررسی کند.
🔸هر کدام از نیازمند های گفته شده نیازمند حجم زیادی از checking و کد میباشند.
علاوه بر آن امکان code duplication نیز وجود دارد و مجبورید کد های تکراری زیادی را در بخش های مختلف سیستم اضافه کنید. که نقض اصل DRY است و نگهداری اپلیکیشن را سخت میکند.
علاوه بر اینها هر تغییر کوچک در این نیازمندی ها باعث تغییرات عظیم در اپلیکیشن میشود.
🔹مواقعی که مجبور باشیم چنین الزاماتی را به اپلیکیشن خود اضافه کنیم، سوالی پیش می اید :
🔻 "چرا اصلا خود کامپایلر نمیتواند این کدهایی که در جاهای مختلف تکرار میشوند را اضافه کند ؟"
خبر خوب این است که چیزی به نام "aspect-oriented programming (AOP)" وجود دارد که general کد ها را از aspect ها در سراسر boundary های یک آبجکت یا یک لایه جدا میکند.
برای مثال logging در اپلیکیشن، وابسته به هیچ یک از لایه های اپلیکیشن نیست، بلکه باید در سراسر اپلیکیشن وجود داشته باشد و در همه جای برنامه بتوان آنرا اضافه کرد.
🔹از مزیت های AOP این است که فقط نیاز است نگران یک جای کد باشید. در یک جا کد بنویسید و آنرا در هرجایی که نیاز است اعمال کنید.
❇️ موارد استفاده از AOP بسیار زیاد است مانند :
▫️پیاده سازی logging در اپلیکیشن
▫️استفاده از authentication قبل از یک عملیات (اجازه دسترسی به کاربرانی که احراز هویت شده اند)
▫️پیاده سازی اعتبار سنجی و همچنین پیاده سازی سیستم اعلانی که با تغییر مقدار یک پراپرتی ایونتی را raise میکند.
▫️تغییر رفتار برخی متد ها.
🔸همانطور که میبینید AOP کاربرد های فراوانی دارد اما باید با احتیاط از آن استفاده کنید.
چون استفاده از آن برخی کد ها را در حالی که وجود دارند Hide میکند. همچنین aspect ها میتواند دارای باگی باشند یا تاثیر بسزایی در پرفورمنس اپلیکیشن داشته باشد. هزینه یک باگ جزئی در یک aspect ممکن است ساعت ها دیباگ کردن باشد. در نتیجه اگر aspect تان در جاهای دیگری ار اپلیکیشن استفاده نمیشود بهتر است مستقیما آنرا وارد کدتان کنید.
@fullStackDevs
🔰AOP
🔹در این پست میخواهیم AOP رو مورد بررسی قرار دهیم.
در توسعه نرم افزار AOP یک مدل توسعه نرم افزار است که برای تکمیل OOP ( برنامه نویسی شی گرا) به وسیله جدا سازی concern ها اپلیکیشن برای رسیدن به ماژولاریتی استفاده میشود.
جداسازی concern ها با دسته بندی feature ها و behavior های اپلیکیشن به بخش های قابل مدیریت که هر کدام هدف خاصی دارن، نگهداری آنرا آسان تر میکند .
البته به کمک OOP امکان modularizing کردن concern ها به متد های مجزا و کلاس ها و package ها وجود دارد.
❇️ Aspect-Oriented Programming (AOP)
🔹یک تکنیک رایج برای ساخت راه کارهایی قابل استفاده در سراسر اپلیکیشن است که در طی توسعه نرم افزار، جداسازی منطق برنامه و تسک های قابل تکرار را تسهیل میکند.
مانند (input validation, logging, error handling, security)
🔸معماری یک اپلیکیشن اگر به خوبی طراحی شده باشد دارای لایه های متفاوت باید باشد و concern های مختلف در این معماری فقط در صورت نیاز باهم در ارتباط باشند.
▪️ فرض کنید یک اپلیکیشن loosely couple و maintainable طراحی کرده اید اما در میان راه نیازمند یه سری موارد هستید که ممکن است، نتوان آنها را در معماری به درستی جایگذاری کرد مانند :
▫️نیازمند سیستم authentication ای هستید برای بررسی احراز هویت کاربر قبل از هر کوئری به دیتا بیس
▫️ یا نیازمند است دیتاها قبل از insert در دیتابیس اعتبار سنجی شوند
▫️اپلیکیشن باید برای عملیات های حساس و مهم logging را انجام دهد.
▫️اپلیکیشن باید دارای یک سیستم debugging log جهت بررسی ok بودن عملیات ها باشد.
▫️اپلیکیشن باید دارای یک سیستم بررسی پرفورمنس باشد تا عملکرد عملیات های اپلیکیشن را بررسی کند.
🔸هر کدام از نیازمند های گفته شده نیازمند حجم زیادی از checking و کد میباشند.
علاوه بر آن امکان code duplication نیز وجود دارد و مجبورید کد های تکراری زیادی را در بخش های مختلف سیستم اضافه کنید. که نقض اصل DRY است و نگهداری اپلیکیشن را سخت میکند.
علاوه بر اینها هر تغییر کوچک در این نیازمندی ها باعث تغییرات عظیم در اپلیکیشن میشود.
🔹مواقعی که مجبور باشیم چنین الزاماتی را به اپلیکیشن خود اضافه کنیم، سوالی پیش می اید :
🔻 "چرا اصلا خود کامپایلر نمیتواند این کدهایی که در جاهای مختلف تکرار میشوند را اضافه کند ؟"
خبر خوب این است که چیزی به نام "aspect-oriented programming (AOP)" وجود دارد که general کد ها را از aspect ها در سراسر boundary های یک آبجکت یا یک لایه جدا میکند.
برای مثال logging در اپلیکیشن، وابسته به هیچ یک از لایه های اپلیکیشن نیست، بلکه باید در سراسر اپلیکیشن وجود داشته باشد و در همه جای برنامه بتوان آنرا اضافه کرد.
🔹از مزیت های AOP این است که فقط نیاز است نگران یک جای کد باشید. در یک جا کد بنویسید و آنرا در هرجایی که نیاز است اعمال کنید.
❇️ موارد استفاده از AOP بسیار زیاد است مانند :
▫️پیاده سازی logging در اپلیکیشن
▫️استفاده از authentication قبل از یک عملیات (اجازه دسترسی به کاربرانی که احراز هویت شده اند)
▫️پیاده سازی اعتبار سنجی و همچنین پیاده سازی سیستم اعلانی که با تغییر مقدار یک پراپرتی ایونتی را raise میکند.
▫️تغییر رفتار برخی متد ها.
🔸همانطور که میبینید AOP کاربرد های فراوانی دارد اما باید با احتیاط از آن استفاده کنید.
چون استفاده از آن برخی کد ها را در حالی که وجود دارند Hide میکند. همچنین aspect ها میتواند دارای باگی باشند یا تاثیر بسزایی در پرفورمنس اپلیکیشن داشته باشد. هزینه یک باگ جزئی در یک aspect ممکن است ساعت ها دیباگ کردن باشد. در نتیجه اگر aspect تان در جاهای دیگری ار اپلیکیشن استفاده نمیشود بهتر است مستقیما آنرا وارد کدتان کنید.
@fullStackDevs
#LINQ
C# LINQ Mind Map
Here a simple mind map for beginners to get an overview over the waste variety off LINQ operations grouped into categories.
This mind map is taken from my free eBook: "LINQ explained with sketches - the eBook" - which I update regularly. I hope that gives a better picture, especially for beginners.
The download link for the free ebook: https://lnkd.in/dG3K8vCW
✏️ Steven Giesel
@fullStackDevs
C# LINQ Mind Map
Here a simple mind map for beginners to get an overview over the waste variety off LINQ operations grouped into categories.
This mind map is taken from my free eBook: "LINQ explained with sketches - the eBook" - which I update regularly. I hope that gives a better picture, especially for beginners.
The download link for the free ebook: https://lnkd.in/dG3K8vCW
✏️ Steven Giesel
@fullStackDevs
#StringBuilder
⁉️ از " +" در حلقه های الحاق رشته ها استفاده نکنید!
1️⃣ در سی شارپ string بصورت immutable پیاده سازی شده است.(وقتی شما یک متغیر string تعریف و مقداردهی می کنید، دیگر نمی توانید آنرا تغییر دهید)
2️⃣ رشته ها بصورت یک آرایه از کاراکترها ذخیره میشوند که با هم کنار قرار گرفتن این کاراکترها، متن مورد نظر شکل می گیرد ( هنگام تعریف آرایه ها باید طول آرایه مشخص شود و بعد از تعریف آن، طول آرایه قابل اضافه و کم کردن نیست)
3️⃣ هنگاهی که مقدار string را میخواهید تغییر دهید، و از طرفی چون مقدار قبلی که یک آرایه از کاراکترها است و قابل تغییر نیست، بنابراین مقدار جدید را در یک آرایه جدید کپی می کند و با این کار یک بلوک جدید از خانه های حافظه را به آن اختصاص داده می دهد.
با انجام چندین بار اصلاح یک رشته موجود باعث می شود تخصیص چندگانه حافظه بر عملکرد تأثیر بگذارد.
✅راه حل استفاده از StringBuilder است که یک کلاس رشته mutable است .
این StringBuilder بعد از تغییر یک شیء جدید در حافظه ایجاد نمی کند بلکه dynamically memory برای قرار دادن رشته اصلاح شده ، تخصیص داده میشود.
@fullStackDevs
⁉️ از " +" در حلقه های الحاق رشته ها استفاده نکنید!
1️⃣ در سی شارپ string بصورت immutable پیاده سازی شده است.(وقتی شما یک متغیر string تعریف و مقداردهی می کنید، دیگر نمی توانید آنرا تغییر دهید)
2️⃣ رشته ها بصورت یک آرایه از کاراکترها ذخیره میشوند که با هم کنار قرار گرفتن این کاراکترها، متن مورد نظر شکل می گیرد ( هنگام تعریف آرایه ها باید طول آرایه مشخص شود و بعد از تعریف آن، طول آرایه قابل اضافه و کم کردن نیست)
3️⃣ هنگاهی که مقدار string را میخواهید تغییر دهید، و از طرفی چون مقدار قبلی که یک آرایه از کاراکترها است و قابل تغییر نیست، بنابراین مقدار جدید را در یک آرایه جدید کپی می کند و با این کار یک بلوک جدید از خانه های حافظه را به آن اختصاص داده می دهد.
با انجام چندین بار اصلاح یک رشته موجود باعث می شود تخصیص چندگانه حافظه بر عملکرد تأثیر بگذارد.
✅راه حل استفاده از StringBuilder است که یک کلاس رشته mutable است .
این StringBuilder بعد از تغییر یک شیء جدید در حافظه ایجاد نمی کند بلکه dynamically memory برای قرار دادن رشته اصلاح شده ، تخصیص داده میشود.
@fullStackDevs
Forwarded from Web Devs
#exploring
#static
#Readonly
✳️Const vs Readonly Static
🔹Const
▫️متغیرهایی که به صورت Constant تعریف میشوند باید در هنگام تعریفشان مقدار دهی شوند و دیگر در طی طول عمر برنامه قابل تغییر نیستند. تنها type های داخلی سی شارپ را میتوان به
صورت Constant تعریف کرد. به استثنای (system.object).
تایپ های تعریف شده توسط کاربر شامل کلاس ها،struct ها و یا آرایه ها ، نمیتوانند به صورت Const تعریف شوند.
همچنین سی شارپ از متدها و پراپرتی ها و ایونت ها به صورت Const پشتیبانی نمیکند.
▪️مقادیر در متغیر های Const در
زمان compile-time شناخته و صدا زده میشوند.
▫️از Const زمانی استفاده کنید که مطمئن هستید مقدار یک متغیر در طی طول عمر اپلیکیشن تغییر نخواهد کرد.
هر متغیری که به صورت Const تعریف میشود به صورت غیر صریح static نیز تعریف خواهد شد.
▫️زمانی که کامپایلر با یک
متغیر Const مواجه میشود، مقدار آنرا مستقیما در کد IL تولید شده جایگزین میکند.
🔹Static
▫️یک عضو Static مانند : متغیرها ،متد ها و ... به Type شی تعلق دارند و از طریق نام Type در دسترس خواهد بود.
🔹ReadOnly
▫️یک فیلد ReadOnly در
زمان run-time مقدار آن ارزیابی میشود.به یک فیلد ReadOnly فقط در زمان تعریف و یا در متد سازنده کلاس دربردارنده آن میتوانید مقدار اختصاص دهید.در نتیجه این را میتوان استنباط کرد که می توان برای یک
فیلد ReadOnly در متدهای سازنده مختلف مقادیر متفاوت اختصاص داد.
🔸از اختصاص دادن مقادیر به یک فیلد Readonly برای value type ها
و reference type ها در متد سازنده چند نتیجه میتوان گرفت :
🔻در valueType ها از آنجایی که مستقیما شامل مقادیر خود هستند اگر یک فیلد به صورت ReadOnly تعریف شود immutable نیز خواهد بود.
🔻در referenceType ها که یک رفرنس به داده های خود هستند اگر یک
فیلد refrenceType
بصورت ReadOnly تعریف شود، همیشه باید به یک شی یکسان و از همان نوع اشاره کند و همچنین ReadOnly تعریف کردن آنها باعث جلوگیری از تغییر مقادیر فیلد های آن شی نمیشود.
▪️با این تعاریف به سوال اصلی خود باز میگردیم❕
❇️const vs static readonly
🔹اول از همه باید بدانیم که ویژگی های یک متغیر static readonly چیست⁉️
🔻در جایی غیر از متد سازنده static و یا در هنگام تعریف آن نمیتوانید به آن مقدار اختصاص دهید.(به خاطر readonly بودن آن)
🔻از طریق نام کلاس به آن دسترسی دارید (به خاطر static بودن آن)
🔹با این وجود در نگاه اول یک
متغیر static readonly به یک
متغیر const بسیار شبیه است زیرا یک constant فقط در هنگام تعریف می تواند مقدار بگیرد و در هیچ جای دیگری از کد مقدار آن نمیتواند تغییر کند اما تفاوت در جزئیات نهفته است.
اول از همه بدانید که یک متغیر const به جیزی ارجاع نمیکند (reference) و استفاده ار آن
به معنای hard code کردن مقدار آن میباشد .
✳️ علاوه بر اینها یک تفاوت کلیدی وجود دارد که باید از آن آگاه باشیم .همانطور که پیش تر گفتیم مقادیر یک متغیر const در کد IL جایگرین میشوند.
⚠️ اگر یک متغیر const در اسمبلی A وجود داشته باشد و اسمبلی B از اسمبلی A استفاده کند . اگر یک مقدار جدید به متغیر const موجود در اسمبلی A دهید و دوباره این اسمبلی را recompile کنید و تا زمانی که اسمبلی B را recompile نکرده اید، از همان مقدار قبلی hard code شده برای متغیر const موجود در اسمبلی A استفاده میکند.
خلاصه
🔻متغیرهای static readonly فقط میتوانند در لحظه تعریف شدن یا در متد سازنده static تغییر کنند.
🔻اگر مطمئن هستید و میدانید که مقدار یک متغیر تغییر نمیکند
از const استفاده کنید.
🔻اگر مطمئن نیستید که مقدار یک متغیر تغییر میکند یا خیر و نمیخواهید کلاس دیگری مقدار آنرا تغییر دهد
از read only استفاده کنید.
🔻متغیر های const به صورت غیر صریح static هستند.
🔻اگر میخواهید از طریق نام type به یک عضو آن دسترسی داشته باشید از static استفاده کنید.
منابع 🔍
1 - 2 -3 - 4 - 5 - 6
@FullStackDevs
#static
#Readonly
✳️Const vs Readonly Static
🔹Const
▫️متغیرهایی که به صورت Constant تعریف میشوند باید در هنگام تعریفشان مقدار دهی شوند و دیگر در طی طول عمر برنامه قابل تغییر نیستند. تنها type های داخلی سی شارپ را میتوان به
صورت Constant تعریف کرد. به استثنای (system.object).
تایپ های تعریف شده توسط کاربر شامل کلاس ها،struct ها و یا آرایه ها ، نمیتوانند به صورت Const تعریف شوند.
همچنین سی شارپ از متدها و پراپرتی ها و ایونت ها به صورت Const پشتیبانی نمیکند.
▪️مقادیر در متغیر های Const در
زمان compile-time شناخته و صدا زده میشوند.
▫️از Const زمانی استفاده کنید که مطمئن هستید مقدار یک متغیر در طی طول عمر اپلیکیشن تغییر نخواهد کرد.
هر متغیری که به صورت Const تعریف میشود به صورت غیر صریح static نیز تعریف خواهد شد.
▫️زمانی که کامپایلر با یک
متغیر Const مواجه میشود، مقدار آنرا مستقیما در کد IL تولید شده جایگزین میکند.
🔹Static
▫️یک عضو Static مانند : متغیرها ،متد ها و ... به Type شی تعلق دارند و از طریق نام Type در دسترس خواهد بود.
🔹ReadOnly
▫️یک فیلد ReadOnly در
زمان run-time مقدار آن ارزیابی میشود.به یک فیلد ReadOnly فقط در زمان تعریف و یا در متد سازنده کلاس دربردارنده آن میتوانید مقدار اختصاص دهید.در نتیجه این را میتوان استنباط کرد که می توان برای یک
فیلد ReadOnly در متدهای سازنده مختلف مقادیر متفاوت اختصاص داد.
🔸از اختصاص دادن مقادیر به یک فیلد Readonly برای value type ها
و reference type ها در متد سازنده چند نتیجه میتوان گرفت :
🔻در valueType ها از آنجایی که مستقیما شامل مقادیر خود هستند اگر یک فیلد به صورت ReadOnly تعریف شود immutable نیز خواهد بود.
🔻در referenceType ها که یک رفرنس به داده های خود هستند اگر یک
فیلد refrenceType
بصورت ReadOnly تعریف شود، همیشه باید به یک شی یکسان و از همان نوع اشاره کند و همچنین ReadOnly تعریف کردن آنها باعث جلوگیری از تغییر مقادیر فیلد های آن شی نمیشود.
▪️با این تعاریف به سوال اصلی خود باز میگردیم❕
❇️const vs static readonly
🔹اول از همه باید بدانیم که ویژگی های یک متغیر static readonly چیست⁉️
🔻در جایی غیر از متد سازنده static و یا در هنگام تعریف آن نمیتوانید به آن مقدار اختصاص دهید.(به خاطر readonly بودن آن)
🔻از طریق نام کلاس به آن دسترسی دارید (به خاطر static بودن آن)
🔹با این وجود در نگاه اول یک
متغیر static readonly به یک
متغیر const بسیار شبیه است زیرا یک constant فقط در هنگام تعریف می تواند مقدار بگیرد و در هیچ جای دیگری از کد مقدار آن نمیتواند تغییر کند اما تفاوت در جزئیات نهفته است.
اول از همه بدانید که یک متغیر const به جیزی ارجاع نمیکند (reference) و استفاده ار آن
به معنای hard code کردن مقدار آن میباشد .
✳️ علاوه بر اینها یک تفاوت کلیدی وجود دارد که باید از آن آگاه باشیم .همانطور که پیش تر گفتیم مقادیر یک متغیر const در کد IL جایگرین میشوند.
⚠️ اگر یک متغیر const در اسمبلی A وجود داشته باشد و اسمبلی B از اسمبلی A استفاده کند . اگر یک مقدار جدید به متغیر const موجود در اسمبلی A دهید و دوباره این اسمبلی را recompile کنید و تا زمانی که اسمبلی B را recompile نکرده اید، از همان مقدار قبلی hard code شده برای متغیر const موجود در اسمبلی A استفاده میکند.
خلاصه
🔻متغیرهای static readonly فقط میتوانند در لحظه تعریف شدن یا در متد سازنده static تغییر کنند.
🔻اگر مطمئن هستید و میدانید که مقدار یک متغیر تغییر نمیکند
از const استفاده کنید.
🔻اگر مطمئن نیستید که مقدار یک متغیر تغییر میکند یا خیر و نمیخواهید کلاس دیگری مقدار آنرا تغییر دهد
از read only استفاده کنید.
🔻متغیر های const به صورت غیر صریح static هستند.
🔻اگر میخواهید از طریق نام type به یک عضو آن دسترسی داشته باشید از static استفاده کنید.
منابع 🔍
1 - 2 -3 - 4 - 5 - 6
@FullStackDevs
Forwarded from Web Devs
#gRPC
🔸 یک بررسی ساده و مفید از پروتکل gRPC
برای اینکه بدانیم gRPC چیست ابتدا باید با rpc و Protocol Buffers اشنایی داشته باشیم .
1️⃣ RPC
مخفف Remote Procedure Call می باشد و همچنین به عنوان subroutine call و function call نیز شناخته میشود.
در واقع RPC پروتکلی هست که یک برنامه می تواند با استفاده از ان به یک برنامه که در کامپیوتری دیگر واقع شده است بدون اینکه اطلاعاتی در مورد شبکه داشته باشد درخواست ارسال کند.
از این پروتکل در شبکه های client-server استفاده میشود.
یک درخواست RPC یک عملیات synchronous است و برنامه درخواست دهنده تا زمانی که سرور درخواستش را پردازش و نتیجه را بازگرداند منتظر مانده و block میشود.
2️⃣ Protocol Buffers
پروتکل بافر که protobuf هم گفته میشود یک روش سریالیزه کردن اطلاعات است که توسط گوگل برای استفاده داخلی توسعه یافته بود که بعداً برای استفاده عموم منتشر شد. از این روش برای برقرار کردن ارتباطات بین برنامهها توسط سیم یا ذخیره کردن اطلاعات استفاده میشود. این روش شامل یک زبان توصیف میانی و یک کامپایلر که کدهای مختلف برای زبانهای برنامهنویسی مختلف از این زبان توصیف میانی تولید میکند، است.
در ابتدای ارائه توسط "گوگل"، کامپایلر آن فقط برای زبانهای C++، جاوا و پایتون، کد تولید میکرد ولی توسط اشخاص ثالث، برای خیل بیشتر زبانهای دیگر هم ابزارهایی ارائه شده است.
❇️ grpc
حال که با این مفاهیم اشنا شدیم نوبت به gRPC میرسد.gRPC یک پروتکلی است که هر دوی این ها را پیاده ساری کرده و ویژگی اصلی ان این است که زبان های برنامه نویسی زیادی را پشتیبانی میکند. و اپلیکیشن ها براساس معماری TCP client-server با این پروتکل با هم ارتباط برقرار میکنند.
این به این معنی است که سرور endpoint های مشخصی را تعریف میکند که توسط هر client ای که قادر است از طریق TCP protocol با سرور ارتباط برقرار کند صدا زده میشود و شما میتوانید همانند REST به این endpoint ها فک کنید و رابط هایی برای سرور برای کار کردن با انها فراهم بیاورید.
پروتکل های زیادی هستند که چنین امکانات و ارتباطاتی را ممکن میسازند اما gRPC ویزگی هایی را به ارمغان می اورد که دیگر پروتکل ها ندارند.
1. این پروتکل در داخل گوگل استفاده میشود پس مراحل تست های خود را دیگر گذرانده است.
2. از انجایی که پیام ها به صورت باینری تبادل میشوند بنابراین به پهنای باند کمتر نسبت به جیسون و ایکس ام ال دارید.
3. جی آر سی پی بسیار پرطرفدار است و لایبری های زیادی برای تعداد زیادی زبان برنامه نویسی وجود دارد تا از آن استفاده کنید.
4. محبوبیت زیاد مستندات خوب و همچنین انجمن هایی را به وجود می اورد که شما را میتواند در کار کردن با ان کمک کند.
@fullStackDevs
🔸 یک بررسی ساده و مفید از پروتکل gRPC
برای اینکه بدانیم gRPC چیست ابتدا باید با rpc و Protocol Buffers اشنایی داشته باشیم .
1️⃣ RPC
مخفف Remote Procedure Call می باشد و همچنین به عنوان subroutine call و function call نیز شناخته میشود.
در واقع RPC پروتکلی هست که یک برنامه می تواند با استفاده از ان به یک برنامه که در کامپیوتری دیگر واقع شده است بدون اینکه اطلاعاتی در مورد شبکه داشته باشد درخواست ارسال کند.
از این پروتکل در شبکه های client-server استفاده میشود.
یک درخواست RPC یک عملیات synchronous است و برنامه درخواست دهنده تا زمانی که سرور درخواستش را پردازش و نتیجه را بازگرداند منتظر مانده و block میشود.
2️⃣ Protocol Buffers
پروتکل بافر که protobuf هم گفته میشود یک روش سریالیزه کردن اطلاعات است که توسط گوگل برای استفاده داخلی توسعه یافته بود که بعداً برای استفاده عموم منتشر شد. از این روش برای برقرار کردن ارتباطات بین برنامهها توسط سیم یا ذخیره کردن اطلاعات استفاده میشود. این روش شامل یک زبان توصیف میانی و یک کامپایلر که کدهای مختلف برای زبانهای برنامهنویسی مختلف از این زبان توصیف میانی تولید میکند، است.
در ابتدای ارائه توسط "گوگل"، کامپایلر آن فقط برای زبانهای C++، جاوا و پایتون، کد تولید میکرد ولی توسط اشخاص ثالث، برای خیل بیشتر زبانهای دیگر هم ابزارهایی ارائه شده است.
❇️ grpc
حال که با این مفاهیم اشنا شدیم نوبت به gRPC میرسد.gRPC یک پروتکلی است که هر دوی این ها را پیاده ساری کرده و ویژگی اصلی ان این است که زبان های برنامه نویسی زیادی را پشتیبانی میکند. و اپلیکیشن ها براساس معماری TCP client-server با این پروتکل با هم ارتباط برقرار میکنند.
این به این معنی است که سرور endpoint های مشخصی را تعریف میکند که توسط هر client ای که قادر است از طریق TCP protocol با سرور ارتباط برقرار کند صدا زده میشود و شما میتوانید همانند REST به این endpoint ها فک کنید و رابط هایی برای سرور برای کار کردن با انها فراهم بیاورید.
پروتکل های زیادی هستند که چنین امکانات و ارتباطاتی را ممکن میسازند اما gRPC ویزگی هایی را به ارمغان می اورد که دیگر پروتکل ها ندارند.
1. این پروتکل در داخل گوگل استفاده میشود پس مراحل تست های خود را دیگر گذرانده است.
2. از انجایی که پیام ها به صورت باینری تبادل میشوند بنابراین به پهنای باند کمتر نسبت به جیسون و ایکس ام ال دارید.
3. جی آر سی پی بسیار پرطرفدار است و لایبری های زیادی برای تعداد زیادی زبان برنامه نویسی وجود دارد تا از آن استفاده کنید.
4. محبوبیت زیاد مستندات خوب و همچنین انجمن هایی را به وجود می اورد که شما را میتواند در کار کردن با ان کمک کند.
@fullStackDevs
Wikipedia
پروتکل بافرز
پروتکل بافرز، یک روش سریالیزه کردن اطلاعات است که توسط گوگل برای استفاده داخلی توسعه یافته بود که بعداً برای استفاده عموم منتشر شد. از این روش برای برقرار کردن ارتباطات بین برنامهها توسط سیم یا ذخیره کردن اطلاعات استفاده میشود. این روش شامل یک زبان توصیف…
Forwarded from Web Devs
#Caching
کشینگ (Caching) چیست؟
از جمله مواردی که استفاده درست و بجا از آن به طور قابل ملاحظه ای باعث افزایش کارایی برنامه میشود Caching میباشد.درواقع Caching مکانیزمی است که داده ها را ذخیره میکند تا درخواست های آینده برای آن داده ها سریعتر انجام شود و نتیجه به کلاینت زودتر بازگشت داده شود.داده های ذخیره شده می تواند نتیجه محاسبات قبلی یا کپی ای از داده های دیگر در جای دیگری باشد.این کار برای جلوگیری از محاسبات تکراری و یا کاهش درخواست ها به دیتابیس،برای داده هایی که امکان تغییر مداوم آنها کم است و همچنین هزینه محاسبه و یا ساخت دوباره آن زیاد است، صورت میگیرد.
خوشبخانه AspNetCore از روش های مختلف Caching پشتیبانی میکند.
از جمله این روش ها به Cache In Memory و Distributed Cache می توان اشاره کرد.
روش Cache In Memory از حافظه رم سرور برای ذخیره داده های کش شده استفاده میکند. این نوع Cache متناسب برای یک سرور است و برای استفاده از این روش زمانی که چند سرور دارید از ویژگی یا تکنینک Sticky session ( که به معنی درخواست های کلاینت به همان سروری که داده ها را Cache کرده برای پردازش Route میشوند) استفاده کرد.
از روش Distributed Cache برای share کردن داده های کش شده بین چندین سرور استفاده میشود. معمولا داده ها در یک سرور خارجی نگه داشته میشوند و دیگر سرور ها به آن دسترسی دارند.
محل قرا گیری عملیات Caching در معماری پروژه هایمان کجاست؟
معماری رایج در بین وب اپلیکیشن ها غالبا یک معماری تمیز (Clean Architecture) میباشد . و ما در این پست به قرار دادن عملیات مربوط به caching در چنین معماری هایی میپردازیم.
در این قبیل معماری ها براساس اصول طراحی و قوائد تعیین شده در DDD اپلیکیشن به لایه هایی تقسیم میشود و به ترتیب داخلی ترین لایه که Domain layer میباشد و کمترین وابستگی را به یک Dll خارجی دارد و هرچه به لایه های بالاتر میرویم وابستگی لایه ها به یکدیگر بیشتر میشود. از ویژگی های یک معماری خوب Loose Coupling در بین لایه ها میباشد یعنی وابستگی لایه ها به یکدیگر را بقدری کاهش داد که با تغییر یک لایه خللی در کار دیگر لایه ها صورت نگیرد. البته در این تعریف منظور از کاهش وابستگی یعنی کاهش وابستگی در زمان Compile time.
در یک Clean Architecture یا به عبارتی در یک Clean DDD Architecture معمولا لایه های بدین شکل خواهند بود :
1 - Domain|Core layer
2 - Services | Application Layer
3 - Infrastructure Layer
4 - UI Layer
در لایه Domain اپلیکیشن Entitiy ها و Contract ها(interface) های قرار میگیرد و در لایه Infrastructure معمولا پیاده سازی دسترسی به داده ها و دیگر سرویس ها خارجی مانند FileLogger و SmtpNotifier میباشد.این لایه امکان دسترسی و ذخیره سازی دائمی داده ها را ممکن میسازد،همچنین اطلاعات موجود Domain Entity ها در دیتابیس یا هر store دیگری به صورت دائمی در این لایه برای ذخیره، پیاده سازی میشود.از سوی دیگر ریپازیتوری های ما در این لایه پیاده سازی میشوند.(ریپازیتوری محلی است که امکان دسترسی یه اینتیتی ها و valueObject ها را فراهم میکند).
برای پیدا کردن محل درست caching باید با وظیفه یک عامل دیگر اشنا باشیم.
Repository pattern
الگوی طراحی ریپازیتوری یک روش برای انتزاعی کردن دسترسی به داده ها به جای استفاده Concrete شده از آنها میباشد.
از جمله دلیل استفاده از این الگو جلوگیری از دوباره نویسی Query ها و همچنین امکان تغییر دیتابیس یا ORM اپلیکیشن را میتوان بر شمرد.
همانطور که گفتیم ریپازیتوری راه دسترسی ما به داده هاست ، این داده ها ممکن از از دیتابیس واکشی شوند یا اینکه از Cache خوانده شوند و از آنجایی که پیاده سازی الگوی Repository در لایه Infrastructure صورت میگیرد پس در نتیجه لایه قرارگیری caching نیز در همین لایه و در ریپازیتوری میباشد .
اما پیاده سازی caching در داخل خود ریپازیتوری چند مشکل اساسی دارد، مشکل، عدم تست پذیری و نقض اصل اول Solid یعنی Single responsibility میباشد.
برای حل این مشکل یک الگوی طراحی Structural به کمک ما می آید و با پیاده سازی آن این مشکل را حل میکنیم.
در این قسمت به بررسی یک سری از مسائل پایه پرداختیم و در قسمت بعدی این پست به طریقه پیاده سازی آن خواهیم پرداخت.
@fullStackDevs
کشینگ (Caching) چیست؟
از جمله مواردی که استفاده درست و بجا از آن به طور قابل ملاحظه ای باعث افزایش کارایی برنامه میشود Caching میباشد.درواقع Caching مکانیزمی است که داده ها را ذخیره میکند تا درخواست های آینده برای آن داده ها سریعتر انجام شود و نتیجه به کلاینت زودتر بازگشت داده شود.داده های ذخیره شده می تواند نتیجه محاسبات قبلی یا کپی ای از داده های دیگر در جای دیگری باشد.این کار برای جلوگیری از محاسبات تکراری و یا کاهش درخواست ها به دیتابیس،برای داده هایی که امکان تغییر مداوم آنها کم است و همچنین هزینه محاسبه و یا ساخت دوباره آن زیاد است، صورت میگیرد.
خوشبخانه AspNetCore از روش های مختلف Caching پشتیبانی میکند.
از جمله این روش ها به Cache In Memory و Distributed Cache می توان اشاره کرد.
روش Cache In Memory از حافظه رم سرور برای ذخیره داده های کش شده استفاده میکند. این نوع Cache متناسب برای یک سرور است و برای استفاده از این روش زمانی که چند سرور دارید از ویژگی یا تکنینک Sticky session ( که به معنی درخواست های کلاینت به همان سروری که داده ها را Cache کرده برای پردازش Route میشوند) استفاده کرد.
از روش Distributed Cache برای share کردن داده های کش شده بین چندین سرور استفاده میشود. معمولا داده ها در یک سرور خارجی نگه داشته میشوند و دیگر سرور ها به آن دسترسی دارند.
محل قرا گیری عملیات Caching در معماری پروژه هایمان کجاست؟
معماری رایج در بین وب اپلیکیشن ها غالبا یک معماری تمیز (Clean Architecture) میباشد . و ما در این پست به قرار دادن عملیات مربوط به caching در چنین معماری هایی میپردازیم.
در این قبیل معماری ها براساس اصول طراحی و قوائد تعیین شده در DDD اپلیکیشن به لایه هایی تقسیم میشود و به ترتیب داخلی ترین لایه که Domain layer میباشد و کمترین وابستگی را به یک Dll خارجی دارد و هرچه به لایه های بالاتر میرویم وابستگی لایه ها به یکدیگر بیشتر میشود. از ویژگی های یک معماری خوب Loose Coupling در بین لایه ها میباشد یعنی وابستگی لایه ها به یکدیگر را بقدری کاهش داد که با تغییر یک لایه خللی در کار دیگر لایه ها صورت نگیرد. البته در این تعریف منظور از کاهش وابستگی یعنی کاهش وابستگی در زمان Compile time.
در یک Clean Architecture یا به عبارتی در یک Clean DDD Architecture معمولا لایه های بدین شکل خواهند بود :
1 - Domain|Core layer
2 - Services | Application Layer
3 - Infrastructure Layer
4 - UI Layer
در لایه Domain اپلیکیشن Entitiy ها و Contract ها(interface) های قرار میگیرد و در لایه Infrastructure معمولا پیاده سازی دسترسی به داده ها و دیگر سرویس ها خارجی مانند FileLogger و SmtpNotifier میباشد.این لایه امکان دسترسی و ذخیره سازی دائمی داده ها را ممکن میسازد،همچنین اطلاعات موجود Domain Entity ها در دیتابیس یا هر store دیگری به صورت دائمی در این لایه برای ذخیره، پیاده سازی میشود.از سوی دیگر ریپازیتوری های ما در این لایه پیاده سازی میشوند.(ریپازیتوری محلی است که امکان دسترسی یه اینتیتی ها و valueObject ها را فراهم میکند).
برای پیدا کردن محل درست caching باید با وظیفه یک عامل دیگر اشنا باشیم.
Repository pattern
الگوی طراحی ریپازیتوری یک روش برای انتزاعی کردن دسترسی به داده ها به جای استفاده Concrete شده از آنها میباشد.
از جمله دلیل استفاده از این الگو جلوگیری از دوباره نویسی Query ها و همچنین امکان تغییر دیتابیس یا ORM اپلیکیشن را میتوان بر شمرد.
همانطور که گفتیم ریپازیتوری راه دسترسی ما به داده هاست ، این داده ها ممکن از از دیتابیس واکشی شوند یا اینکه از Cache خوانده شوند و از آنجایی که پیاده سازی الگوی Repository در لایه Infrastructure صورت میگیرد پس در نتیجه لایه قرارگیری caching نیز در همین لایه و در ریپازیتوری میباشد .
اما پیاده سازی caching در داخل خود ریپازیتوری چند مشکل اساسی دارد، مشکل، عدم تست پذیری و نقض اصل اول Solid یعنی Single responsibility میباشد.
برای حل این مشکل یک الگوی طراحی Structural به کمک ما می آید و با پیاده سازی آن این مشکل را حل میکنیم.
در این قسمت به بررسی یک سری از مسائل پایه پرداختیم و در قسمت بعدی این پست به طریقه پیاده سازی آن خواهیم پرداخت.
@fullStackDevs
blog.airbrake.io
Domain-Driven Design: What is it and how do you use it?
A detailed look at domain-driven design in software development, including basic implementation and a handful of advantages and disadvantages of its use.
#SemanticKernel
#Semantic_Kernel
سی شارپ (C#) و Python Copilot خود را بسازید و آنها را بهتر از ChatGPT با GPT4 بسازید :).
معرفی Semantic Kernel — یک پروژه منبع باز جدید که به توسعه دهندگان کمک می کند تا LLM های پیشرفته را به راحتی در برنامه های خود ادغام کنند: لینک
در واقع Semantic Kernel یک SDK سبک و منبع باز است که به شما کمک می کند از زبان طبیعی و کد بومی (natural language and native code) برای ساخت برنامه های مبتنی بر هوش مصنوعی با چهار کامپوننت استفاده کنید:
Skills،
Memories/Embeddings،
Connectors ,
Planner.
با OpenAI و Azure OpenAI Services کار می کند و از C# با پشتیبانی از پیش نمایش پایتون پشتیبانی می کند.
در اینجا بیشتر بیاموزید.
@fullStackDevs
#Semantic_Kernel
سی شارپ (C#) و Python Copilot خود را بسازید و آنها را بهتر از ChatGPT با GPT4 بسازید :).
معرفی Semantic Kernel — یک پروژه منبع باز جدید که به توسعه دهندگان کمک می کند تا LLM های پیشرفته را به راحتی در برنامه های خود ادغام کنند: لینک
در واقع Semantic Kernel یک SDK سبک و منبع باز است که به شما کمک می کند از زبان طبیعی و کد بومی (natural language and native code) برای ساخت برنامه های مبتنی بر هوش مصنوعی با چهار کامپوننت استفاده کنید:
Skills،
Memories/Embeddings،
Connectors ,
Planner.
با OpenAI و Azure OpenAI Services کار می کند و از C# با پشتیبانی از پیش نمایش پایتون پشتیبانی می کند.
در اینجا بیشتر بیاموزید.
@fullStackDevs
#Net8
#DotNet8
ویژگی های جدید دات نت 8 : آنچه باید در مورد این ویژگی ها بدانید.
✏️ لینک مطلب
@fullStackDevs
#DotNet8
ویژگی های جدید دات نت 8 : آنچه باید در مورد این ویژگی ها بدانید.
✏️ لینک مطلب
@fullStackDevs
#Net8
#DotNet8
💡 .𝐍𝐄𝐓 8 𝐟𝐞𝐚𝐭𝐮𝐫𝐞𝐬
IPNetwork class is A representation of an IP network based on 𝐂𝐈𝐃𝐑 𝐧𝐨𝐭𝐚𝐭𝐢𝐨𝐧.
کلاس IPNetwork نمایشی از یک شبکه IP بر اساس 𝐂𝐈𝐃𝐑 𝐧𝐨𝐭𝐚𝐭𝐢𝐨𝐧 است. در این نسخه متدهای جدید Parse و TryParse t برای ایجاد یک شبکه IP اضافه شده است.
@fullStackDevs
#DotNet8
💡 .𝐍𝐄𝐓 8 𝐟𝐞𝐚𝐭𝐮𝐫𝐞𝐬
IPNetwork class is A representation of an IP network based on 𝐂𝐈𝐃𝐑 𝐧𝐨𝐭𝐚𝐭𝐢𝐨𝐧.
کلاس IPNetwork نمایشی از یک شبکه IP بر اساس 𝐂𝐈𝐃𝐑 𝐧𝐨𝐭𝐚𝐭𝐢𝐨𝐧 است. در این نسخه متدهای جدید Parse و TryParse t برای ایجاد یک شبکه IP اضافه شده است.
@fullStackDevs
#chunk
#linq
💡مجموعه اسپیلیت ( split) با LINQ Chunk💡
اگر نیاز دارید که مجموعه و یا collection ای را به مجموعه های کوچکتر بدون روش های اضافی تقسیم کنید، LINQ یک متد 𝗖𝗵𝘂𝗻𝗸 را که از 6 NET. معرفی شد ،پیشنهاد میکند.
تعداد سایز عنصر را به عنوان پارامتر در نظر می گیرد و سپس مجموعه را با حداکثر تعداد عنصر به مجموعه های کوچکتر تقسیم می کند.
در مثال فوق در chunkResult سه آرایه وجود خواهد داشت که تعداد دو مجموعه اول 5 و مجموعه سوم 4 آیتم وجود دارد.!
@fullStackDevs
#linq
💡مجموعه اسپیلیت ( split) با LINQ Chunk💡
اگر نیاز دارید که مجموعه و یا collection ای را به مجموعه های کوچکتر بدون روش های اضافی تقسیم کنید، LINQ یک متد 𝗖𝗵𝘂𝗻𝗸 را که از 6 NET. معرفی شد ،پیشنهاد میکند.
تعداد سایز عنصر را به عنوان پارامتر در نظر می گیرد و سپس مجموعه را با حداکثر تعداد عنصر به مجموعه های کوچکتر تقسیم می کند.
در مثال فوق در chunkResult سه آرایه وجود خواهد داشت که تعداد دو مجموعه اول 5 و مجموعه سوم 4 آیتم وجود دارد.!
@fullStackDevs
#feature
#cSharp
یکی از ویژگی های که از C# 7.0 معرفی شد استفاده از خط زیر (underscore) ( _ ) به عنوان جداکننده رقم شناخته می شود. هدف این است که خوانایی بهتر و آسانتر را بخصوص در اعداد بزرگ فراهم کنیم.
📌 اطلاعات بیشتر
@fullStackDevs
#cSharp
یکی از ویژگی های که از C# 7.0 معرفی شد استفاده از خط زیر (underscore) ( _ ) به عنوان جداکننده رقم شناخته می شود. هدف این است که خوانایی بهتر و آسانتر را بخصوص در اعداد بزرگ فراهم کنیم.
📌 اطلاعات بیشتر
@fullStackDevs