Przejdź do głównej zawartości

Receptury .NET i C#

Receptury .NET i C# dla agentów AI to konfiguracja i prompty, dzięki którym Claude Code, Codex i Cursor dostarczają godne zaufania zmiany w ASP.NET Core i EF Core: jeden AGENTS.md z dokładnymi komendami dotnet, testy xUnit v3 pisane przed kodem i uruchamiane na prawdziwym PostgreSQL oraz analizatory Roslyn ustawione tak, że zły wzorzec wywraca build.

Ta strona jest dla programistów C#, za których większość kodu usługi pisze już agent. Sytuacja, którą rozwiązuje: pull request od agenta się kompiluje, a testy są zielone, tyle że testy używają dostawcy in-memory z EF Core, nowa migracja usuwa kolumnę, którą miała tylko przemianować, .Result blokuje wątek w środku żądania, a świeże #pragma warning disable ukrywa jedyne ostrzeżenie analizatora, które by to wyłapało. C# daje ci kompilator i potok analizatorów, jakich większość języków nie ma. Poniższe receptury sprawiają, że agent zderza się z nimi w każdej turze, więc przeglądasz dowody, a nie diffy. Jeśli repozytorium nie ma jeszcze AGENTS.md ani bramki w CI, zacznij od strony Repozytorium gotowe na agentów.

  • Directory.Build.props i global.json, które zamieniają ostrzeżenia nullable, reguły jakości kodu i styl kodu w błędy builda oraz przypinają SDK i runner testów używany przez agenta.
  • AGENTS.md dla solucji .NET, podpięty do Claude Code, Codex i Cursora.
  • Pętlę test-first dla endpointu ASP.NET Core: kryteria akceptacji, padające testy xUnit v3 na bazie PostgreSQL z Testcontainers, a dopiero potem implementacja.
  • Hook Stop w Claude Code, który nie pozwala ogłosić „gotowe”, dopóki dotnet build i dotnet test nie przejdą, oraz rozwiązania zastępcze dla Codex i Cursora: hook Stop w Codex albo skryptowy przebieg codex exec oraz bramka w CI.
  • Recepturę na migracje EF Core z kontrolą rozjazdu modelu i ludzką akceptacją zmian niszczących dane.
  • Listę zakazanych API, test architektury i testy mutacyjne, które łapią to, co agenci psują w C#.
  • Typowe awarie przy pracy agentów w .NET, każda z promptem naprawczym.

Jakie wersje .NET, EF Core i xUnit zakładają te receptury?

Dział zatytułowany „Jakie wersje .NET, EF Core i xUnit zakładają te receptury?”

Agenci uczyli się na latach przykładów .NET, więc mieszają API z różnych wersji głównych. Podaj agentowi wersje i przypnij je w repozytorium, żeby nie mógł się od nich odchylać. Te były aktualne 2026-09-26:

KomponentWersjaŹródło (sprawdzone 2026-09-26)
.NET (LTS).NET 10, runtime 10.0.12, SDK 10.0.401; wsparcie do 2028-11-14indeks wydań dotnet/core
.NET 11Release candidate 1 (2026-09-08), wsparcie standardowe (STS)indeks wydań dotnet/core
.NET 8 i .NET 9Oba tracą wsparcie 2026-11-10indeks wydań dotnet/core
EF Core, dotnet-ef, Microsoft.AspNetCore.Mvc.Testing10.0.12NuGet
Npgsql.EntityFrameworkCore.PostgreSQL10.0.3NuGet
xUnit.net v3 (rdzeń, xunit.v3)4.0.1 (opublikowane 2026-09-12)NuGet
Testcontainers.PostgreSql4.15.0NuGet

Receptury zakładają .NET 10. Jeśli nadal jesteś na .NET 8 lub 9, aktualizacja to pierwsze zadanie warte zlecenia agentowi, bo obie wersje tracą wsparcie w listopadzie.

Umieść bramki w MSBuild, a nie w prompcie. Polecenie „pisz czysty kod” następna sesja zapomni. Właściwość w Directory.Build.props działa w każdym projekcie, każdej sesji i każdym przebiegu CI. Większość pracy robią trzy pliki w katalogu głównym solucji.

Directory.Build.props zamienia ostrzeżenia w błędy i podnosi poprzeczkę analizatorom:

<!-- Directory.Build.props: applies to every project under this folder -->
<Project>
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<AnalysisLevel>latest-recommended</AnalysisLevel>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
</PropertyGroup>
</Project>

AnalysisLevel ustawione na latest-recommended włącza zalecany zestaw reguł jakości CA. EnforceCodeStyleInBuild sprawia, że reguły stylu IDE, którym .editorconfig nadaje poziom warning lub error, działają przy dotnet build, więc trafiają do terminala agenta, a nie tylko do IDE. RestorePackagesWithLockFile zapisuje packages.lock.json, dzięki czemu CI przywraca pakiety z --locked-mode i pada, gdy agent zmieni zależność bez zacommitowania pliku blokady.

global.json przypina SDK i przełącza dotnet test na Microsoft Testing Platform (MTP), czyli tryb, który dokumentacja xUnit v3 opisuje dla SDK .NET 10 i nowszych:

{
"sdk": { "version": "10.0.401", "rollForward": "latestFeature" },
"test": { "runner": "Microsoft.Testing.Platform" }
}

Lokalny manifest narzędzi daje agentowi tę samą wersję dotnet ef co w CI, a nie tę, która akurat jest zainstalowana globalnie:

Okno terminala
# terminal, at the solution root
dotnet new tool-manifest
dotnet tool install dotnet-ef
dotnet new install xunit.v3.templates # adds the xunit3 project template

W trybie MTP dotnet test przyjmuje projekt przez --project, a solucję przez --solution, zamiast samej ścieżki, a filtry xUnit v3 stają się opcjami w rodzaju --filter-class. Agenci uczeni na przykładach z epoki VSTest piszą dotnet test MyProject.csproj --filter …, więc wpisz nową składnię do AGENTS.md.

Wpisz komendy, które agent musi uruchomić, w kolejności, w jakiej ma je uruchomić, oraz kilka reguł, które ogólny model C# myli w twoim kodzie. Trzymaj plik krótki; wszystko, co już wymusza MSBuild, zostaje poza nim.

AGENTS.md
# Orders service (.NET 10, ASP.NET Core minimal APIs, EF Core 10, PostgreSQL)
## Commands (run from the repo root)
- Restore: `dotnet restore --locked-mode` (commit packages.lock.json if you add a package)
- Build: `dotnet build --no-restore` (warnings are errors; fix the code, never add NoWarn or #pragma)
- Test all: `dotnet test --no-build` (needs Docker: integration tests start PostgreSQL with Testcontainers)
- Test one class: `dotnet test --project tests/Orders.Api.Tests --filter-class Orders.Api.Tests.CreateOrderTests`
- Format: `dotnet format --verify-no-changes`
- Migration drift: `dotnet ef migrations has-pending-model-changes --project src/Orders.Infrastructure --startup-project src/Orders.Api`
## Definition of done
Build, tests, format and the drift check all pass. Report the commands you ran and their result.
## Rules
- Tests are the specification. If a test fails, fix src/, not tests/. Ask before changing an assertion.
- Integration tests use the ApiFactory fixture (real PostgreSQL). Never use the EF Core in-memory provider.
- Package versions live in Directory.Packages.props. Never put Version= on a PackageReference.
- Time comes from TimeProvider, never DateTime.Now or DateTime.UtcNow.
- async all the way down: no .Result, .Wait() or GetAwaiter().GetResult().
- Read-only queries use AsNoTracking(). Queries that Include two or more collections use AsSplitQuery().
- Never edit an existing migration. Add a new one.

Każde narzędzie wczytuje ten sam plik inaczej:

Utwórz CLAUDE.md, którego pierwsza linia importuje wspólny plik, a pod nią dopisz ewentualne linie tylko dla Claude:

CLAUDE.md
@AGENTS.md

Claude Code w wersji 2.1.277 i nowszych czyta też AGENTS.md bezpośrednio, gdy projekt nie ma CLAUDE.md (w Bedrock, Google Cloud, Foundry i bramach LLM od v2.1.281; obie wersje na kanale wydań latest w dniu 2026-09-26). Import działa w każdej wersji i na każdym kanale. Do nawigacji po kodzie dodaj wtyczkę z serwerem języka C# opisaną w sekcji Jak dać agentowi aktualną dokumentację .NET i nawigację po kodzie?.

Wspólne reguły agentów wyjaśniają, jak utrzymać jedno źródło prawdy, gdy zespół używa wszystkich trzech narzędzi.

Jak prowadzić pętlę test-first dla endpointu ASP.NET Core?

Dział zatytułowany „Jak prowadzić pętlę test-first dla endpointu ASP.NET Core?”

Opisz zachowanie, każ agentowi napisać padające testy, sprawdź, że padają z właściwego powodu, i dopiero wtedy pozwól mu napisać implementację. Testy stają się wyrocznią, której ufasz, więc muszą trafiać w ten sam silnik bazy co produkcja. Dostawca in-memory z EF Core nie wymusza unikalnych indeksów, kluczy obcych ani transakcji, więc test na nim nie dowodzi niczego o zachowaniu, na którym zależy ci najbardziej.

  1. Spisz kryteria akceptacji. Od trzech do sześciu obserwowalnych zachowań, każde jako zdanie, które test może sprawdzić. Format pokazuje strona kryteria akceptacji, które agent potrafi zweryfikować.

  2. Poproś wyłącznie o padające testy. Użyj pierwszego promptu poniżej. Zatrzymaj agenta, gdy testy się kompilują i padają.

  3. Czytaj porażki, nie kod. Każdy test powinien paść na asercji (na przykład 404 zamiast 201), a nie na brakującej klasie czy błędzie kontenera. Test, który pada z niewłaściwego powodu, przejdzie też z niewłaściwego powodu.

  4. Poproś o implementację. Użyj drugiego promptu. Pętla kończy się, gdy przechodzi cała bramka, a nie gdy agent ogłosi, że skończył.

  5. Sprawdź siłę testów. Uruchom testy mutacyjne na zmienionym kodzie (zobacz Które analizatory i testy zamieniają komentarze z review w błędy builda?).

Porównaj fixture napisany przez agenta z tym kształtem. xUnit v3 przestawił IAsyncLifetime na ValueTask, a Testcontainers 4.x oznacza bezparametrowy konstruktor buildera jako przestarzały, więc starsze wzorce wywracają tu build. Ustaw tag obrazu na główną wersję PostgreSQL, której używasz na produkcji; postgres:17-alpine to tylko przykład:

tests/Orders.Api.Tests/ApiFactory.cs
public sealed class ApiFactory : WebApplicationFactory<Program>, IAsyncLifetime
{
private readonly PostgreSqlContainer _db = new PostgreSqlBuilder("postgres:17-alpine").Build();
public async ValueTask InitializeAsync()
{
await _db.StartAsync();
using var scope = Services.CreateScope();
await scope.ServiceProvider.GetRequiredService<OrdersDbContext>().Database.MigrateAsync();
}
protected override void ConfigureWebHost(IWebHostBuilder builder) =>
builder.UseSetting("ConnectionStrings:Orders", _db.GetConnectionString());
public override async ValueTask DisposeAsync()
{
await _db.DisposeAsync();
await base.DisposeAsync();
}
}

Fixture stosuje twoje prawdziwe migracje, więc każdy przebieg testów integracyjnych dowodzi też, że migracje dają się nałożyć na pustą bazę. Jeśli projekt testowy nie widzi klasy Program, agent może dopisać public partial class Program; na końcu Program.cs; to jedyna zmiana w src/, jaką wolno zrobić na etapie testów.

Ochrona wyroczni wyjaśnia, dlaczego polecenie „nie zmieniaj testów” potrzebuje też mechanicznego zabezpieczenia. Dodaje je następna sekcja.

Jak powstrzymać agenta przed ogłoszeniem „gotowe”, zanim build jest zielony?

Dział zatytułowany „Jak powstrzymać agenta przed ogłoszeniem „gotowe”, zanim build jest zielony?”

Polecenie w AGENTS.md to rada. Hook albo kontrola w CI to bramka. Każde narzędzie daje inny sposób, żeby bramka uruchamiała się w każdej turze.

Hook Stop uruchamia się, gdy Claude kończy odpowiedź. Kod wyjścia 2 blokuje zakończenie i pokazuje Claude’owi stderr hooka, więc Claude pracuje dalej. Po ośmiu kolejnych kontynuacjach Claude Code nadpisuje blokadę, więc pętla nie może trwać w nieskończoność; zmienna środowiskowa CLAUDE_CODE_STOP_HOOK_BLOCK_CAP ustawia ten limit (domyślnie 8; sprawdzone w Claude Code 2.1.283, nadal obecne w 2.1.286).

.claude/hooks/dotnet-gate.sh
#!/usr/bin/env bash
# Refuse "done" until the solution builds without warnings and the tests pass.
cd "$CLAUDE_PROJECT_DIR" || exit 0
# Skip turns that changed no C#, project or gate files (a question, a plan).
[ -z "$(git status --porcelain -- '*.cs' '*.csproj' '*.props' '*.targets' '*.sln' '*.slnx' '.editorconfig' 'BannedSymbols.txt' 'global.json' 'packages.lock.json')" ] && exit 0
if ! out=$(dotnet build --no-restore 2>&1); then
echo "dotnet build failed. Fix the code; do not add NoWarn or #pragma." >&2
echo "If the error is NETSDK1004 or NU1xxx, run 'dotnet restore --locked-mode' (or update packages.lock.json) first." >&2
echo "$out" | grep -E ": (error|warning) " | sort -u | head -30 >&2
exit 2
fi
if ! out=$(dotnet test --no-build 2>&1); then
echo "dotnet test failed. Fix src/, never tests/:" >&2
echo "$out" | tail -40 >&2
exit 2
fi
exit 0

Warunek pomijania (linia z git status) sprawdza tylko niezatwierdzone zmiany: jeśli agent zrobi commit w trakcie tury, bramka się nie uruchomi, więc każ mu nie commitować (albo porównuj z HEAD z początku sesji). Drzewo, które już wcześniej miało zmiany, uruchamia pełną bramkę w każdej turze, także przy samych pytaniach.

Zarejestruj go w ustawieniach projektu i nadaj skryptowi prawo wykonania (chmod +x):

.claude/settings.json
{
"hooks": {
"Stop": [
{
"hooks": [
{ "type": "command", "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/dotnet-gate.sh", "timeout": 600 }
]
}
]
}
}

Skrypt celowo ignoruje stop_hook_active, pole, które Claude Code ustawia w danych wejściowych hooka, gdy zakończenie zostało już raz zablokowane. Wyjście z kodem 0 przy wartości true przepuściłoby drugą próbę mimo czerwonego builda; zamiast tego pętlę ogranicza limit ośmiu blokad, więc uporczywy błąd może kosztować około dziewięciu przebiegów builda i testów, zanim Claude Code i tak zakończy pracę.

Hook kosztuje pełny build i przebieg testów, razem z Testcontainers, w każdej turze, która zmieniła pliki C# lub projektu, więc trzymaj Dockera włączonego w trakcie sesji. Jeśli lokalnie to za wolno, uruchamiaj tylko testy, które nie potrzebują Dockera, a pełny zestaw zostaw CI. W trybie MTP składnia --filter z VSTest nie działa; użyj filtra cech (trait) z xUnit v3. Oznacz każdą klasę testów integracyjnych atrybutem [Trait("Category", "Integration")], a potem zmień linię z testami w dotnet-gate.sh:

Okno terminala
if ! out=$(dotnet test --no-build --filter-not-trait "Category=Integration" 2>&1); then

Pozostałe zdarzenia opisuje strona automatyzacja hookami.

Niezależnie od tego, co działa lokalnie, rozstrzyga CI. Uruchom tam te same komendy z tokenem tylko do odczytu:

.github/workflows/dotnet-gates.yml
name: dotnet-gates
on: [pull_request]
permissions:
contents: read
jobs:
gates:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: actions/setup-dotnet@v6
with:
global-json-file: global.json
- run: dotnet restore --locked-mode
- run: dotnet tool restore
- run: dotnet format --verify-no-changes --no-restore
- run: dotnet build --no-restore
- run: dotnet test --no-build
- run: dotnet ef migrations has-pending-model-changes --project src/Orders.Infrastructure --startup-project src/Orders.Api

Migracje to jedyne miejsce, w którym zielone testy nie są wystarczającym dowodem. Agent może wygenerować migrację, która czysto nakłada się na pustą bazę testową, a mimo to niszczy dane na produkcji. Klasyczny przypadek: agent zmienia nazwę właściwości, EF Core generuje DropColumn i AddColumn, testy przechodzą na świeżej bazie, a dane z kolumny znikają przy pierwszym wdrożeniu.

Podziel pracę między bramki i człowieka:

KontrolaJakKto decyduje
Model i migracje są zgodnedotnet ef migrations has-pending-model-changes (EF Core 8 i nowsze) pada, gdy model zmienił się bez migracjiCI
Migracje nakładają się od zeraFixture ApiFactory uruchamia MigrateAsync() na pustym PostgreSQLCI
SQL jest taki, jakiego oczekujeszWynik dotnet ef migrations script --idempotent dołączony do pull requestaCzłowiek czyta SQL, a nie C#
Operacje niszcząceDropColumn, DropTable, AlterColumn zawężające typCzłowiek, przez CODEOWNERS na **/Migrations/

Szersze zasady zmian typu expand-and-contract i wycofywania opisują wzorce migracji baz danych. EF Core 11 dodaje plik .config/dotnet-ef.json z domyślnymi wartościami --project i --startup-project; w EF Core 10 trzymaj te flagi w AGENTS.md.

Które analizatory i testy zamieniają komentarze z review w błędy builda?

Dział zatytułowany „Które analizatory i testy zamieniają komentarze z review w błędy builda?”

Każdy komentarz, który piszesz drugi raz pod pull requestem agenta, to reguła, której jeszcze nie zautomatyzowałeś. W C# większość z nich może stać się diagnostyką kompilatora albo testem. Każda bramka poniżej robi się czerwona z konkretnego powodu:

BramkaKonfiguracjaRobi się czerwona, gdy
Typy referencyjne nullable<Nullable>enable</Nullable> plus TreatWarningsAsErrorsAgent dereferuje wartość, która może być null
Reguły jakości CA<AnalysisLevel>latest-recommended</AnalysisLevel>Kod łamie regułę z zalecanego zestawu CA (projekt, niezawodność, wydajność, bezpieczeństwo, użycie)
Styl koduPoziomy w .editorconfig plus EnforceCodeStyleInBuildAgent ignoruje konwencje nazewnictwa albo użycia var w zespole
Zakazane APIMicrosoft.CodeAnalysis.BannedApiAnalyzers (5.6.0) i BannedSymbols.txtKod wywołuje DateTime.Now, Task.Result albo cokolwiek innego z listy (diagnostyka RS0030)
ArchitekturaNetArchTest.Rules (1.3.2, ostatnie wydanie w maju 2021; aktywnie rozwijaną alternatywą jest TngTech.ArchUnitNET 0.13.4) w teścieProjekt domeny odwołuje się do infrastruktury albo ASP.NET Core
Siła testówdotnet-stryker (5.0.0)Testy nadal przechodzą po tym, jak Stryker zmutuje testowany kod

Lista zakazanych API to najprostszy sposób na zapisanie zasady „tak tu nie robimy”. Dodaj pakiet do projektów w src/, dodaj <AdditionalFiles Include="BannedSymbols.txt" /> i wypisz identyfikatory dokumentacji z komunikatem, który agent przeczyta:

BannedSymbols.txt
P:System.DateTime.Now;Inject TimeProvider and call GetUtcNow()
P:System.DateTime.UtcNow;Inject TimeProvider and call GetUtcNow()
P:System.Threading.Tasks.Task`1.Result;Await the task instead of blocking
M:System.Threading.Tasks.Task.Wait;Await the task instead of blocking
M:System.Threading.Tasks.Task.Wait(System.Int32);Await the task instead of blocking
M:System.Threading.Tasks.Task.Wait(System.TimeSpan);Await the task instead of blocking
M:System.Threading.Tasks.Task.Wait(System.Threading.CancellationToken);Await the task instead of blocking
M:System.Threading.Tasks.Task.Wait(System.Int32,System.Threading.CancellationToken);Await the task instead of blocking
M:System.Runtime.CompilerServices.TaskAwaiter.GetResult;Await the task instead of blocking
M:System.Runtime.CompilerServices.TaskAwaiter`1.GetResult;Await the task instead of blocking

Test architektury utrzymuje warstwy solucji po 50 sesjach agenta:

[Fact]
public void Domain_does_not_depend_on_infrastructure_or_web()
{
var result = Types.InAssembly(typeof(Order).Assembly)
.ShouldNot().HaveDependencyOnAny("Orders.Infrastructure", "Microsoft.AspNetCore")
.GetResult();
Assert.True(result.IsSuccessful, string.Join(", ", result.FailingTypeNames ?? []));
}

Testy mutacyjne odpowiadają na pytanie, na które zielony przebieg nie odpowie: czy te testy zauważyłyby, że kod jest błędny? Uruchom dotnet tool install dotnet-stryker, a potem dotnet stryker w katalogu projektu testowego i przeczytaj mutanty, które przeżyły. Siła wyroczni wyjaśnia, jak zamienić ocalałe mutanty w brakujące przypadki testowe.

Zmiany w Directory.Build.props, .editorconfig, BannedSymbols.txt i każdy wpis NoWarn osłabiają same bramki, więc przypisz tym ścieżkom ludzkiego właściciela w CODEOWNERS. Strona pakiet dowodów pokazuje, jak dołączyć wyniki bramek i SQL migracji do pull requesta, żeby recenzent sprawdzał dowody, a nie kod.

Jak dać agentowi aktualną dokumentację .NET i nawigację po kodzie?

Dział zatytułowany „Jak dać agentowi aktualną dokumentację .NET i nawigację po kodzie?”

W .NET najbardziej pomagają dwa dodatki: aktualna dokumentacja Microsoftu, bo API zmieniają się między wersjami głównymi, oraz serwer języka, bo wyszukiwanie tekstowe słabo znajduje wszystkie wywołania w dużej solucji.

Serwer MCP Microsoft Learn. Zdalny serwer pod https://learn.microsoft.com/api/mcp, bez uwierzytelniania, z trzema narzędziami: microsoft_docs_search, microsoft_docs_fetch i microsoft_code_sample_search. Parametr ?maxTokenBudget=2000 w adresie ogranicza, ile każda odpowiedź dokłada do kontekstu; poniższe fragmenty dla Codex i Cursora już go zawierają. Popularność: około 1,9 tys. gwiazdek na GitHubie dla MicrosoftDocs/mcp (GitHub, odczyt 2026-09-26).

Zainstaluj wtyczkę Microsoftu, która łączy serwer MCP z jego skillami (uruchom w sesji):

/plugin install microsoft-docs@claude-plugins-official

Wtyczka rejestruje adres bez limitu. Żeby dodać sam serwer z limitem, uruchom zamiast tego w powłoce:

Okno terminala
claude mcp add --transport http microsoft-learn "https://learn.microsoft.com/api/mcp?maxTokenBudget=2000"

Do nawigacji po kodzie zainstaluj serwer języka C# i wtyczkę, która go rejestruje. Wtyczka nie zawiera samego programu:

Okno terminala
dotnet tool install --global csharp-ls
claude plugin install csharp-lsp@claude-plugins-official

csharp-lsp miał 43 741 instalacji w katalogu wtyczek claude.com (odczyt 2026-09-26). Wtyczki LSP nie dokładają stałego kosztu kontekstu, bo serwer działa poza procesem.

Bez serwera agent łata luki danymi treningowymi i pisze na przykład bezparametrowy builder Testcontainers albo filtry dotnet test w stylu VSTest. Z serwerem jeden prompt najpierw sprawdza aktualne API:

Według README microsoft/skills zawiera 175 skilli do SDK we wtyczkach językowych dla Pythona, .NET, TypeScriptu, Javy i Rusta. Leżą poniżej katalogu głównego repozytorium, więc npx skills add microsoft/skills --list pokazuje tylko 13 skilli z korzenia; z --full-depth znajduje 192, czyli zestaw SDK oraz skille z korzenia i inne zagnieżdżone (sprawdzone 2026-09-26). README ostrzega też, że wczytanie wszystkich naraz psuje kontekst („context rot”), więc instaluj tylko skille do tych SDK Azure, których używa twoja usługa. Strona najlepsze skille backendowe i platformowe porównuje je z zestawami innych dostawców.