Del 7 av 8

AI-genererade Instruktionsfiler

Låt AI extrahera reglerna ur din kod och skapa instruktionsfiler som håller projektet konsekvent — oavsett vem som kodar eller vilken fil som öppnas.

Lektion 6 — V3 dag 2 Vi tar referensimplementationen från del 06 och använder Copilot för att generera en komplett instruktionsfil-svit. Vi itererar filerna tills de faktiskt styr AI:s beteende på rätt sätt.

Instruktionsfilernas roll i arkitekturstyrning

En välskriven instruktionsfil är ett kontrakt: den berättar för AI exakt vad som gäller i detta projekt, för denna typ av fil. När Copilot genererar kod kommer den att följa dessa regler — precis som en erfaren kollega som läst den tekniska specifikationen.

Det viktiga är att reglerna är projektspecifika, inte generella. "Följ SOLID" är inte en bra regel i en instruktionsfil — det vet AI redan. "Returnera alltid Result<T>, aldrig null, aldrig exception" är en bra regel.

Typer av instruktionsfiler och deras syfte

FilapplyToTäcker
copilot-instructions.md Alla filer (automatisk) Stack, namnkonventioner, globala förbud
domain.instructions.md src/Domain/**/*.cs AggregateRoot-mönster, Value Objects, domänhändelser, Result-typ
application.instructions.md src/Application/**/*.cs Command/Query-struktur, MediatR-handlers, validering
api.instructions.md src/Api/**/*.cs Minimal API-mönster, HTTP-felhantering, versionshantering
tests.instructions.md tests/**/*.cs xUnit-mönster, NSubstitute-användning, AAA-struktur, namnkonvention
infrastructure.instructions.md src/Infrastructure/**/*.cs EF Core-konfiguration, migrationer, repository-implementation

Promptmall — Generera instruktionsfil från kod

# Kör med Claude Sonnet 4.6 eller GPT-5.4 för bäst resultat

Du är en senior C#-arkitekt. Analysera dessa referensimplementationer:

#file:src/Domain/Common/AggregateRoot.cs
#file:src/Domain/Common/Result.cs
#file:src/Domain/Orders/Order.cs
#file:src/Domain/Orders/OrderItem.cs
#file:src/Domain/Orders/Events/OrderPlacedEvent.cs
#file:tests/Domain/OrderTests.cs

Generera en .instructions.md-fil med frontmatter:
---
applyTo: "src/Domain/**/*.cs"
---

KRAV på filens innehåll:
1. Extrahera BARA regler som explicit framgår av koden — inget generellt
2. Skriv varje regel som en imperativ mening ("Returnera alltid...", "Använd aldrig...")
3. Täck: basklassanvändning, namnkonventioner, felhanteringskontrakt,
   händelsehantering, Value Object-mönster, factory-metoder
4. Max 35 regler, grupperade under beskrivande rubriker
5. Inkludera ett minimalt C#-kodexempel per rubrik som visar rätt mönster

Avsluta med en sektion "Förbjudna mönster" med 5-7 negativa regler.

Exempel — Genererad instruktionsfil (domänlagret)

---
applyTo: "src/Domain/**/*.cs"
---

# Domänlager — Instruktioner

## Aggregat
- Alla aggregat ärver från `AggregateRoot<TId>` i `MyApp.Domain.Common`
- Använd `Guid` som TId om inte domänkravet specificerar annat
- Konstruktorer är alltid `private`; exponera skapande via statisk `Create()`-metod
- Registrera domänhändelser med `AddDomainEvent()` — publicera aldrig direkt

```csharp
// Rätt mönster
public sealed class Order : AggregateRoot<OrderId>
{
    private Order() { }

    public static Result<Order> Create(CustomerId customerId, Address shippingAddress)
    {
        // validering
        var order = new Order { Id = OrderId.New() };
        order.AddDomainEvent(new OrderCreatedEvent(order.Id));
        return Result<Order>.Success(order);
    }
}
```

## Felhantering
- Returnera alltid `Result<T>` från public domänmetoder — aldrig `null`, aldrig exception
- Använd `DomainError` med en semantisk felkod (t.ex. `"Order.InvalidStatus"`)
- Kasta `InvalidOperationException` BARA vid programmeringsfel, aldrig vid affärsbegränsningar

## Value Objects
- Implementera som `sealed record` med privat konstruktor
- `Create()`-metoden validerar och returnerar `Result<T>`
- Exponera aldrig råvärdet som primitiv — använd den semantiska typen

```csharp
public sealed record EmailAddress
{
    public string Value { get; }
    private EmailAddress(string value) => Value = value;

    public static Result<EmailAddress> Create(string? raw)
    {
        if (string.IsNullOrWhiteSpace(raw)) return Result<EmailAddress>.Failure(
            new DomainError("Email.Empty", "E-postadress får inte vara tom"));
        if (!raw.Contains('@')) return Result<EmailAddress>.Failure(
            new DomainError("Email.InvalidFormat", "Ogiltig e-postadress"));
        return Result<EmailAddress>.Success(new EmailAddress(raw.Trim().ToLowerInvariant()));
    }
}
```

## Domänhändelser
- Namnge händelser i past tense: `OrderPlacedEvent`, `OrderCancelledEvent`
- Implementera som `sealed record` med `IDomainEvent`
- Inkludera alltid aggregatets Id och tidsstämpel

## Namnkonventioner
- Aggregat: singular substantiv i PascalCase (`Order`, `Customer`, `Shipment`)
- Commands: verb + substantiv + "Command" (`PlaceOrderCommand`)
- Events: verb i past tense + "Event" (`OrderPlacedEvent`)
- Value Objects: beskrivande substantiv (`EmailAddress`, `Money`, `Address`)

## Förbjudna mönster
- Använd ALDRIG `DbContext` eller infrastrukturbibliotek i domänlagret
- Kasta ALDRIG `ArgumentException` från domänmetoder — returnera `Result.Failure`
- Ange ALDRIG ett statefullt beroende (service etc.) direkt på ett aggregat
- Skapa ALDRIG aggregat med `new` utanför dess `Create()`-metod
- Exponera ALDRIG `List<T>` — använd alltid `IReadOnlyList<T>`

Iterativ förbättring — när instruktionerna inte räcker

Instruktionsfiler är levande dokument. Uppdatera dem när:

Förbättringsloop
  1. Copilot genererar fel kod (regel saknas)
  2. Identifiera vilken regel som saknades
  3. Formulera regeln imperativt ("Gör X aldrig / alltid / när Y")
  4. Lägg till i rätt instruktionsfil under lämplig rubrik
  5. Testa: be Copilot generera samma kod igen — följs regeln nu?
  6. Committa instruktionsfilen med beskrivande commit-meddelande

Versionshanterings-strategi för instruktionsfiler

# Bra commit-meddelanden för instruktionsfiler

git commit -m "chore(instructions): add rule for Result<T> on public domain methods"
git commit -m "chore(instructions): prohibit DbContext in domain layer"
git commit -m "chore(instructions): add Value Object pattern with code example"
git commit -m "chore(instructions): update test naming convention to MethodName_Scenario_Expected"
Instruktionsfiler som levande dokumentation En välunderhållen instruktionsfil-svit är bättre dokumentation än en wiki — den är alltid aktuell (för att AI:n annars ger fel svar), versionshanterad och direkt kopplad till koden den beskriver. Prioritera dem över traditionell dokumentation.

Referenser

Elektroniska resurser

Böcker

Övningar

Lös övningarna självständigt. Det finns inget facit — lärandet sker i processen.

  1. Generera din första instruktionsfil med AI
    Ta referensimplementationen från del 06. Kör promptmallen ovan med Claude Sonnet 4.6. Granska resultatet: vilka regler stämmer, vilka är för generella, vilka saknas? Redigera filen och ta bort allt som inte är projektspecifikt. Testa den genom att be Copilot generera ett nytt aggregat.
  2. Iterationsövning — hitta gapet
    Med din instruktionsfil aktiv, be Copilot generera ett Value Object. Identifiera minst ett ställe där resultatet inte matchar dina förväntningar. Formulera en ny regel och lägg till den. Upprepa tre gånger. Dokumentera förbättringsloopen.
  3. Instruktionsfil för testlagret
    Skapa tests.instructions.md med applyTo: "tests/**/*.cs". Inkludera: namnkonvention, AAA-struktur, NSubstitute-regler och FluentAssertions-krav. Verifiera att Copilot följer filens regler när du genererar tester för en befintlig klass.

Soloprojektor

Projekt 1 — Komplett instruktionsfil-svit med verifiering Bygg en fullständig instruktionsfil-svit för projektet från del 06: minst fyra .instructions.md-filer med applyTo plus en copilot-instructions.md. Verifiera varje fil genom att: (a) ta bort instruktionsfilen, (b) generera en klass, (c) notera vad som är fel, (d) lägg tillbaka filen och generera igen. Dokumentera skillnaden.
Projekt 2 — Automatisk instruktionsuppdatering (fördjupning) Skapa en .prompt.md-fil vars syfte är att uppdatera en befintlig instruktionsfil baserat på ny kod. Prompten ska ta en ny C#-klass som input och identifiera vilka regler i instruktionsfilen som behöver läggas till eller uppdateras. Testa den på tre olika nya klasser.

← Föregående: Arkitekturgrund Nästa: AI-driven Code Review →