<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Geoffrey Vandiest</title>
    <link>https://vandiest.biz/</link>
    <description>Geoffrey Vandiest on software development: architecture, .NET, Azure, testing and AI-assisted development.</description>
    <language>en</language>
    <lastBuildDate>Sat, 18 Apr 2026 08:00:00 GMT</lastBuildDate>
    <atom:link href="https://vandiest.biz/rss.xml" rel="self" type="application/rss+xml"/>
    <item>
      <title>Spec-Driven Development with AI: A Practical Guide to Staying in Control</title>
      <link>https://vandiest.biz/post/2026/04/18/spec-driven-development-with-ai-a-practical-guide-to-staying-in-control/</link>
      <guid isPermaLink="true">https://vandiest.biz/post/2026/04/18/spec-driven-development-with-ai-a-practical-guide-to-staying-in-control/</guid>
      <pubDate>Sat, 18 Apr 2026 08:00:00 GMT</pubDate>
      <category>AI development</category>
      <category>Testing</category>
      <category>Architecture</category>
      <description><![CDATA[<p><img src="https://vandiest.biz/img/2026/spec-driven-development-with-ai-a-practical-guid-01.webp" alt width="800" height="1200" loading="lazy"><br>AI coding tools have made it remarkably easy to generate software at speed. They can scaffold features, sketch architecture, wire endpoints, and fill in boilerplate in a matter of minutes. That speed is real, and in the right hands it is valuable. But it also creates a new kind of risk: teams can move from idea to implementation so quickly that they skip the thinking that keeps a system coherent.</p>
          <p>This article is about that missing discipline. More specifically, it is about the&nbsp;<strong>spec-driven development process</strong>: how to turn a vague feature request into a clear specification, a reviewed plan, and a sequence of small, testable steps that keep the developer in control. The goal is not to slow AI down. It is to give that speed structure, so that what gets built still reflects deliberate design rather than improvised output.</p>
          <p>If Part 1 was about principles and tooling, this article is about workflow. We will look at how specifications, plans, and Red-Green-Refactor with human gates fit together into a practical process you can actually use on a real project.</p>
          <hr>
          <h2>The Problem: Vibe Coding Feels Fast Until It Doesn't</h2>
          <p>Most teams experimenting with AI coding assistants fall into the same trap.</p>
          <p>Someone types:</p>
          <blockquote>
          <p>"Add a subscriptions feature to the app."</p>
          </blockquote>
          <p>The model starts working. Entities appear. Repositories show up. Controllers and DTOs materialize. A page gets scaffolded. Twenty minutes later there are fifteen new files across half the solution, and at first glance it all looks plausible.</p>
          <p>That is exactly the danger.</p>
          <p>The code often&nbsp;<em>looks</em>&nbsp;coherent long before it actually is. The entity does not match the database schema. The DTO exposes fields the UI never uses and misses fields it needs. The controller returns a shape the client does not expect. Naming drifts away from the rest of the codebase. Tests are missing or meaningless. And the developer, who is still accountable for the result, can no longer explain why the design looks the way it does.</p>
          <p>That is what I mean by&nbsp;<strong>vibe coding</strong>: using AI in a way that creates the&nbsp;<em>feeling</em>&nbsp;of momentum while steadily eroding control.</p>
          <p>The alternative is&nbsp;<strong>spec-driven development</strong>. Instead of asking the AI to improvise, you give it a clear contract, a reviewed plan, and a tight execution loop. The model still writes code, but it does so inside a process the developer controls.</p>
          <hr>
          <h2>The Three Pillars</h2>
          <p>In practice, spec-driven development with AI rests on three pillars:</p>
          <ol>
          <li><strong>Specifications</strong>&nbsp;&mdash; define what should be built before code exists.</li>
          <li><strong>Plans</strong>&nbsp;&mdash; break the work into small vertical slices before implementation starts.</li>
          <li><strong>Red-Green-Refactor with human gates</strong>&nbsp;&mdash; implement one verified step at a time.</li>
          </ol>
          <p>Each pillar solves a different failure mode.</p>
          <ul>
          <li>Specs stop the AI from guessing.</li>
          <li>Plans stop the AI from wandering across the architecture.</li>
          <li>Red-Green-Refactor with gates stops mistakes from compounding across multiple steps.</li>
          </ul>
          <p>Once these three are in place, the AI becomes much more useful. It stops behaving like an overeager intern and starts behaving like a disciplined teammate.</p>
          <hr>
          <h2>Pillar 1: Specifications &mdash; The Contract Between the Developer and the Model</h2>
          <h3>Why Specs Matter</h3>
          <p>Without a spec, the AI is forced to infer intent from a vague prompt.</p>
          <p>"Add subscriptions" sounds simple, but it leaves too many decisions open:</p>
          <ul>
          <li>What properties does a subscription have?</li>
          <li>Who is allowed to create one?</li>
          <li>Which validation rules apply?</li>
          <li>Which endpoints are required?</li>
          <li>What happens when there are no subscriptions?</li>
          <li>What is intentionally out of scope?</li>
          </ul>
          <p>The model will happily answer all of those questions for you. The problem is that it answers them by guessing.</p>
          <p>A good specification removes that guesswork. It captures the things the AI should not invent for itself:</p>
          <ul>
          <li><strong>Who</strong>&nbsp;the feature is for</li>
          <li><strong>What</strong>&nbsp;the user can do</li>
          <li><strong>Acceptance criteria</strong>&nbsp;for done</li>
          <li><strong>Data model</strong>&nbsp;and constraints</li>
          <li><strong>API surface</strong></li>
          <li><strong>Business rules</strong>&nbsp;and expected error messages</li>
          <li><strong>Edge cases</strong></li>
          <li><strong>Out-of-scope items</strong></li>
          </ul>
          <p>Once this exists, the developer and the AI are working from the same contract.</p>
          <h3>A Concrete Spec Example</h3>
          <p>Here is a realistic spec for a "Patient Subscriptions" feature:</p>
          <pre><code class="language-csharp"># Spec: Patient Subscriptions

## User Story

**As a** patient,
**I want to** view my active medication subscriptions,
**So that** I can track which medications are being dispensed on my behalf.

---

## Acceptance Criteria

| # | Given | When | Then |
|---|-------|------|------|
| AC-1 | Patient is authenticated | They navigate to /subscriptions | A list of active subscriptions is displayed |
| AC-2 | Patient has no subscriptions | They navigate to /subscriptions | An empty-state message is shown |
| AC-3 | A subscription has expired | The list is loaded | Expired subscriptions are not shown |
| AC-4 | Doctor creates a valid subscription | They submit the form | The subscription appears in the patient's list |
| AC-5 | Doctor submits invalid data | They click Save | Validation errors are shown and nothing is persisted |

---

## Data Model

### Entity: `Subscription`

| Property | Type | Required | Constraints |
|----------|------|----------|-------------|
| Id | int | PK | Auto-generated |
| PatientId | int | Yes | FK -&gt; Patient |
| MedicationName | string | Yes | Max 200 chars |
| StartDate | DateOnly | Yes | Must be today or later |
| EndDate | DateOnly | No | Must be after StartDate |
| CreatedBy | string | Yes | Doctor's national register ID |

---

## API Endpoints

### `GET /api/subscriptions?patientId={id}`
**Purpose**: Return active subscriptions for a patient.
**Auth**: Bearer (patient or doctor role)
**Response** (200):
```json
[
  {
    "id": 1,
    "medicationName": "Metformin 500mg",
    "startDate": "2026-04-01",
    "endDate": "2026-10-01"
  }
]
```
**Error responses**: 401 -&gt; Unauthorized, 403 -&gt; Wrong patient

### `POST /api/subscriptions`
**Purpose**: Create a new subscription.
**Auth**: Bearer (doctor role only)
**Request body**:
```json
{
  "patientId": 42,
  "medicationName": "Metformin 500mg",
  "startDate": "2026-04-01",
  "endDate": "2026-10-01"
}
```
**Response** (201): Created subscription
**Error responses**: 400 -&gt; Validation errors, 401, 403

---

## Business Rules

| # | Rule | Error message |
|---|------|---------------|
| BR-1 | MedicationName is required | "Medication name is required" |
| BR-2 | StartDate must be today or later | "Start date cannot be in the past" |
| BR-3 | EndDate, if provided, must be after StartDate | "End date must be after start date" |
| BR-4 | Only doctors can create subscriptions | HTTP 403 |

---

## Out of Scope

- Editing or deleting subscriptions
- Medication lookup or auto-complete
- Notifications when a subscription is created
- Prescription history</code></pre>
          <p>This is not documentation for documentation's sake. It is the minimum structure needed to stop the AI from improvising on critical details.</p>
          <h3>What the Spec Actually Buys You</h3>
          <p>The value of a spec is not abstract. It shows up immediately in review.</p>
          <p>If the AI writes validation logic using&nbsp;<code>&gt;=</code>&nbsp;instead of&nbsp;<code>&gt;</code>, you can point directly to BR-3. If it adds edit and delete endpoints, you can point to the out-of-scope section. If it makes&nbsp;<code>EndDate</code>&nbsp;required, you can point to the data model.</p>
          <p>That changes the developer's role. Instead of arguing with code, you are validating implementation against an agreed contract.</p>
          <h3>Writing the Spec with AI</h3>
          <p>This is one of the most useful ways to use AI early in the process.</p>
          <p>You do not need to sit down and write the whole spec from a blank page. A better approach is an&nbsp;<strong>interview-style prompt</strong>&nbsp;that asks focused questions and assembles the answers into a structured&nbsp;<code>_specs/&lt;FeatureName&gt;.md</code>&nbsp;file.</p>
          <p>That is why this repo now includes a dedicated&nbsp;<code>write-spec</code>&nbsp;prompt. The workflow is simple:</p>
          <ol>
          <li>The developer brings the feature idea.</li>
          <li>The AI runs a short Q&amp;A session.</li>
          <li>The AI drafts the user story, acceptance criteria, data model, API surface, business rules, edge cases, and out-of-scope section.</li>
          <li>The developer reviews and adjusts the spec.</li>
          <li>Only then does planning begin.</li>
          </ol>
          <p>This matters because the interview happens at the right level. You are discussing intent, not classes and folders. That keeps the conversation grounded in product and business behaviour before implementation details take over.</p>
          <h3>When to Write Specs</h3>
          <p>Not every change needs a specification.</p>
          <ul>
          <li><strong>Spec required</strong>: new feature, new user-facing behaviour, new API surface</li>
          <li><strong>Spec optional</strong>: larger refactor, infrastructure change, architecture decision</li>
          <li><strong>No spec needed</strong>: bugfix, config correction, 1-2 file change</li>
          </ul>
          <p>In this workflow, specs live at&nbsp;<code>_specs/&lt;FeatureName&gt;.md</code>&nbsp;in the repository root.</p>
          <hr>
          <h2>Pillar 2: Plans &mdash; Turning Intent into an Execution Strategy</h2>
          <h3>From Spec to Plan</h3>
          <p>The spec defines&nbsp;<strong>what</strong>&nbsp;should exist. The plan defines&nbsp;<strong>how</strong>&nbsp;the work will be delivered.</p>
          <p>That distinction matters.</p>
          <p>The spec says, "patients can view subscriptions" and "doctors can create subscriptions." The plan says, "first build the list page with a stubbed service, then wire it to the real API, then build the create form, then wire the create flow." The plan translates intent into an ordered set of implementation steps.</p>
          <p>A plan lives at&nbsp;<code>_plans/&lt;FeatureName&gt;.md</code>. Each step is one Red-Green-Refactor cycle with explicit scope, tests, proof criteria, and a human gate.</p>
          <h3>The Planning Gate</h3>
          <p>Before any code is written, the plan must be reviewed and approved.</p>
          <p>This is the hard stop between thinking and doing.</p>
          <p>The AI should ask a simple question:</p>
          <blockquote>
          <p>Does this change need a plan?</p>
          </blockquote>
          <table>
          <thead>
          <tr>
          <th>Situation</th>
          <th>Plan required?</th>
          </tr>
          </thead>
          <tbody>
          <tr>
          <td>New feature or vertical slice</td>
          <td><strong>Yes</strong></td>
          </tr>
          <tr>
          <td>Change touching 3+ files</td>
          <td><strong>Yes</strong></td>
          </tr>
          <tr>
          <td>Risk area: auth, PII, DB schema, shared contracts</td>
          <td><strong>Yes</strong></td>
          </tr>
          <tr>
          <td>1-2 file bugfix</td>
          <td>No, but still test-first</td>
          </tr>
          <tr>
          <td>Config correction or simple refactor</td>
          <td>No</td>
          </tr>
          </tbody>
          </table>
          <p>If the answer is yes, the AI writes the plan and stops. No production code starts until the developer approves it.</p>
          <p>That single rule prevents a remarkable amount of wasted work.</p>
          <h3>Vertical Slices, Not Horizontal Layers</h3>
          <p>This is the planning principle that matters most.</p>
          <p>The natural instinct, for both humans and models, is to decompose work by layer:</p>
          <pre><code class="language-plaintext">Step 1 — Create the Subscription entity
Step 2 — Add SubscriptionRepository
Step 3 — Create SubscriptionCreateHandler
Step 4 — Add GET endpoint
Step 5 — Add POST endpoint
Step 6 — Build the subscriptions list page
Step 7 — Build the create form</code></pre>
          <p>It looks organized. In reality, it delays feedback and pushes integration risk to the end.</p>
          <p>By the time you reach the UI, you may discover that the entity shape is wrong, the DTO is incomplete, the query returns the wrong projection, or the route contract does not match the client. Now several "completed" steps have to be reopened.</p>
          <p>Vertical slicing avoids that by planning around&nbsp;<strong>user-visible behaviour</strong>&nbsp;instead of technical layers:</p>
          <pre><code class="language-plaintext">Step 1 — Display subscription list (stubbed)
Step 2 — Wire subscription list to real API
Step 3 — Create subscription form (stubbed)
Step 4 — Wire subscription create to real API</code></pre>
          <p>Each slice has two phases:</p>
          <ol>
          <li><strong>Stub</strong>&nbsp;&mdash; build the UI against a fake or mocked service so the user can validate the experience early.</li>
          <li><strong>Wire</strong>&nbsp;&mdash; replace the stub with real production code from controller to persistence and database.</li>
          </ol>
          <p>This gives you three practical advantages:</p>
          <ul>
          <li><strong>You fail fast.</strong>&nbsp;Contract mismatches show up on Step 2 instead of Step 7.</li>
          <li><strong>You get UI feedback early.</strong>&nbsp;The user can review the shape of the feature before backend code sprawls.</li>
          <li><strong>You reduce blast radius.</strong>&nbsp;If something changes, one slice moves, not the whole architecture.</li>
          </ul>
          <h3>A Concrete Plan Example</h3>
          <p>Here is a more realistic plan for the subscriptions feature:</p>
          <pre><code class="language-csharp"># Plan: Patient Subscriptions — Patient views active medication subscriptions

## Overview
Patients can view their active medication subscriptions. Doctors can create new
subscriptions. Reference feature: **Doctors**.

---

## Step 1 — Display subscription list (stubbed)

**Scope** *(Presentation only — stub phase)*:
- `src/Presentation/Subscriptions/ViewModels/SubscriptionsViewModel.cs` *(create)*
- `src/Presentation/Subscriptions/ServiceClients/ISubscriptionServiceClient.cs` *(create)*
- `src/Presentation/Subscriptions/ServiceClients/SubscriptionServiceClientStub.cs` *(create)*
- `src/Presentation/Subscriptions/Pages/Subscriptions.razor` *(create)*
- `src/Contracts/Subscriptions/Api/SubscriptionDto.cs` *(create)*
- `src/Test/Unit/Presentation/Subscriptions/SubscriptionsViewModelTests.cs` *(create)*

**RED**:
- Test: `InitializeAsync_WithSubscriptions_LoadsList`
- Command: `{{TestExePath}} --filter "SubscriptionsViewModelTests"`

**GREEN**:
- `SubscriptionDto` with Id, MedicationName, StartDate, EndDate
- `ISubscriptionServiceClient` with `GetByPatientAsync(int patientId)`
- Stub service returning three fake subscriptions
- `SubscriptionsViewModel` with `IsBusy` guard and `InitializeAsync`
- `Subscriptions.razor` bound to `ViewModel.Items`

**DB changes**: none

**🛑 HUMAN GATE**:
- [ ] Behavioral: ViewModel test passes and the page renders three stubbed items
- [ ] Review: Page layout matches the reference feature pattern

---

## Step 2 — Wire subscription list to real API

**Scope** *(all backend layers — wire phase)*:
- `src/Core/Domain/Functionalities/Subscriptions/Subscription.cs` *(create)*
- `src/Core/Persistence/EntityTypeConfigurations/SubscriptionConfiguration.cs` *(create)*
- `src/Core/Persistence/Repositories/SubscriptionRepository.cs` *(create)*
- `src/Core/Application/Functionalities/Subscriptions/Queries/GetSubscriptions/` *(create)*
- `src/Host/Client/Controllers/SubscriptionController.cs` *(create — GET only)*
- `src/Database/Scripts/0001_CreateSubscriptionTable.sql` *(create)*
- `src/Presentation/Subscriptions/ServiceClients/SubscriptionServiceClient.cs` *(create — real Refit-backed service client)*
- `src/Presentation/Shared/ServiceClients/Bff/Clients/ISubscriptionClient.cs` *(create)*
- `src/Test/Unit/Application/Subscriptions/GetSubscriptionsQueryTests.cs` *(create)*
- `src/Test/Integration/Endpoints/Subscriptions/GetSubscriptionsTest.cs` *(create)*

**RED**:
- Unit: `Execute_WithActiveSubscriptions_ReturnsList`
- Integration: `GetSubscriptions_Authenticated_ReturnsOk`
- Integration: `GetSubscriptions_WrongPatient_ReturnsForbidden`

**GREEN**:
- `Subscription` entity and repository
- `IGetSubscriptionsQuery` and `GetSubscriptionsQuery`
- `SubscriptionController` GET `/api/subscriptions?patientId={id}`
- Refit client + real service client replacing the stub
- EF configuration + DbUp script

**DB changes**: `src/Database/Scripts/0001_CreateSubscriptionTable.sql`

**🛑 HUMAN GATE**:
- [ ] Behavioral: Integration tests pass and GET `/api/subscriptions` returns the expected list
- [ ] Review: Entity, query, repository, and controller follow the reference pattern

---

## Step 3 — Create subscription form (stubbed)

**Scope** *(Presentation only — stub phase)*:
- `src/Presentation/Subscriptions/ViewModels/AddSubscriptionViewModel.cs` *(create)*
- `src/Presentation/Subscriptions/Pages/AddSubscription.razor` *(create)*
- `src/Contracts/Subscriptions/Api/AddSubscriptionDto.cs` *(create)*
- `src/Test/Unit/Presentation/Subscriptions/AddSubscriptionViewModelTests.cs` *(create)*

**RED**:
- Test: `Submit_EmptyMedicationName_ShowsValidationError`
- Test: `Submit_ValidData_CallsService`

**GREEN**:
- `AddSubscriptionDto`
- `AddSubscriptionViewModel` with BR-1 through BR-3 validation
- Stub submission flow
- `AddSubscription.razor` with validated fields

**DB changes**: none

**🛑 HUMAN GATE**:
- [ ] Behavioral: Validation errors render correctly and valid submission calls the stub service
- [ ] Review: Form matches the business rules in the spec

---

## Step 4 — Wire subscription create to real API

**Scope** *(all backend layers — wire phase)*:
- `src/Core/Application/Functionalities/Subscriptions/Commands/AddSubscription/` *(create)*
- `src/Host/Client/Controllers/SubscriptionController.cs` *(modify — add POST)*
- `src/Presentation/Subscriptions/ServiceClients/SubscriptionServiceClient.cs` *(modify — add AddAsync)*
- `src/Test/Unit/Application/Subscriptions/AddSubscriptionHandlerTests.cs` *(create)*
- `src/Test/Integration/Endpoints/Subscriptions/PostSubscriptionTest.cs` *(create)*

**RED**:
- Unit: `Execute_ValidCommand_ReturnsSuccess`
- Unit: `Execute_PastStartDate_ReturnsFailure`
- Integration: `PostSubscription_ValidData_ReturnsCreated`
- Integration: `PostSubscription_MissingFields_ReturnsBadRequest`

**GREEN**:
- `AddSubscriptionCommand` and `AddSubscriptionCommandHandler`
- POST `/api/subscriptions`
- Real `AddAsync` implementation in the service client

**DB changes**: none

**🛑 HUMAN GATE**:
- [ ] Behavioral: Integration tests pass; valid POST creates a record; invalid POST returns 400
- [ ] Review: Authorization and business rules are enforced correctly</code></pre>
          <p>The important thing here is not the exact class names. It is the shape of the plan:</p>
          <ul>
          <li>step names describe behaviour</li>
          <li>each slice is completed before the next starts</li>
          <li>the stub phase is intentionally narrow</li>
          <li>the wire phase crosses layers on purpose</li>
          <li>every step has explicit proof and review criteria</li>
          </ul>
          <h3>The Interview Before the Plan</h3>
          <p>Good planning starts with the right questions.</p>
          <p>Before an AI writes a plan, it should confirm that it has enough information to do so safely:</p>
          <ol>
          <li>Which existing feature is the reference pattern?</li>
          <li>What entities and properties are involved?</li>
          <li>Is there a database change?</li>
          <li>Are there relationships to existing entities?</li>
          <li>Which endpoints are needed?</li>
          <li>Which UI pages or routes are needed?</li>
          <li>Are there auth or claims requirements?</li>
          </ol>
          <p>If any of that is missing, the AI should ask now, not halfway through implementation.</p>
          <hr>
          <h2>Pillar 3: Red-Green-Refactor with Human Gates</h2>
          <h3>The Core Loop</h3>
          <p>Every plan step should run through the same execution loop:</p>
          <pre><code class="language-plaintext">READ      — Read the plan step and understand the scope.
RED       — Write the failing test first. Run it. Confirm it fails.
GREEN     — Write the minimum production code needed to pass.
REFACTOR  — Clean up without changing behaviour.
ANALYSE   — Run static analysis and fix issues.
PROVE     — Build, test, and format checks all pass.
🛑 STOP   — Present results and wait for human approval.
MARK DONE — After approval, update the plan checkboxes.</code></pre>
          <p>Three rules are non-negotiable:</p>
          <ol>
          <li><strong>Never skip RED.</strong>&nbsp;The test must fail before production code exists.</li>
          <li><strong>Never batch steps.</strong>&nbsp;One step, one proof cycle, one review.</li>
          <li><strong>Never skip the human gate.</strong>&nbsp;Approval is part of the workflow, not an optional extra.</li>
          </ol>
          <h3>Why RED Must Come First</h3>
          <p>The most common AI mistake is writing the test and the production code at the same time.</p>
          <p>It feels efficient, but it breaks the feedback loop.</p>
          <p>If the test was never observed failing, you do not know whether it would have caught a real defect. The assertion may be wrong. The mock setup may be too permissive. The test may accidentally validate nothing.</p>
          <p>When the test fails first and then passes after the code change, the relationship is clear. The test is meaningful, and the production code is the reason it now passes.</p>
          <p>That matters even more with AI-generated tests, because they often look correct while hiding subtle mistakes.</p>
          <h3>RED-First in Practice</h3>
          <p>Take Step 2 from the subscriptions example: return active subscriptions for a patient.</p>
          <p>The AI starts by writing the failing unit test:</p>
          <pre><code class="language-csharp">[TestClass]
public class GetSubscriptionsQueryTests
{
    [TestMethod]
    public async Task Execute_WithActiveSubscriptions_ReturnsList()
    {
        // Arrange
        var mockRepo = new Mock&lt;ISubscriptionRepository&gt;();
        mockRepo.Setup(r =&gt; r.GetActiveByPatientAsync(42))
            .ReturnsAsync(new List&lt;Subscription&gt;
            {
                new()
                {
                    Id = 1,
                    PatientId = 42,
                    MedicationName = "Metformin 500mg",
                    StartDate = new DateOnly(2026, 4, 1),
                    EndDate = new DateOnly(2026, 10, 1),
                },
            });

        var query = new GetSubscriptionsQuery(mockRepo.Object);

        // Act
        var result = await query.Execute(42);

        // Assert
        Assert.AreEqual(1, result.Count);
        Assert.AreEqual("Metformin 500mg", result[0].MedicationName);
    }
}</code></pre>
          <p>At this point the test fails, because&nbsp;<code>GetSubscriptionsQuery</code>&nbsp;does not exist yet. Good. That is exactly what should happen.</p>
          <p>Only then does the AI create the minimal production code: the repository contract, the query implementation, the mapping, and the endpoint path needed for the full slice. When the test turns green, you know it turned green for a reason.</p>
          <h3>The Human Gate</h3>
          <p>After the proof step, the AI stops and presents results.</p>
          <p>For example:</p>
          <pre><code class="language-plaintext">Step 2 — Wire subscription list to real API

Build:   0 warnings, 0 errors
Tests:   24 passed, 0 failed
Format:  no changes required

🛑 HUMAN GATE:
- [ ] Integration test GetSubscriptions_Authenticated_ReturnsOk passes
- [ ] Query, repository, controller, and DTO follow the reference feature pattern</code></pre>
          <p>This is where the developer checks the behaviour and the design:</p>
          <ul>
          <li>Does the implementation satisfy the spec?</li>
          <li>Do the tests cover the acceptance criteria?</li>
          <li>Do the file locations and naming match the codebase?</li>
          <li>Are risk areas handled correctly?</li>
          </ul>
          <p>Only after approval does the next step begin.</p>
          <p>That gate is what prevents AI errors from cascading into later steps.</p>
          <h3>Bugfixes: Regression Test First</h3>
          <p>The same principle applies to bugfixes, just with a smaller workflow:</p>
          <ol>
          <li>Investigate the root cause.</li>
          <li>Write a regression test that reproduces the bug.</li>
          <li>Confirm it fails.</li>
          <li>Make the smallest fix.</li>
          <li>Confirm the test passes.</li>
          <li>Run the standard proof commands.</li>
          </ol>
          <p>This keeps bugfixes honest. The AI does not get to poke at code until symptoms disappear. It has to prove it understands the failure first.</p>
          <hr>
          <h2>Progress Tracking Across Sessions</h2>
          <p>One of the hardest things about AI-assisted development is that sessions are ephemeral. Context disappears when the conversation ends.</p>
          <p>Plan files solve that with a very simple convention: checkboxes on the human gate.</p>
          <pre><code class="language-plaintext">## Step 1 — Display subscription list (stubbed)
...
**🛑 HUMAN GATE**:
- [x] Behavioral: ViewModel test passes and the list renders with stubbed data
- [x] Review: Layout matches the reference feature

## Step 2 — Wire subscription list to real API
...
**🛑 HUMAN GATE**:
- [ ] Behavioral: Integration tests pass and GET /api/subscriptions returns data
- [ ] Review: Query, repository, and controller follow existing patterns</code></pre>
          <p>When a new session starts, the AI reads the plan and finds the first unchecked box. That is the next step.</p>
          <p>No guesswork. No repeated work. No fragile reliance on chat history.</p>
          <hr>
          <h2>Encoding the Process into AI Instructions</h2>
          <p>What makes this approach practical is that the workflow is not just a team convention. It is encoded into the repo's instructions, agents, prompts, and skills.</p>
          <p>That gives the model structure before the conversation even begins.</p>
          <h3><code>AGENTS.md</code>&nbsp;&mdash; The Workflow Backbone</h3>
          <p>This file defines the planning gate, the mandatory workflow rules, the vertical-slice strategy, and the stop points.</p>
          <p>It is the reason the AI knows that planning comes before coding and that one step must finish before the next begins.</p>
          <h3><code>planner.agent.md</code>&nbsp;&mdash; The Planning Specialist</h3>
          <p>This agent is deliberately constrained. It can read the codebase and produce&nbsp;<code>_plans/&lt;FeatureName&gt;.md</code>, but it cannot write production code.</p>
          <p>That separation matters. Planning is a different task from implementation, and mixing the two is where many AI workflows lose discipline.</p>
          <h3><code>copilot-instructions.md</code>&nbsp;&mdash; Project Rules and Boundaries</h3>
          <p>This file tells the AI what kind of system it is working in: project structure, naming rules, dependency boundaries, verification commands, and critical architectural constraints.</p>
          <p>It answers questions the AI should not answer by improvisation.</p>
          <h3><code>write-spec.prompt.md</code>&nbsp;&mdash; AI-Assisted Specification Writing</h3>
          <p>This prompt fills an important gap in the process.</p>
          <p>Before planning, it interviews the developer section by section and produces&nbsp;<code>_specs/&lt;FeatureName&gt;.md</code>&nbsp;from the spec template. It turns a vague idea into something concrete enough to plan safely.</p>
          <h3><code>build-feature/SKILL.md</code>&nbsp;&mdash; The Execution Engine</h3>
          <p>This skill tells the AI how to execute an approved plan step using the right layer patterns and proof loop. It does not replace judgement, but it gives the model reliable rails.</p>
          <h3>Supporting Project-Level Folders</h3>
          <p>The wider workflow also benefits from a small set of project-level folders with clear intent:</p>
          <ul>
          <li><code>_specs/</code>&nbsp;for feature specifications</li>
          <li><code>_plans/</code>&nbsp;for approved implementation plans</li>
          <li><code>_decisions/</code>&nbsp;for ADRs and architectural choices</li>
          <li><code>_qa/</code>&nbsp;for smoke-test plans and QA artifacts</li>
          <li><code>_infrastructure/</code>&nbsp;for infrastructure specs and environment records</li>
          </ul>
          <p>None of these are complicated. Their value comes from being explicit, predictable, and easy for both humans and AI to discover.</p>
          <hr>
          <h2>Putting It All Together</h2>
          <p>At a high level, the workflow looks like this:</p>
          <pre><code class="language-plaintext">Idea -&gt; Spec -&gt; Plan -&gt; Step 1 -&gt; Review -&gt; Step 2 -&gt; Review -&gt; Step 3 -&gt; Review -&gt; Step 4 -&gt; Review</code></pre>
          <p>More concretely:</p>
          <ol>
          <li>A developer identifies a feature.</li>
          <li>The AI helps write the spec through a focused Q&amp;A session.</li>
          <li>The planner reads the spec and the codebase, then proposes a vertical-slice plan.</li>
          <li>The developer approves the plan.</li>
          <li>The AI implements one step at a time using Red-Green-Refactor.</li>
          <li>After each step, the AI proves the result and stops for review.</li>
          <li>The plan file tracks progress across sessions.</li>
          </ol>
          <p>This keeps the developer in control of design while still capturing the speed benefits of AI-assisted implementation.</p>
          <hr>
          <h2>Summary</h2>
          <table>
          <thead>
          <tr>
          <th>Aspect</th>
          <th>Vibe Coding</th>
          <th>Spec-Driven Development</th>
          </tr>
          </thead>
          <tbody>
          <tr>
          <td>Starting point</td>
          <td>Vague prompt</td>
          <td>Structured spec</td>
          </tr>
          <tr>
          <td>Planning</td>
          <td>None or implicit</td>
          <td>Explicit plan approved before coding</td>
          </tr>
          <tr>
          <td>Decomposition</td>
          <td>Ad hoc or by layer</td>
          <td>By user-visible behaviour</td>
          </tr>
          <tr>
          <td>Testing</td>
          <td>Optional or late</td>
          <td>RED-first and mandatory</td>
          </tr>
          <tr>
          <td>Verification</td>
          <td>Eyeballing the output</td>
          <td>Build, tests, analysis, format</td>
          </tr>
          <tr>
          <td>Human involvement</td>
          <td>Mostly at the end</td>
          <td>At every gate</td>
          </tr>
          <tr>
          <td>Session continuity</td>
          <td>Fragile</td>
          <td>Tracked in&nbsp;<code>_plans/</code></td>
          </tr>
          <tr>
          <td>Accountability</td>
          <td>Diffuse</td>
          <td>Clear: human owns intent, AI executes</td>
          </tr>
          </tbody>
          </table>
          <p>The shift from vibe coding to spec-driven development is not about producing more documents. It is about preserving control.</p>
          <p>The spec makes intent explicit. The plan makes execution deliberate. Red-Green-Refactor makes each change verifiable. Human gates keep the developer accountable.</p>
          <p>AI is most useful when it accelerates a process you already trust. Without that process, speed just multiplies ambiguity.</p>
          <p>With it, you get the best of both worlds: faster delivery and stronger control.</p>
          <hr>
          <p><em>The practices described in this article are encoded in the open-source&nbsp;<a href="https://github.com/geobarteam/github-copilot-configs">github-copilot-configs</a>&nbsp;repository &mdash; a reusable template library of GitHub Copilot and Claude Code configuration files for .NET projects.</em></p>
          <blockquote>
          <p><strong>Previous:</strong>&nbsp;<a href="https://vandiest.biz/post/2026/04/15/from-vibe-coding-to-spec-driven-development-a-reusable-ai-agent-configuration-for-net-projects/">Part 1 &mdash; Best Practices for AI-Assisted Coding</a></p>
          </blockquote>]]></description>
    </item>
    <item>
      <title>From Vibe Coding to Spec-Driven Development: A Reusable AI Agent Configuration for .NET Projects</title>
      <link>https://vandiest.biz/post/2026/04/15/from-vibe-coding-to-spec-driven-development-a-reusable-ai-agent-configuration-for-net-projects/</link>
      <guid isPermaLink="true">https://vandiest.biz/post/2026/04/15/from-vibe-coding-to-spec-driven-development-a-reusable-ai-agent-configuration-for-net-projects/</guid>
      <pubDate>Wed, 15 Apr 2026 08:00:00 GMT</pubDate>
      <category>AI development</category>
      <category>Testing</category>
      <category>Architecture</category>
      <description><![CDATA[<p><img src="https://vandiest.biz/img/2026/from-vibe-coding-to-spec-driven-development-a-re-01.png" alt width="800" height="533" loading="lazy"><a title="github-copilot-configs" href="https://github.com/geobarteam/github-copilot-configs">github-copilot-configs</a></p>
          <p><em>An open-source library of GitHub Copilot and Claude Code configuration files that encode a disciplined, repeatable engineering process into AI agents, skills, and instructions.</em></p>
          <blockquote>
          <p><strong>Repository:</strong>&nbsp;<strong>Part 1 of 2.</strong>&nbsp;This article covers the motivation, best practices, and the configuration library. For a hands-on walkthrough of the spec-driven process, see&nbsp;<a href="https://github.com/geobarteam/github-copilot-configs/blob/master/BLOGPOST-PROCESS.md">Part 2: The Process in Action</a>.</p>
          </blockquote>
          <hr>
          <div>
          <h2>Why I Built This</h2>
          <a href="https://github.com/geobarteam/github-copilot-configs/blob/master/BLOGPOST.md#why-i-built-this"></a></div>
          <p>Over the past year I've spent hundreds of hours working with AI coding agents &mdash; GitHub Copilot, Claude Code, and various MCP tool integrations. I learned a lot, made plenty of mistakes, and eventually landed on a workflow that works reliably.</p>
          <p>The problem was: every time I started a new project, I was rebuilding the same configuration from scratch. The same planning discipline. The same Red-Green-Refactor loop. The same human gates. The same layer-specific coding conventions. Each project had slight variations, and none of them captured everything I'd learned.</p>
          <p>So I built&nbsp;<strong><a href="https://github.com/geobarteam/github-copilot-configs">github-copilot-configs</a></strong>&nbsp;&mdash; a reusable template library of AI agent configuration files that I copy into every new .NET project. It encodes the engineering process I've developed over my career &mdash; now translated into agents, skills, and instruction files that AI assistants follow automatically.</p>
          <p>The goal is twofold:</p>
          <ol>
          <li><strong>Quick project setup</strong>&nbsp;&mdash; Copy the files, run the&nbsp;<code>/init</code>&nbsp;skill, and every AI assistant in the project immediately knows the architecture, conventions, test commands, and development workflow. No more explaining the same things in every chat session.</li>
          <li><strong>Consistent engineering process</strong>&nbsp;&mdash; The agents enforce plan-first development, test-first implementation, vertical slicing, and mandatory human review gates. This isn't just guidance &mdash; it's a process that prevents the most common AI coding failures I've encountered.</li>
          </ol>
          <p>This article shares what I've learned and how the configuration library works. Whether you adopt it wholesale or just borrow ideas, I hope it saves you some of the trial and error I went through.</p>
          <hr>
          <div>
          <h2>The Problem: Vibe Coding</h2>
          <a href="https://github.com/geobarteam/github-copilot-configs/blob/master/BLOGPOST.md#the-problem-vibe-coding"></a></div>
          <p>AI coding assistants like GitHub Copilot are changing how developers write software. But without clear practices, teams drift into what I call&nbsp;<strong>vibe coding</strong>&nbsp;&mdash; letting the AI generate code with no plan, no verification, and no accountability. The result? Code nobody fully understands.</p>
          <hr>
          <div>
          <h2>Part 1: Best Practices for AI-Assisted Coding</h2>
          <a href="https://github.com/geobarteam/github-copilot-configs/blob/master/BLOGPOST.md#part-1-best-practices-for-ai-assisted-coding"></a></div>
          <div>
          <h3>Understand the Core Concepts</h3>
          <a href="https://github.com/geobarteam/github-copilot-configs/blob/master/BLOGPOST.md#understand-the-core-concepts"></a></div>
          <p>Before diving into workflows, every developer should understand three foundational concepts:</p>
          <p><strong>The Model</strong>&nbsp;&mdash; The LLM behind Copilot. Different models have different strengths, costs, and context windows. Choosing the right model for the task is a deliberate decision, not an afterthought.</p>
          <p><strong>The Context</strong>&nbsp;&mdash; Everything the model sees when generating a response: your open files, instructions, conversation history, and workspace structure. Context quality directly determines output quality.</p>
          <p><strong>The Agent</strong>&nbsp;&mdash; The orchestration layer that can inspect files, reason about tasks, propose plans, make changes, run checks, launch commands, and iterate through workflows. Agents interact with tools including MCP servers, making them far more capable than simple chat completions.</p>
          <div>
          <h3>Treat Tokens as a Limited Resource</h3>
          <a href="https://github.com/geobarteam/github-copilot-configs/blob/master/BLOGPOST.md#treat-tokens-as-a-limited-resource"></a></div>
          <p>Tokens &mdash; the unit of model consumption &mdash; are finite on any plan. Practical consequences:</p>
          <ul>
          <li>Use&nbsp;<strong>short, focused sessions</strong></li>
          <li>Keep prompts&nbsp;<strong>specific and bounded</strong></li>
          <li>Choose the&nbsp;<strong>right model for the task</strong></li>
          <li>Avoid unnecessary retries in polluted sessions</li>
          <li>Use&nbsp;<strong>lighter models</strong>&nbsp;for simple work</li>
          <li>Reserve&nbsp;<strong>premium models</strong>&nbsp;for harder problems</li>
          </ul>
          <div>
          <h3>Choose the Right Model for the Job</h3>
          <a href="https://github.com/geobarteam/github-copilot-configs/blob/master/BLOGPOST.md#choose-the-right-model-for-the-job"></a></div>
          <p>Not every task needs the most powerful model. Here's how I think about model selection:</p>
          <table>
          <thead>
          <tr>
          <th>Model</th>
          <th>Best for</th>
          <th>Relative cost</th>
          </tr>
          </thead>
          <tbody>
          <tr>
          <td>Claude Haiku 4.5 / Gemini Flash</td>
          <td>Cheap, simple tasks</td>
          <td>~0.33&times;</td>
          </tr>
          <tr>
          <td>Claude Sonnet 4.6 / GPT-5.3-Codex</td>
          <td>Daily default for coding</td>
          <td>1&times;</td>
          </tr>
          <tr>
          <td>Gemini 2.5 Pro</td>
          <td>Large-context reasoning</td>
          <td>1&times;</td>
          </tr>
          <tr>
          <td>Claude Opus 4.6</td>
          <td>Hardest coding and review tasks</td>
          <td>3&times;</td>
          </tr>
          </tbody>
          </table>
          <p><strong>The rule</strong>: use Haiku or Flash for quick simple work, Sonnet as the daily default, and Opus only when the problem genuinely demands it. Don't burn premium tokens on formatting a JSON file.</p>
          <div>
          <h3>The Biggest Risk: Losing Control</h3>
          <a href="https://github.com/geobarteam/github-copilot-configs/blob/master/BLOGPOST.md#the-biggest-risk-losing-control"></a></div>
          <p>The main danger of AI-assisted coding isn't wrong code &mdash; it's&nbsp;<strong>no longer knowing what the AI changed, why it changed it, or whether it still matches the design</strong>.</p>
          <table>
          <thead>
          <tr>
          <th>Vibe Coding</th>
          <th>Spec-Driven Development</th>
          </tr>
          </thead>
          <tbody>
          <tr>
          <td>Vague goal, no constraints</td>
          <td>Clear goal, explicit constraints, reviewed plan</td>
          </tr>
          <tr>
          <td>Long unstructured sessions</td>
          <td>Short iterations with build/test/review gates</td>
          </tr>
          <tr>
          <td>AI decides design and validation</td>
          <td>You stay accountable for design, code, and validation</td>
          </tr>
          </tbody>
          </table>
          <p>The antidote is&nbsp;<strong>spec-driven development</strong>: start with a specification, produce a plan, implement in small verified steps, and stay in control throughout. That's what this configuration library encodes.</p>
          <div>
          <h3>Keep Sessions Short</h3>
          <a href="https://github.com/geobarteam/github-copilot-configs/blob/master/BLOGPOST.md#keep-sessions-short"></a></div>
          <p>A good AI coding session has one task, one goal, a small scope, only relevant files, and clear validation criteria. A bad session involves multiple unrelated tasks, too much history, broad exploration, repeated retries, and unclear ownership.</p>
          <p><strong>Rule: if the topic changes, start a new session.</strong>&nbsp;Session pollution &mdash; accumulated irrelevant context &mdash; is one of the most common causes of degraded AI output.</p>
          <div>
          <h3>Anthropic Principle #1: Give the Model a Way to Verify Its Work</h3>
          <a href="https://github.com/geobarteam/github-copilot-configs/blob/master/BLOGPOST.md#anthropic-principle-1-give-the-model-a-way-to-verify-its-work"></a></div>
          <p>Verification is the&nbsp;<strong>single biggest improvement</strong>&nbsp;you can make in AI-assisted coding.</p>
          <p>What counts as verification:</p>
          <ul>
          <li>Failing tests that turn green</li>
          <li>Successful builds</li>
          <li>Static analyzers passing</li>
          <li>Expected output matching</li>
          <li>Deterministic scripts</li>
          </ul>
          <p>Without verification, the AI produces plausible but potentially wrong code, and you become the only feedback loop &mdash; a feedback loop that gets tired and loses attention.</p>
          <p>In my workflow, every change follows:&nbsp;<strong>RED &rarr; GREEN &rarr; REFACTOR &rarr; ANALYSIS &rarr; PROOF &rarr; HUMAN GATE</strong>.</p>
          <div>
          <h3>Anthropic Principle #2: Explore &rarr; Specify &rarr; Plan &rarr; Implement &rarr; Commit</h3>
          <a href="https://github.com/geobarteam/github-copilot-configs/blob/master/BLOGPOST.md#anthropic-principle-2-explore--specify--plan--implement--commit"></a></div>
          <p>This is the workflow backbone:</p>
          <ol>
          <li><strong>Explore</strong>&nbsp;&mdash; Understand the codebase, existing patterns, and the real problem</li>
          <li><strong>Specify</strong>&nbsp;&mdash; Write a detailed, AI-friendly specification (acceptance criteria, constraints, edge cases)</li>
          <li><strong>Plan</strong>&nbsp;&mdash; Produce a concrete implementation plan&nbsp;<em>before</em>&nbsp;code changes</li>
          <li><strong>Implement</strong>&nbsp;&mdash; Execute one step at a time and verify after each step</li>
          <li><strong>Commit</strong>&nbsp;&mdash; Checkpoint small, validated changes in Git</li>
          </ol>
          <p>Planning is most valuable for new features, multi-file changes, or risk areas. Tiny fixes can be done directly &mdash; but still verified.</p>
          <div>
          <h3>Anthropic Principle #3: Provide Specific Context</h3>
          <a href="https://github.com/geobarteam/github-copilot-configs/blob/master/BLOGPOST.md#anthropic-principle-3-provide-specific-context"></a></div>
          <p>Good prompts are not long prompts &mdash; they are&nbsp;<strong>scoped, specific, and verifiable</strong>.</p>
          <table>
          <thead>
          <tr>
          <th>Weak Prompt</th>
          <th>Better Prompt</th>
          </tr>
          </thead>
          <tbody>
          <tr>
          <td>"Fix the login bug."</td>
          <td>"Reproduce the session-timeout bug, inspect auth flow, write a failing test, fix root cause, verify it passes."</td>
          </tr>
          <tr>
          <td>"Add an endpoint."</td>
          <td>"Add GET endpoint X using feature Y as reference pattern; no new table; add tests."</td>
          </tr>
          <tr>
          <td>"Improve this page."</td>
          <td>"Use existing page pattern, keep same components, add validation and proof steps."</td>
          </tr>
          </tbody>
          </table>
          <p>The pattern is always the same: scope the task, reference existing patterns, and define what "done" looks like.</p>
          <hr>
          <div>
          <h2>Part 2: The "github-copilot-configs"&nbsp;library &mdash; What's Inside</h2>
          <a href="https://github.com/geobarteam/github-copilot-configs/blob/master/BLOGPOST.md#part-2-the-configuration-library--whats-inside"></a></div>
          <p>Knowing best practices is one thing. Making every AI session follow them automatically is another. That's what the&nbsp;<a href="https://github.com/geobarteam/github-copilot-configs">github-copilot-configs</a>&nbsp;library does &mdash; it encodes your architecture, conventions, and development process into configuration files that GitHub Copilot and Claude Code follow without you repeating yourself every session.</p>
          <p><strong>Target stack</strong>: ASP.NET Core + Blazor WebAssembly &middot; Onion/Screaming Architecture &middot; CQRS-lite &middot; .NET 10. But the patterns and workflow are adaptable to any .NET project.</p>
          <div>
          <h3>Agents &mdash; Specialized AI Roles</h3>
          <a href="https://github.com/geobarteam/github-copilot-configs/blob/master/BLOGPOST.md#agents--specialized-ai-roles"></a></div>
          <p>Agents are invoked via&nbsp;<code>@name</code>&nbsp;in VS Code chat. Each is a specialized role with its own constraints, workflow, and tools.</p>
          <table>
          <thead>
          <tr>
          <th>Agent</th>
          <th>What it does</th>
          </tr>
          </thead>
          <tbody>
          <tr>
          <td><strong><code>@planner</code></strong></td>
          <td>Reads&nbsp;<code>_specs/&lt;Feature&gt;.md</code>&nbsp;(if it exists) and creates&nbsp;<code>_plans/&lt;Feature&gt;.md</code>&nbsp;with vertical-slice steps before any code is written. Interviews you about the feature, reads the reference pattern, decomposes into testable behavior slices. Never writes production code &mdash; planning only.</td>
          </tr>
          <tr>
          <td><strong><code>@bugfix</code></strong></td>
          <td>Diagnoses bugs using regression-test-first discipline. Writes a test that reproduces the bug, confirms it fails, then writes the minimal fix. Escalates to&nbsp;<code>@planner</code>&nbsp;if the fix spans 3+ files.</td>
          </tr>
          <tr>
          <td><strong><code>@debug</code></strong></td>
          <td>A debug engineer that combines Application Insights log analysis with Playwright browser automation. Queries Azure telemetry for exceptions and traces, reproduces UI issues in a real browser, correlates frontend errors with backend traces, and produces structured debug reports.</td>
          </tr>
          <tr>
          <td><strong><code>@devops</code></strong></td>
          <td>Maintains GitHub Actions CI/CD pipelines and Azure Bicep infrastructure. Knows the exact workflow structure (build &rarr; deploy &rarr; summary), Bicep module conventions, resource naming patterns, and the Architecture.md/Deployment-Info pattern for tracking infrastructure state.</td>
          </tr>
          <tr>
          <td><strong><code>@smoke-test</code></strong></td>
          <td>Post-deployment smoke testing using browser automation. Discovers environment URLs from infrastructure docs, navigates to health endpoints and pages, checks for console errors and network failures, and produces a structured pass/fail report.</td>
          </tr>
          <tr>
          <td><strong><code>@git</code></strong></td>
          <td>GitFlow branching specialist. Creates feature/bugfix/hotfix branches, manages tags, guides PR workflows. Only runs mutating Git commands after explicit confirmation.</td>
          </tr>
          </tbody>
          </table>
          <p>Each agent follows the shared workflow rules in&nbsp;<code>AGENTS.md</code>: plan-first discipline, Red-Green-Refactor loops, and mandatory human gates.</p>
          <div>
          <h4>The DevOps Agent in Detail</h4>
          <a href="https://github.com/geobarteam/github-copilot-configs/blob/master/BLOGPOST.md#the-devops-agent-in-detail"></a></div>
          <p>The&nbsp;<code>@devops</code>&nbsp;agent deserves special attention because it encodes a pattern I found particularly valuable: the&nbsp;<strong>Architecture.md &rarr; Bicep &rarr; Deployment-Info pipeline</strong>.</p>
          <p>The workflow separates&nbsp;<em>what should exist</em>&nbsp;(Architecture.md) from&nbsp;<em>what does exist</em>&nbsp;(Deployment-Info-{env}.md):</p>
          <ol>
          <li><strong>Architecture.md</strong>&nbsp;is the specification &mdash; the desired infrastructure state, including resource topology, SKUs, cost estimates, DNS records, and URL maps</li>
          <li><strong>Bicep templates</strong>&nbsp;implement that specification</li>
          <li><strong>Deployment-Info-stg.md / Deployment-Info-prd.md</strong>&nbsp;record the actual deployed state &mdash; real resource names, FQDNs, and live URLs</li>
          </ol>
          <p>This separation means the devops agent always knows the current state of each environment and can reason about what needs to change. It also means the&nbsp;<code>@smoke-test</code>&nbsp;and&nbsp;<code>@debug</code>&nbsp;agents can discover live URLs automatically &mdash; no hardcoded values.</p>
          <div>
          <h4>The Smoke Test Agent in Detail</h4>
          <a href="https://github.com/geobarteam/github-copilot-configs/blob/master/BLOGPOST.md#the-smoke-test-agent-in-detail"></a></div>
          <p>The&nbsp;<code>@smoke-test</code>&nbsp;agent is designed for post-deployment verification. Instead of relying on hardcoded URLs, it dynamically builds its environment URL table by reading the infrastructure documentation files. It then executes a structured test plan:</p>
          <ul>
          <li><strong>Health endpoints</strong>: Checks&nbsp;<code>/health</code>,&nbsp;<code>/health/ready</code>,&nbsp;<code>/health/live</code>&nbsp;on every API</li>
          <li><strong>Page loads</strong>: Navigates to each web application, verifies content renders</li>
          <li><strong>Console errors</strong>: Captures JavaScript errors and failed resource loads</li>
          <li><strong>Network errors</strong>: Monitors for 4xx/5xx HTTP responses</li>
          <li><strong>Authentication flows</strong>: Verifies login redirects appear on protected routes (without entering real credentials)</li>
          </ul>
          <p>The agent produces a structured report with pass/fail counts, collected errors, and details on every failure. It's read-only by design &mdash; it never submits forms or modifies data, especially in production.</p>
          <div>
          <h3>Skills &mdash; Step-by-Step Recipes</h3>
          <a href="https://github.com/geobarteam/github-copilot-configs/blob/master/BLOGPOST.md#skills--step-by-step-recipes"></a></div>
          <p>Skills are invoked via&nbsp;<code>/name</code>&nbsp;in chat and guide the AI through multi-step code generation tasks.</p>
          <table>
          <thead>
          <tr>
          <th>Skill</th>
          <th>Purpose</th>
          </tr>
          </thead>
          <tbody>
          <tr>
          <td><strong><code>/init</code></strong></td>
          <td><strong>Start here</strong>&nbsp;&mdash; discovers project tokens (<code>{{SolutionName}}</code>,&nbsp;<code>{{TestExePath}}</code>, etc.) and replaces placeholders across all config files</td>
          </tr>
          <tr>
          <td><strong><code>/build-feature</code></strong></td>
          <td>Implements an approved plan step by step using Red-Green-Refactor</td>
          </tr>
          <tr>
          <td><strong><code>/add-endpoint</code></strong></td>
          <td>Adds a vertical-slice API endpoint (domain &rarr; application &rarr; controller &rarr; tests)</td>
          </tr>
          <tr>
          <td><strong><code>/add-blazor-page</code></strong></td>
          <td>Adds a Blazor page with ViewModel, Refit client, and MudBlazor components</td>
          </tr>
          <tr>
          <td><strong><code>/add-blazor-module</code></strong></td>
          <td>Scaffolds a standalone WASM module with MVVM + HttpClient</td>
          </tr>
          <tr>
          <td><strong><code>/add-dbup</code></strong></td>
          <td>Creates a DbUp migration script (CREATE TABLE, ALTER, seed data)</td>
          </tr>
          <tr>
          <td><strong><code>/e2e-test</code></strong></td>
          <td>Full-stack browser testing with Playwright MCP</td>
          </tr>
          <tr>
          <td><strong><code>/csharp-coding-standards</code></strong></td>
          <td>Reference-only C# coding standards and patterns</td>
          </tr>
          <tr>
          <td>Specs and plans are created as project-level documentation at the repo root:</td>
          <td>&nbsp;</td>
          </tr>
          </tbody>
          </table>
          <ul>
          <li><strong><code>_specs/&lt;Feature&gt;.md</code></strong>&nbsp;&mdash; Feature specifications (the contract between developer and AI). Written before planning using&nbsp;<code>templates/spec-template.md</code>.</li>
          <li><strong><code>_plans/&lt;Feature&gt;.md</code></strong>&nbsp;&mdash; Implementation plans with vertical-slice steps, created by&nbsp;<code>@planner</code>&nbsp;from the spec.</li>
          </ul>
          <div>
          <h3>Instructions &mdash; Auto-Activated Layer Conventions</h3>
          <a href="https://github.com/geobarteam/github-copilot-configs/blob/master/BLOGPOST.md#instructions--auto-activated-layer-conventions"></a></div>
          <p>Instruction files activate automatically when you edit files matching their&nbsp;<code>applyTo</code>&nbsp;glob pattern. When you open a domain entity file, the AI silently loads domain conventions. When you edit a controller, it loads BFF patterns.</p>
          <table>
          <thead>
          <tr>
          <th>Instruction</th>
          <th>Activates for</th>
          </tr>
          </thead>
          <tbody>
          <tr>
          <td><code>domain-entity</code></td>
          <td><code>src/Core/Domain/**</code></td>
          </tr>
          <tr>
          <td><code>application-layer</code></td>
          <td><code>src/Core/Application/**</code></td>
          </tr>
          <tr>
          <td><code>persistence-layer</code></td>
          <td><code>src/Core/Persistence/**</code></td>
          </tr>
          <tr>
          <td><code>bff-controller</code></td>
          <td><code>src/Host/Client/**</code></td>
          </tr>
          <tr>
          <td><code>blazor-presentation</code></td>
          <td><code>src/Presentation/**</code></td>
          </tr>
          <tr>
          <td><code>refit-client</code></td>
          <td><code>src/Presentation/**/ServiceClients/**</code></td>
          </tr>
          <tr>
          <td><code>tests</code></td>
          <td><code>src/Test/**</code></td>
          </tr>
          </tbody>
          </table>
          <p>This means the AI always knows the conventions for the layer you're working in &mdash; without you needing to mention them.</p>
          <div>
          <h3>Root Files &mdash; The Process Backbone</h3>
          <a href="https://github.com/geobarteam/github-copilot-configs/blob/master/BLOGPOST.md#root-files--the-process-backbone"></a></div>
          <table>
          <thead>
          <tr>
          <th>File</th>
          <th>Purpose</th>
          </tr>
          </thead>
          <tbody>
          <tr>
          <td><strong><code>AGENTS.md</code></strong></td>
          <td>Shared workflow rules &mdash; planning gates, Red-Green-Refactor-Proof loop, human gates, vertical slice decomposition rules</td>
          </tr>
          <tr>
          <td><strong><code>copilot-instructions.md</code></strong></td>
          <td>Project-level Copilot instructions &mdash; critical rules, project structure, dependency matrix, naming conventions, verification commands</td>
          </tr>
          <tr>
          <td><strong><code>CLAUDE.md</code></strong></td>
          <td>Claude Code entrypoint &mdash; references AGENTS.md, adds Claude-specific rules</td>
          </tr>
          </tbody>
          </table>
          <div>
          <h3>Token System &mdash; Project-Specific Customization</h3>
          <a href="https://github.com/geobarteam/github-copilot-configs/blob/master/BLOGPOST.md#token-system--project-specific-customization"></a></div>
          <p>Templates use&nbsp;<code>{{MustacheStyle}}</code>&nbsp;placeholders that the&nbsp;<code>/init</code>&nbsp;skill replaces with project-specific values:</p>
          <table>
          <thead>
          <tr>
          <th>Token</th>
          <th>Example</th>
          </tr>
          </thead>
          <tbody>
          <tr>
          <td><code>{{SolutionName}}</code></td>
          <td><code>FindMyDoctor</code></td>
          </tr>
          <tr>
          <td><code>{{NamespaceRoot}}</code></td>
          <td><code>Contoso.FindMyDoctor</code></td>
          </tr>
          <tr>
          <td><code>{{DbContextName}}</code></td>
          <td><code>FindMyDoctorDbContext</code></td>
          </tr>
          <tr>
          <td><code>{{TestExePath}}</code></td>
          <td><code>.\src\Test\Unit\bin\Debug\net10.0\Contoso.FindMyDoctor.Unit.Tests.exe</code></td>
          </tr>
          </tbody>
          </table>
          <p>Copy the files, run&nbsp;<code>/init</code>, and the entire AI configuration is project-specific in seconds.</p>
          <hr>
          <div>
          <h2>Part 3: Getting Started &mdash; Setting Up Your Own Project</h2>
          <a href="https://github.com/geobarteam/github-copilot-configs/blob/master/BLOGPOST.md#part-3-getting-started--setting-up-your-own-project"></a></div>
          <p>You don't need to use the exact same stack. The engineering process &mdash; plan first, test first, one step at a time &mdash; works for any project. Here's how to get started:</p>
          <div>
          <h3>Step 1: Copy the Configuration Files</h3>
          <a href="https://github.com/geobarteam/github-copilot-configs/blob/master/BLOGPOST.md#step-1-copy-the-configuration-files"></a></div>
          <p>Clone or download&nbsp;<a href="https://github.com/geobarteam/github-copilot-configs">github-copilot-configs</a>&nbsp;and copy the relevant files into your project:</p>
          <pre><code class="language-plaintext">your-project/
├── .github/
│   ├── copilot-instructions.md   ← from this repo's root copilot-instructions.md
│   ├── instructions/             ← auto-activated by applyTo glob patterns
│   ├── agents/                   ← invoked via @name in chat
│   ├── skills/                   ← invoked via /name in chat
│   └── prompts/                  ← available in prompt picker
├── AGENTS.md                     ← shared agent workflow rules (always-on)
├── CLAUDE.md                     ← optional, for Claude Code users
└── templates/                    ← referenced by agents and skills</code></pre>
          <blockquote>
          <p>The&nbsp;<code>.github/copilot-instructions.md</code>&nbsp;in this repo is meta (for the template library itself). Copy the&nbsp;<strong>root-level</strong>&nbsp;<code>copilot-instructions.md</code>&nbsp;to&nbsp;<code>.github/copilot-instructions.md</code>&nbsp;in your project.</p>
          </blockquote>
          <div>
          <h3>Step 2: Run the Init Skill</h3>
          <a href="https://github.com/geobarteam/github-copilot-configs/blob/master/BLOGPOST.md#step-2-run-the-init-skill"></a></div>
          <p>Open VS Code chat and type&nbsp;<code>/init</code>. The skill will:</p>
          <ol>
          <li><strong>Discover</strong>&nbsp;your project's tokens automatically (solution name, namespace root, test executable path, etc.)</li>
          <li><strong>Ask</strong>&nbsp;for values that can't be auto-discovered</li>
          <li><strong>Replace</strong>&nbsp;all&nbsp;<code>{{token}}</code>&nbsp;placeholders across your config files</li>
          <li><strong>Verify</strong>&nbsp;no unreplaced tokens remain</li>
          </ol>
          <div>
          <h3>Step 3: Start Building Features</h3>
          <a href="https://github.com/geobarteam/github-copilot-configs/blob/master/BLOGPOST.md#step-3-start-building-features"></a></div>
          <pre><code class="language-plaintext"># 1. Plan the feature
@planner Add subscription management with list and create

# 2. Review and approve the plan

# 3. Implement step by step
/build-feature Step 1 — Display subscription list</code></pre>
          <div>
          <h3>Step 4: Adapt to Your Architecture</h3>
          <a href="https://github.com/geobarteam/github-copilot-configs/blob/master/BLOGPOST.md#step-4-adapt-to-your-architecture"></a></div>
          <p>These files are templates. After running&nbsp;<code>/init</code>, adapt them to your project:</p>
          <ul>
          <li><strong>Different architecture?</strong>&nbsp;Edit the project structure in&nbsp;<code>.github/copilot-instructions.md</code>&nbsp;and update&nbsp;<code>applyTo</code>&nbsp;globs in instruction files</li>
          <li><strong>Different test runner?</strong>&nbsp;Update&nbsp;<code>{{TestExePath}}</code>&nbsp;references</li>
          <li><strong>Additional layers?</strong>&nbsp;Create new&nbsp;<code>&lt;topic&gt;.instructions.md</code>&nbsp;files with an&nbsp;<code>applyTo</code>&nbsp;glob in&nbsp;<code>.github/instructions/</code></li>
          <li><strong>Additional agents?</strong>&nbsp;Create&nbsp;<code>&lt;name&gt;.agent.md</code>&nbsp;in&nbsp;<code>.github/agents/</code>&nbsp;following the existing pattern</li>
          <li><strong>Different CI/CD?</strong>&nbsp;Update the&nbsp;<code>@devops</code>&nbsp;agent with your pipeline conventions</li>
          </ul>
          <div>
          <h3>Step 5: Build Your Own Agent Library</h3>
          <a href="https://github.com/geobarteam/github-copilot-configs/blob/master/BLOGPOST.md#step-5-build-your-own-agent-library"></a></div>
          <p>The most valuable thing in this repo isn't the boilerplate &mdash; it's the&nbsp;<strong>workflow encoded in agents</strong>. Consider building agents for your own recurring tasks:</p>
          <ul>
          <li>A&nbsp;<strong>code review agent</strong>&nbsp;that checks PRs against your team's conventions</li>
          <li>A&nbsp;<strong>migration agent</strong>&nbsp;that handles framework or library upgrades</li>
          <li>A&nbsp;<strong>documentation agent</strong>&nbsp;that keeps API docs in sync with implementation</li>
          <li>A&nbsp;<strong>performance agent</strong>&nbsp;that profiles and suggests optimizations</li>
          </ul>
          <p>The pattern is always the same: define the agent's role, constraints, workflow steps, and tools &mdash; then let it execute within guardrails you control.</p>
          <hr>
          <div>
          <h2>Part 4: Best Practices for AI Development Process</h2>
          <a href="https://github.com/geobarteam/github-copilot-configs/blob/master/BLOGPOST.md#part-4-best-practices-for-ai-development-process"></a></div>
          <p>Parts 1&ndash;3 covered principles and tooling. This part describes the&nbsp;<strong>concrete development process</strong>&nbsp;we encode into our templates &mdash; the workflow an AI agent follows when building features. This is where spec-driven development, Red-Green-Refactor, vertical slicing, and human gates come together into a single disciplined loop.</p>
          <div>
          <h3>The Five-Phase Workflow</h3>
          <a href="https://github.com/geobarteam/github-copilot-configs/blob/master/BLOGPOST.md#the-five-phase-workflow"></a></div>
          <p>Every feature follows five phases. The AI agent is responsible for executing them, but the developer stays in control at every gate.</p>
          <pre><code class="language-plaintext">Explore → Specify → Plan → Implement → Commit</code></pre>
          <p><strong>Explore</strong>&nbsp;&mdash; The agent reads the codebase to understand existing patterns, the reference feature, and the problem space. No code changes happen here.</p>
          <p><strong>Specify</strong>&nbsp;&mdash; A specification file (<code>_specs/&lt;FeatureName&gt;.md</code>) captures user stories, acceptance criteria, data model, and business rules. This is the contract between the developer and the AI.</p>
          <p><strong>Plan</strong>&nbsp;&mdash; A plan file (<code>_plans/&lt;FeatureName&gt;.md</code>) decomposes the feature into concrete implementation steps. Each step is a vertical behaviour slice with explicit file paths, test methods, and verification criteria. The plan must be approved before any code is written.</p>
          <p><strong>Implement</strong>&nbsp;&mdash; The agent executes the plan one step at a time using Red-Green-Refactor. Each step ends at a human gate &mdash; the developer reviews and approves before the next step starts.</p>
          <p><strong>Commit</strong>&nbsp;&mdash; Small, validated changes are checkpointed in Git after each approved step.</p>
          <div>
          <h3>The Planning Gate: When Plans Are Required</h3>
          <a href="https://github.com/geobarteam/github-copilot-configs/blob/master/BLOGPOST.md#the-planning-gate-when-plans-are-required"></a></div>
          <p>Not every change needs a plan. The configuration library encodes a simple decision matrix:</p>
          <table>
          <thead>
          <tr>
          <th>Situation</th>
          <th>Plan required?</th>
          </tr>
          </thead>
          <tbody>
          <tr>
          <td>New feature or vertical slice</td>
          <td><strong>Yes</strong></td>
          </tr>
          <tr>
          <td>Change touching 3+ files</td>
          <td><strong>Yes</strong></td>
          </tr>
          <tr>
          <td>Risk area change (auth, PII, DB schema, shared contracts)</td>
          <td><strong>Yes</strong></td>
          </tr>
          <tr>
          <td>1&ndash;2 file bugfix</td>
          <td>No &mdash; but still test-first</td>
          </tr>
          <tr>
          <td>Config correction, simple refactor</td>
          <td>No</td>
          </tr>
          </tbody>
          </table>
          <p>The planning gate prevents the most common AI failure mode: the agent starts writing code before understanding the scope, creates files in the wrong locations, misses existing patterns, and produces work that has to be thrown away.</p>
          <div>
          <h3>Vertical Slice Decomposition</h3>
          <a href="https://github.com/geobarteam/github-copilot-configs/blob/master/BLOGPOST.md#vertical-slice-decomposition"></a></div>
          <p>This is the most important planning principle and the one most teams get wrong when working with AI.</p>
          <p><strong>The problem with horizontal slicing:</strong>&nbsp;A natural instinct &mdash; for both humans and AI &mdash; is to plan layer by layer: "Step 1: Create the entity. Step 2: Add the repository. Step 3: Build the controller. Step 4: Create the page." This feels orderly, but it has a critical flaw:&nbsp;<strong>you don't discover integration problems until the very end</strong>. The entity might not match the DTO. The query might return the wrong shape. The page might need data the controller doesn't provide. By the time you find out, you've built four layers of code that need rework.</p>
          <p><strong>The vertical slice alternative:</strong>&nbsp;Decompose features into the smallest possible&nbsp;<strong>user-visible behaviours</strong>, not into layers. Each slice delivers something a user or a test can verify end-to-end.</p>
          <p>The strategy is&nbsp;<strong>UI first with mocks, then replace mocks top-to-bottom</strong>:</p>
          <ol>
          <li>
          <p><strong>Stub phase</strong>&nbsp;&mdash; Build the page, form, or component at the Presentation layer with a stubbed service returning fake data. The user validates the UI and interaction design immediately. Tests verify the ViewModel behaviour against the mock.</p>
          </li>
          <li>
          <p><strong>Wire phase</strong>&nbsp;&mdash; Replace the stub with real production code, working from top to bottom: controller &rarr; application handler &rarr; domain &rarr; persistence &rarr; database. Integration tests verify the full stack. Remove the stub.</p>
          </li>
          </ol>
          <p>This two-phase approach has three benefits:</p>
          <ul>
          <li><strong>Fail fast</strong>&nbsp;&mdash; Integration mismatches between layers surface on the second step, not after building the entire feature in isolation.</li>
          <li><strong>UI feedback early</strong>&nbsp;&mdash; The user sees and validates the interface before any backend work begins, catching UX issues when they're cheap to fix.</li>
          <li><strong>Smaller blast radius</strong>&nbsp;&mdash; If the backend step reveals that the contract needs to change, only one stub step and one wire step are affected &mdash; not four independent layers.</li>
          </ul>
          <div>
          <h3>Example: Two Slices for a "Subscriptions" Feature</h3>
          <a href="https://github.com/geobarteam/github-copilot-configs/blob/master/BLOGPOST.md#example-two-slices-for-a-subscriptions-feature"></a></div>
          <p><strong>Slice A &mdash; View subscriptions:</strong></p>
          <table>
          <thead>
          <tr>
          <th>Step</th>
          <th>What it delivers</th>
          <th>Layers touched</th>
          </tr>
          </thead>
          <tbody>
          <tr>
          <td>1. Display subscription list (stubbed)</td>
          <td>Page renders a list with fake data; user validates layout and columns</td>
          <td>Presentation only</td>
          </tr>
          <tr>
          <td>2. Wire subscription list to real API</td>
          <td>GET endpoint returns real data; integration test passes</td>
          <td>Controller, Application, Persistence, DB, Refit client</td>
          </tr>
          </tbody>
          </table>
          <p><strong>Slice B &mdash; Create a subscription:</strong></p>
          <table>
          <thead>
          <tr>
          <th>Step</th>
          <th>What it delivers</th>
          <th>Layers touched</th>
          </tr>
          </thead>
          <tbody>
          <tr>
          <td>3. Create subscription form (stubbed)</td>
          <td>Form renders with validation; user validates error handling</td>
          <td>Presentation only</td>
          </tr>
          <tr>
          <td>4. Wire subscription create to real API</td>
          <td>POST endpoint creates record; validation errors returned; integration test passes</td>
          <td>Controller, Application, Domain, Persistence, DB, Refit client</td>
          </tr>
          </tbody>
          </table>
          <p>Notice: each slice is completed (UI + backend) before the next slice starts. Step 1 and Step 2 are adjacent &mdash; they belong to the same user behaviour. The agent never jumps to Slice B before Slice A is fully wired.</p>
          <p><strong>Compare with the anti-pattern:</strong>&nbsp;Step 1 "Create Entity + Repository", Step 2 "Build Application handlers", Step 3 "GET endpoint", Step 4 "POST endpoint", Step 5 "List page", Step 6 "Create page". This builds layers in isolation. The UI is validated last, when it should be validated first.</p>
          <div>
          <h3>Red-Green-Refactor-Proof: The Implementation Loop</h3>
          <a href="https://github.com/geobarteam/github-copilot-configs/blob/master/BLOGPOST.md#red-green-refactor-proof-the-implementation-loop"></a></div>
          <p>Each plan step is executed as a single Red-Green-Refactor cycle. This is the loop the AI agent follows for every step:</p>
          <pre><code class="language-plaintext">READ       — Read the plan step. Understand scope, files, and the test to write.
RED        — Write the failing test FIRST. Run it. Confirm it fails.
GREEN      — Write the minimal production code to make the test pass. Run it. Confirm it passes.
REFACTOR   — Clean up if needed. Do not change behaviour.
ANALYSE    — Run static analysis. Fix all violations. Run the full test suite.
PROVE      — Build (zero warnings) + all tests pass + format check passes.
🛑 STOP    — Present results. Wait for human approval.
MARK DONE  — After approval, update the plan file checkboxes.</code></pre>
          <p>Three rules are non-negotiable:</p>
          <ol>
          <li>
          <p><strong>Never skip RED.</strong>&nbsp;The test must exist and fail before any production code is written. This proves the test actually validates something. An AI that writes the test and production code simultaneously has no guarantee the test would have caught a real failure.</p>
          </li>
          <li>
          <p><strong>Never batch steps.</strong>&nbsp;One step per interaction. The agent completes a step, proves it, presents results, and stops. The developer reviews before the next step begins. This prevents the common AI failure of compounding errors across multiple steps.</p>
          </li>
          <li>
          <p><strong>Never proceed past the human gate.</strong>&nbsp;The 🛑 STOP is absolute. The agent does not continue until the developer explicitly approves. This is the single most important control mechanism &mdash; it keeps the developer accountable for every change.</p>
          </li>
          </ol>
          <div>
          <h3>Human Gates: The Developer Stays in Control</h3>
          <a href="https://github.com/geobarteam/github-copilot-configs/blob/master/BLOGPOST.md#human-gates-the-developer-stays-in-control"></a></div>
          <p>The human gate is not a rubber stamp. Each gate requires&nbsp;<strong>behavioural verification</strong>&nbsp;&mdash; a concrete assertion that the system now does something it didn't before:</p>
          <ul>
          <li>"Integration test&nbsp;<code>GetSubscriptionsTest</code>&nbsp;passes &mdash; GET&nbsp;<code>/api/subscriptions</code>&nbsp;returns a list."</li>
          <li>"Form renders with validation &mdash; entering an empty name shows 'Name is required'."</li>
          <li>"POST&nbsp;<code>/api/subscriptions</code>&nbsp;with valid data returns 200; with invalid data returns 400 with error message."</li>
          </ul>
          <p>"Code review" alone is never sufficient as the sole verification. If a step cannot be verified through observable behaviour, it should be merged into a step that can.</p>
          <p>Gates also trigger at higher-risk moments:</p>
          <ul>
          <li>After plan creation (before any code is written)</li>
          <li>Before any Git push</li>
          <li>On any change to authentication, PII handling, shared contracts, or database schema</li>
          </ul>
          <div>
          <h3>Spec-Driven Development: The Spec as Contract</h3>
          <a href="https://github.com/geobarteam/github-copilot-configs/blob/master/BLOGPOST.md#spec-driven-development-the-spec-as-contract"></a></div>
          <p>The specification file (<code>_specs/&lt;FeatureName&gt;.md</code>) is written before the plan and serves as the contract between developer intent and AI execution. A good spec contains:</p>
          <ul>
          <li><strong>User stories</strong>&nbsp;&mdash; Who benefits and what they can do</li>
          <li><strong>Acceptance criteria</strong>&nbsp;&mdash; Concrete, testable conditions for "done"</li>
          <li><strong>Data model</strong>&nbsp;&mdash; Entities, properties, relationships</li>
          <li><strong>Business rules</strong>&nbsp;&mdash; Validation, constraints, edge cases</li>
          <li><strong>Non-goals</strong>&nbsp;&mdash; What is explicitly out of scope</li>
          </ul>
          <p>The spec prevents a common AI failure: scope creep. Without a spec, the AI infers what "Add subscriptions" means and may add features nobody asked for, use patterns that don't match the codebase, or miss critical business rules. The spec makes intent unambiguous.</p>
          <p>The plan is then derived from the spec. Every plan step traces back to an acceptance criterion. If a step doesn't serve an acceptance criterion, it shouldn't exist.</p>
          <div>
          <h3>Progress Tracking Across Sessions</h3>
          <a href="https://github.com/geobarteam/github-copilot-configs/blob/master/BLOGPOST.md#progress-tracking-across-sessions"></a></div>
          <p>AI coding sessions are ephemeral &mdash; context is lost when a session ends. Our plan files solve this with a simple convention:&nbsp;<strong>checkbox tracking</strong>.</p>
          <pre><code class="language-plaintext">## Step 1 — Display subscription list (stubbed)
...
**🛑 HUMAN GATE**:
- [x] Behavioral verification: ViewModel test passes, page renders list with fake data
- [x] Code review: Page layout matches reference feature

## Step 2 — Wire subscription list to real API
...
**🛑 HUMAN GATE**:
- [ ] Behavioral verification: Integration test passes, GET /api/subscriptions returns list
- [ ] Code review: Repository, query, controller follow reference patterns</code></pre>
          <p>When a new session starts, the agent reads the plan file and finds the first unchecked&nbsp;<code>[ ]</code>&nbsp;&mdash; that's where to resume. No context is lost. Any agent, in any session, can pick up exactly where the last one left off.</p>
          <div>
          <h3>Encoding All of This as AI Instructions</h3>
          <a href="https://github.com/geobarteam/github-copilot-configs/blob/master/BLOGPOST.md#encoding-all-of-this-as-ai-instructions"></a></div>
          <p>The key insight is that this entire process &mdash; planning gates, vertical slicing, Red-Green-Refactor, human gates, progress tracking &mdash; is&nbsp;<strong>encoded as AI instruction files</strong>&nbsp;that the agent reads and follows automatically. It's not a convention that developers need to remember; it's a constraint the AI enforces on itself.</p>
          <table>
          <thead>
          <tr>
          <th>File</th>
          <th>What it encodes</th>
          </tr>
          </thead>
          <tbody>
          <tr>
          <td><code>AGENTS.md</code></td>
          <td>Planning gate, mandatory workflow rules, RGR-Proof loop, human gates, vertical slice rules</td>
          </tr>
          <tr>
          <td><code>planner.agent.md</code></td>
          <td>How to decompose features into vertical slices, interview checklist, self-check validation</td>
          </tr>
          <tr>
          <td><code>devops.agent.md</code></td>
          <td>CI/CD pipeline structure, Bicep conventions, Architecture.md &rarr; Deployment-Info pipeline</td>
          </tr>
          <tr>
          <td><code>smoke-test.agent.md</code></td>
          <td>Post-deployment verification, URL discovery, browser-based health checks</td>
          </tr>
          <tr>
          <td><code>debug.agent.md</code></td>
          <td>App Insights telemetry analysis, Playwright reproduction, cross-layer correlation</td>
          </tr>
          <tr>
          <td><code>bugfix.agent.md</code></td>
          <td>Regression-test-first bug fixing, 2-file scope limit, escalation rules</td>
          </tr>
          <tr>
          <td><code>build-feature/SKILL.md</code></td>
          <td>How to execute plan steps with layer-specific code templates</td>
          </tr>
          <tr>
          <td><code>copilot-instructions.md</code></td>
          <td>Critical rules, project structure, dependency matrix, naming conventions</td>
          </tr>
          </tbody>
          </table>
          <p>When a developer says "build the Subscriptions feature", the AI reads these files, asks clarifying questions, produces a vertically-sliced plan, waits for approval, and executes one step at a time with test-first discipline and mandatory human gates. The developer's job shifts from writing boilerplate to&nbsp;<strong>reviewing vertical slices of working behaviour</strong>.</p>
          <hr>
          <div>
          <h2>Conclusion</h2>
          <a href="https://github.com/geobarteam/github-copilot-configs/blob/master/BLOGPOST.md#conclusion"></a></div>
          <p>AI coding assistants are powerful, but they amplify both good and bad practices. The developers who benefit most will be those who:</p>
          <ol>
          <li><strong>Adopt deliberate workflows</strong>&nbsp;&mdash; Explore, Specify, Plan, Implement, Commit</li>
          <li><strong>Require verification</strong>&nbsp;&mdash; never trust AI output without automated proof</li>
          <li><strong>Keep sessions focused</strong>&nbsp;&mdash; one task, one goal, clear validation</li>
          <li><strong>Choose models wisely</strong>&nbsp;&mdash; match cost to complexity</li>
          <li><strong>Encode their process into AI configuration</strong>&nbsp;&mdash; turn personal engineering discipline into agents that enforce it automatically</li>
          </ol>
          <p>I built&nbsp;<a href="https://github.com/geobarteam/github-copilot-configs">github-copilot-configs</a>&nbsp;because I got tired of re-explaining my process to every AI session in every project. Now I copy the files, run&nbsp;<code>/init</code>, and every agent &mdash; planner, bugfix, debug, devops, smoke-test, git &mdash; knows the architecture, the conventions, and the workflow. The AI becomes a disciplined teammate instead of an unpredictable code generator.</p>
          <p>The library is open source and designed to be forked and adapted. Take what works for you, modify what doesn't, and build your own agent library on top of it. The investment in encoding your engineering process pays for itself on the first feature you build with it.</p>
          <hr>
          <p><em>The practices described in this article are encoded in the&nbsp;<a href="https://github.com/geobarteam/github-copilot-configs">github-copilot-configs</a>&nbsp;open-source repository &mdash; a reusable template library of GitHub Copilot and Claude Code configuration files for .NET projects.</em></p>]]></description>
    </item>
    <item>
      <title>My way to apply the test pyramid to an Onion Style architecture with Blazor</title>
      <link>https://vandiest.biz/post/2023/08/21/my-way-to-apply-the-test-pyramid-to-an-onion-style-architecture-with-blazor/</link>
      <guid isPermaLink="true">https://vandiest.biz/post/2023/08/21/my-way-to-apply-the-test-pyramid-to-an-onion-style-architecture-with-blazor/</guid>
      <pubDate>Mon, 21 Aug 2023 08:00:00 GMT</pubDate>
      <category>Testing</category>
      <category>Unit testing</category>
      <category>Architecture</category>
      <description><![CDATA[<p><img src="https://vandiest.biz/img/2023/my-way-to-apply-the-test-pyramid-to-an-onion-sty-01.jpg" alt width="1200" height="630" loading="lazy"></p>
          <p>It's crucial to understand that the testing pyramid must be tailored to a project's unique requirements. The team is responsible for fine-tuning the ideal testing architecture for their project.</p>
          <p>I'm offering an example of a basic test pyramid suited to the Onion architecture I commonly employ in my projects. My intent is to showcase what a testing pyramid might look like for DDD (Domain-Driven Design) projects. However, always consider that it might need further adjustments to fit specific project circumstances.</p>
          <p>At the core of my testing strategy lie unit tests. These focus on individual components or functions and are quick to write and maintain. They ensure the fundamental components of our application remain robust.</p>
          <p>It's worth noting that a pyramid doesn't stand solely on its base. While unit tests are foundational, they represent just one facet. Beyond unit tests, I implement Service tests to validate the entire backend's integration. These play a pivotal role in ensuring proper interactions with databases and external services. They're generally slower and more intricate than unit tests, which is why I house them in separate projects.</p>
          <p>The term "Service test" originates from the idea that these tests operate from the service layer.</p>
          <p>By segregating Service Tests from unit tests and placing them in distinct projects, I enhance the manageability and scalability of my testing approach. This separation allows for unit tests to validate individual components, while integration tests or Service tests verify interactions with databases and services.</p>
          <p><img src="https://vandiest.biz/img/2023/my-way-to-apply-the-test-pyramid-to-an-onion-sty-02.png" alt width="601" height="601" loading="lazy"></p>
          <p><strong>Unit Tests</strong> At the pyramid's base, you find the unit test. This positioning implies a higher volume of unit tests compared to other kinds. A unit test is an automated test that targets a small, isolated code segment, typically an individual method or class. The unit test's objective is to confirm the correctness of this specific code piece.</p>
          <p>The included diagram showcases typical software layers for an Onion/Clean architecture, with color-coding to highlight their testing suitability.</p>
          <ul>
          <li><strong>Green:</strong> Layers such as ViewModels, Application, and Domain are primary unit testing candidates. They offer high value when tested in isolation due to their quick execution and maintainability.</li>
          <li><strong>Yellow:</strong> The yellow layers are less efficient when tested standalone. Their unit tests often bring limited added value since they usually rely heavily on the layer below. Therefore, integration tests are generally used to verify the accurate functionality and interaction of these components.</li>
          <li><strong>Red:</strong> Testing red layers usually results in fragile or sluggish tests that demand considerable maintenance. These layers typically offer lower testing ROI, making integration tests a more pragmatic choice.</li>
          </ul>
          <p>Some attributes my unit tests adhere to include:</p>
          <ul>
          <li><strong>Testing Single Units:</strong> A unit test evaluates one class or method at a time. This limited scope makes it simpler to identify issues, aiding codebase maintenance and refactoring.</li>
          <li><strong>Testing Pure Logic:</strong> The primary purpose of a unit test is to confirm the class or method logic. This encompasses checking if the method returns expected results for given inputs or handles edge cases effectively.</li>
          <li><strong>Mocking Dependencies:</strong> To ensure code isolation, unit tests shouldn't depend on external systems. For instance, a class interacting with a database should be tested with a mock or stub, not a real database connection.</li>
          <li><strong>Fast Execution:</strong> Due to their foundational role, unit tests need swift execution to facilitate frequent runs, ideally after each code alteration.</li>
          </ul>
          <p><strong>UI Test</strong></p>
          <p>UI Tests validate user interactions and the application's interface. They replicate real user scenarios involving UI elements, ensuring the application operates as anticipated. My UI Test are done in isolation of the Service Tier, they exclusively test an application's presentation layer. As these test are performed in isolation you can see them as a specialized subset of unit tests. Using the MVVM pattern within my Blazor components enables to abstract away the UI logic and free the test from depending on UI specific details that make the test brittle.&nbsp; For testing the logic in the Razor views I typically use BUnit but try to avoid them as much as possible.</p>
          <p>However, while UI Tests are insightful, they can be fragile and require regular updates, especially if UI elements undergo changes. They might not detect all UI issues, emphasizing the need for a comprehensive testing strategy that incorporates End2End tests for thorough application quality verification.</p>
          <p><strong>Service Tests</strong></p>
          <p>Occupying the pyramid's middle layer, Service Tests anchor my integration testing strategy. They center on the application's back-end components, ensuring that services, APIs, and business logic layers cohesively function. They also serve as a type of acceptance testing for complex logic. Due to their ability to test business flows without UI test fragility, Service Tests often yield the highest ROI, making them indispensable in my testing strategy.</p>
          <p>For Service Tests, I utilize Specflow and follow the BDD (Behavior-Driven Development) approach. This method bridges the gap between non-technical and technical team members, fostering shared application behavior understanding.</p>
          <p><img src="https://vandiest.biz/img/2023/my-way-to-apply-the-test-pyramid-to-an-onion-sty-03.png" alt width="602" height="159" loading="lazy"></p>
          <p>Domain-Driven Design's concept of Ubiquitous Language pairs seamlessly with BDD. It denotes a shared language among team members, ensuring mutual understanding of domain concepts and requirements.</p>
          <p>SpecFlow, a renowned BDD tool, enables business analysts to describe expected application behaviors in Gherkin syntax. Developers then create step definitions, which are code pieces that map scenarios to test actions. By adopting a BDD approach with tools like SpecFlow, I ensure the Ubiquitous Language isn't just documentation but living specifications executable as tests. This promotes clear communication and a cohesive application.</p>

          <p><strong>Database: In-memory or Disk-based?</strong></p>
          <p>Efficiency and speed are paramount for all tests, especially when integrated into a continuous integration process on a build server. Database selection significantly influences this.</p>
          <p>From my experience, an in-memory database like SQLite is typically more build-time efficient. However, there are instances where SQLite might be unsuitable, especially when integrating with complex databases with features like stored procedures. In such cases, a closer representation of the production environment, like SQL Server, is essential. To optimize tests without compromising build times, I suggest making tests configurable to different databases. For instance, SQLite can be used on the build server for rapid tests, while SQL Server is employed locally.</p>
          <p>It's also crucial to emphasize that tests should mock external services, ensuring the application's logic is the primary testing focus.</p>
          <p><strong>Conclusion</strong> Structuring my test pyramid with unit tests at the base, Service Tests in the middle, and UI tests at the peak results in a comprehensive testing strategy. While UI tests play a role, Service Tests truly underpin a quality product. Combining these tests ensures both the individual components' integrity and the smooth interaction between the application's various tiers.</p>]]></description>
    </item>
    <item>
      <title>Going All-In on UI Testing Automation: a Recipe for Failure!</title>
      <link>https://vandiest.biz/post/2023/07/17/going-all-in-on-ui-testing-automation-a-recipe-for-failure/</link>
      <guid isPermaLink="true">https://vandiest.biz/post/2023/07/17/going-all-in-on-ui-testing-automation-a-recipe-for-failure/</guid>
      <pubDate>Mon, 17 Jul 2023 08:00:00 GMT</pubDate>
      <category>Testing</category>
      <description><![CDATA[<p><img src="https://vandiest.biz/img/2023/going-all-in-on-ui-testing-automation-a-recipe-f-01.jpg" alt width="850" height="400" loading="lazy"></p>
          <p>Many professionals in our industry believe that the key to an effective testing strategy is a robust system of automated tests that interact with the User Interface (UI). Imagine an advanced tool that can meticulously record user interactions and replay them to check for consistent results.</p>
          <p>However, when applied in practice, this approach reveals several problems. The most significant issue is the fragility of these tests. Even minor UI changes can cause numerous tests to fail, resulting in a time-consuming process of re-recording. Moreover, these tests tend to run at a slow pace and require substantial infrastructure to execute effectively. A testing strategy solely focused on automating UI tests is too expensive, delicate, and time intensive.</p>
          <p>My consideration of the effectiveness of automated testing was restored when I discovered the concept of the test pyramid. This concept, integrated within the Software Development Life Cycle (SDLC), organizes tests across different layers of the application stack. Visualize a pyramid with a sturdy and balanced structure. The broad base consists of quick and efficient unit tests, while the narrow peak represents a smaller set of slower, more complex end-to-end tests, often performed through the UI. This approach prioritizes comprehensive coverage at the foundational levels where tests are simpler, faster, and more cost-effective, while still conducting sufficient high-level tests to ensure the harmonious functioning of system components.</p>
          <p><img src="https://vandiest.biz/img/2023/going-all-in-on-ui-testing-automation-a-recipe-f-02.png" alt width="731" height="490" loading="lazy"></p>
          <p>However, the test pyramid does not stop at the base and the peak. It also incorporates an intermediate layer of tests that operate discreetly through an application's service layer. These tests combine the strengths of end-to-end tests while bypassing the complexities of UI frameworks. For applications based on a three-tier architectures, this corresponds to testing through the second tier, the <a href="https://medium.com/mobilepeople/backend-for-frontend-pattern-why-you-need-to-know-it-46f94ce420b0">Backend For Frontend.</a> For two-tier applications, this can be done through the <a href="https://marcoatschaefer.medium.com/onion-architecture-explained-building-maintainable-software-54996ff8e464#:~:text=The%20Application%20Layer%20is%20the,cases)%20instead%20of%20Business%20rules.">Application layer</a>.</p>
          <p>It is also important to note that there can be variations of the test pyramid based on different philosophies or specific needs of a project. It is up to the team to fine-tune the right testing strategy for his project. Here I provide a straightforward strategy that should fit most scenarios but not all. You should take account of your specific circumstances and adapt this strategy to your specific situation.</p>]]></description>
    </item>
    <item>
      <title>Best Practices for Effective Unit Testing in C#</title>
      <link>https://vandiest.biz/post/2023/07/07/best-practices-for-effective-unit-testing-in-c/</link>
      <guid isPermaLink="true">https://vandiest.biz/post/2023/07/07/best-practices-for-effective-unit-testing-in-c/</guid>
      <pubDate>Fri, 07 Jul 2023 08:00:00 GMT</pubDate>
      <category>Testing</category>
      <category>Unit testing</category>
      <category>.NET</category>
      <description><![CDATA[<p><img src="https://vandiest.biz/img/2023/best-practices-for-effective-unit-testing-in-c-01.jpg" alt width="300" height="168" loading="lazy"></p>
          <p>In <a href="https://vandiest.biz/post/2023/07/03/crafting-c-code-for-success-6-key-principles-to-elevate-testability-and-maintainability/">my previous blog</a> post, I explained the importance of writing testable code. In this post, I will explore some common best practices for unit testing in C#, focusing on key areas such as test structuring, mocking frameworks, test naming, and differentiating between unit and integration tests.</p>
          <h3>Follow the Arrange-Act-Assert (AAA) Pattern</h3>
          <p>The AAA pattern provides a structured approach for organizing unit tests. Follow these steps:</p>
          <ul>
          <li>Arrange: Set up the object to be tested and its dependencies.</li>
          <li>Act: Perform the action to be tested.</li>
          <li>Assert: Verify that the action led to the expected result.</li>
          </ul>
          <p>Using the AAA pattern enhances readability and maintainability of your tests.</p>

          <div>
          <pre><code class="language-csharp">using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace MyApp.Tests
{
    [TestClass]
    public class CalculatorTests
    {
        [TestMethod]
        public void Add_GivenTwoIntegers_ReturnsTheirSum()
        {
            // Arrange
            var calculator = new Calculator();
            int number1 = 2;
            int number2 = 3;

            // Act
            int result = calculator.Add(number1, number2);

            // Assert
            Assert.AreEqual(5, result, "The addition of 2 and 3 should equal 5");
        }
    }
}</code></pre>
          </div>
          <h3><strong>&nbsp;Use Mocking Frameworks Wisely</strong></h3>
          <p><strong><img src="https://vandiest.biz/img/2023/best-practices-for-effective-unit-testing-in-c-02.png" alt width="450" height="348" loading="lazy"></strong></p>
          <p>Mocking frameworks, like <a href="https://github.com/moq/moq">Moq</a>, allow the creation of mock implementations of dependencies. However, use them judiciously to avoid potential pitfalls:</p>
          <ul>
          <li>False Sense of Security: Over-mocking can lead to tests passing despite real-world failures.</li>
          <li>Brittle Tests: Unmet mock expectations due to code changes can cause unnecessary test failures.</li>
          <li>Overemphasis on Implementation Details: Too many mocks may shift focus from testing behavior to testing implementation.</li>
          <li>Increased Complexity: Tests with excessive mocks become harder to understand and maintain.</li>
          <li>Balance the use of mocking frameworks to isolate code units effectively without introducing unnecessary complexity.</li>
          </ul>
          <h3>Write Meaningful Test Names</h3>
          <p>A good test name should describe what the test does and provide a clear idea of what went wrong in case of failure. Follow a convention like "MethodName_StateUnderTest_ExpectedBehavior" to improve test name clarity and simplicity.</p>
          <h3>Separate Unit and Integration Tests</h3>
          <p>Distinguish between unit tests and integration tests to ensure effective testing and maintainable code.</p>
          <ul>
          <li>Unit Tests: Focus on testing individual components in isolation, with mocked or stubbed dependencies. They are fast, cheap, and provide valuable feedback on small code units.</li>
          <li>Integration Tests: Verify interactions and integration between components, including infrastructure. They have higher code coverage but can be more complex and fragile.</li>
          </ul>
          <p>Separating these test types in separate projects or folders or namespaces allows developers to leverage the strengths of each approach while maintaining code quality.</p>
          <h3>Utilize Parametrized or Data-Driven Tests</h3>
          <p>Parametrized tests allow running the same test method with different inputs and expected outcomes. In C#, you can use parameterized tests or data-driven tests with frameworks like MSTest. These tests provide flexibility and code reuse.</p>

          <div>
          <pre><code class="language-csharp">[TestClass]
public class CalculatorTests
{
private Calculator _calculator;

[TestInitialize]
public void Setup()
{
    _calculator = new Calculator();
}

[TestMethod]
public void Add_PositiveNumbers_CorrectSum()
{
    AddAndVerify(1, 1, 2);
    AddAndVerify(2, 2, 4);
    AddAndVerify(3, 3, 6);
}

[TestMethod]
public void Add_NegativeNumbers_CorrectSum()
{
    AddAndVerify(-1, -1, -2);
    AddAndVerify(-2, -2, -4);
    AddAndVerify(-3, -3, -6);
}

private void AddAndVerify(int a, int b, int expected)
{
    // Arrange - setup is done before this

    // Act
    int result = _calculator.Add(a, b);

    // Assert
    Assert.AreEqual(expected, result);
}</code></pre>
          </div>

          <p>By applying these best practices, you can write robust, reliable, and maintainable unit tests in C#. The Arrange-Act-Assert pattern brings clarity and structure to your tests. Using mocking frameworks wisely, writing meaningful test names, separating unit and integration tests, and leveraging parametrized or data-driven tests enhance the effectiveness and efficiency of your test suite. Unit testing is an essential tool for building high-quality software, and following these best practices will greatly contribute to your success.</p>]]></description>
    </item>
    <item>
      <title>6 Key Principles to Elevate Testability and Maintainability in C#</title>
      <link>https://vandiest.biz/post/2023/07/03/crafting-c-code-for-success-6-key-principles-to-elevate-testability-and-maintainability/</link>
      <guid isPermaLink="true">https://vandiest.biz/post/2023/07/03/crafting-c-code-for-success-6-key-principles-to-elevate-testability-and-maintainability/</guid>
      <pubDate>Mon, 03 Jul 2023 08:00:00 GMT</pubDate>
      <category>Testing</category>
      <category>Unit testing</category>
      <category>.NET</category>
      <description><![CDATA[<p><img src="https://vandiest.biz/img/2023/crafting-c-code-for-success-6-key-principles-to-01.jpg" alt width="550" height="367" loading="lazy"></p>
          <p>Testable code stands as a cornerstone in the realm of quality software development. Crafting code that can be effortlessly verified for correctness through automated testing is not just a skill, but an art. In this blog post, we will embark on a journey through six fundamental principles that serve as the pillars of writing testable and maintainable code in C#. Whether you are an experienced software engineer or taking your first steps in the C# landscape, these principles will equip you with the tools needed to sculpt your code into a masterpiece of quality and reliability.</p>
          <p><strong>1. Single Responsibility Principle (SRP):</strong> Each class or method should have a single responsibility, meaning it should only have one reason to change. If a class or method does too many things, it becomes harder to test because each test must consider the different paths through the code.</p>
          <p><strong>2. Use Dependency Injection</strong>: Dependency injection is a technique where an object's dependencies (the other objects it needs to do its job) are provided to it, rather than creating them itself. This allows the dependencies to be swapped out with fake or mock objects in tests, making the code easier to test. For example, you could inject a database service into your class, and replace it with a mock database service in your tests.</p>

          <div>
          <pre><code class="language-csharp">public class OrderService
{
    private readonly IOrderDatabase _database;

    public OrderService(IOrderDatabase database)
    {
        _database = database;
    }

    public void PlaceOrder(OrderedParallelQuery order)
    {
        // ... some logic ...
        _database.AddOrder(order);
    }

    // ... rest of class ...
}

[TestClass]
public class OrderServiceTests
{
    [TestMethod]
    public void PlaceOrder_OrderIsValid_AddsOrderToDatabase()
    {
        // Arrange
        var mockDatabase = new Mock&lt;IOrderDatabase&gt;();
        var orderService = new OrderService(mockDatabase.Object);
        var order = new Order { /* initialize order here */ };

        // Act
        orderService.PlaceOrder(order);

        // Assert
        mockDatabase.Verify(db =&gt; db.AddOrder(order), Times.Once, "Order should be added to database once");
    }
}</code></pre>
          </div>

          <p><strong>3. Use Interfaces and Abstractions:</strong> Relying on concrete classes makes your code more tightly coupled and harder to test. By depending on abstractions (like interfaces or abstract classes), you can easily substitute real implementations with mock ones for testing.</p>
          <p><strong>4. Avoid Static and Singleton Classes:</strong> Static methods and singleton classes cannot be substituted with mock implementations, making your code harder to test. They also hold state between tests, leading to tests that are not independent.<br> <br> Static and Singleton classes can hinder testability by preventing substitution with mock implementations, making testing more difficult and relying on real-world dependencies. Additionally, static methods and singleton classes can hold state between tests, leading to interdependent tests and potential inconsistencies.<br><br>While static and singleton classes should usually be avoided, there are cases where using static classes is appropriate. This includes scenarios such as defining utility functions or extension methods, which encapsulate reusable pure functions or extend the behavior of existing types without modifying their source code. In these cases, static classes can provide organization and improve code readability.</p>
          <p>For more explicit examples on when using static methods see the last part: 6.<em> Write Pure Functions When Possible</em></p>
          <p><strong>5. Favor Composition Over Inheritance:</strong> Composition refers to building complex objects by combining simpler ones, while inheritance describes a relationship between parent and child classes. Composition is more flexible and easier to test because you can replace parts of the composed object with mocks.<br> This example highlights the benefits of using composition for achieving better testable code:<br><br></p>

          <div>
          <pre><code class="language-csharp">// Composed object that uses composition instead of inheritance
public class ComposedObject : ISimpleObject
{
    private readonly ISimpleObject _simpleObject;

    public ComposedObject(ISimpleObject simpleObject)
    {
        _simpleObject = simpleObject;
    }

    public void PerformAction()
    {
        // Additional logic can be added here
        Console.WriteLine("Performing action in ComposedObject");

        // Delegating the action to the composed object
        _simpleObject.PerformAction();
    }
}

[TestClass]
public class ComposedObjectTests
{
    [TestMethod]
    public void PerformAction_Should_Call_PerformAction_Method_On_SimpleObject()
    {
        // Arrange
        var mockSimpleObject = new Mock&lt;ISimpleObject&gt;();
        var composedObject = new ComposedObject(mockSimpleObject.Object);

        // Act
        composedObject.PerformAction();

        // Assert
        mockSimpleObject.Verify(m =&gt; m.PerformAction(), Times.Once);
    }
}</code></pre>
          </div>

          <p>By favoring composition over inheritance, the code becomes more flexible and easier to test. The example demonstrates how the ComposedObject class is constructed by injecting dependencies through its constructor, allowing for easy substitution of real implementations with mock objects during testing. This decoupling of dependencies enables isolated testing, focusing solely on the behavior of the ComposedObject without being tightly coupled to the implementation details of its dependencies. The ability to inject and replace dependencies with mocks or stubs provides greater control over the testing environment and leads to more reliable and maintainable tests. Embracing composition enhances testability and promotes modular and testable code.</p>
          <p><strong>6. Write Pure Functions When Possible:</strong> A pure function's output is solely determined by its input, and it does not have any side effects (like changing global variables). This makes it easy to test, because you just need to check the output for a given input.<br><br></p>
          <p>Take this example where the method &ldquo;StoreAverageOfPrimes&rdquo; is extremely hard to test:</p>

          <div>
          <table>
          <tbody>
          <tr>
          <td>&nbsp;</td>
          <td>
          <pre><code class="language-sql">public void StoreAverageOfPrimes(int number)
{
    List&lt;int&gt; primeNumbers = new List&lt;int&gt;();

    using (SqlConnection connection = new SqlConnection(_connectionString))
    {
        connection.Open();

        for (int i = 2; i &lt;= number; i++)
        {
            bool isPrime = true;

            for (int j = 2; j &lt; i; j++)
            {
                if (i % j == 0)
                {
                    isPrime = false;
                    break;
                }
            }

            if (isPrime)
            {
                primeNumbers.Add(i);

                SqlCommand command = new SqlCommand("INSERT INTO PrimeNumbers (Value) VALUES (@Value)", connection);
                command.Parameters.AddWithValue("@Value", i);
                command.ExecuteNonQuery();
            }
        }

        SqlCommand command = new SqlCommand("INSERT INTO Results (Value) VALUES (@Value)", connection);
        command.Parameters.AddWithValue("@Value", primeNumbers.Average());
        command.ExecuteNonQuery();
    }
}</code></pre>
          </td>
          </tr>
          </tbody>
          </table>
          </div>

          <p>This code suffers from the same challenges when it comes to testing the calculation logic in isolation. The code tightly couples the calculation logic with the database access code, making it difficult to write focused unit tests. The lack of separation and abstraction hinders code reusability and maintainability.</p>
          <p>In the refactored code here under, the StoreAverageOfPrimes method has been modified to separate the calculation logic from the data access code:</p>

          <div>
          <table>
          <tbody>
          <tr>
          <td>&nbsp;</td>
          <td>
          <pre><code class="language-sql">public static List&lt;int&gt; GetPrimeNumbers(int number)
{
    List&lt;int&gt; primeNumbers = new List&lt;int&gt;();

    for (int i = 2; i &lt;= number; i++)
    {
        if (IsPrime(i))
        {
            primeNumbers.Add(i);
        }
    }

    return primeNumbers;
}

public static bool IsPrime(int number)
{
    if (number &lt; 2)
        return false;

    for (int i = 2; i &lt;= Math.Sqrt(number); i++)
    {
        if (number % i == 0)
            return false;
    }

    return true;
}

public void StoreAverageOfPrimes(int number)
{
    List&lt;int&gt; primeNumbers = GetPrimeNumbers(number);
    double average = primeNumbers.Average();

    using (SqlConnection connection = new SqlConnection(_connectionString))
    {
        connection.Open();

        StorePrimeNumbers(primeNumbers, connection);
        StoreResult(average, connection);
    }
}

private void StorePrimeNumbers(List&lt;int&gt; primeNumbers, SqlConnection connection)
{
    foreach (int number in primeNumbers)
    {
        SqlCommand command = new SqlCommand("INSERT INTO PrimeNumbers (Value) VALUES (@Value)", connection);
        command.Parameters.AddWithValue("@Value", number);
        command.ExecuteNonQuery();
    }
}

private void StoreResult(double result, SqlConnection connection)
{
    SqlCommand command = new SqlCommand("INSERT INTO Results (Value) VALUES (@Value)", connection);
    command.Parameters.AddWithValue("@Value", result);
    command.ExecuteNonQuery();
}</code></pre>
          </td>
          </tr>
          </tbody>
          </table>
          </div>

          <p>In this updated code, the pure functions GetPrimeNumbers and IsPrime have been made static. By making them static, they can be accessed directly without an instance of the CalculationService class.</p>
          <p>The static nature of these functions allows them to be more easily testable and promotes code reusability, as they can be used independently without requiring an instance of the class.</p>
          <p>When writing testable code, it is important to consider the nature of the functions you are working with. Pure functions, which have the characteristic of producing the same output for a given set of inputs and having no side effects, are particularly beneficial for testability.</p>
          <p>In the context of testability, pure functions can be made static to further enhance their usability. By making pure functions static, they can be accessed and used directly without the need to create an instance of the class containing them. This eliminates the dependency on the object's state, making the functions more independent and self-contained.</p>
          <p>The static nature of pure functions simplifies the testing process. Since they rely only on the provided input and produce a deterministic output, you can easily write focused unit tests for these functions without the need to set up complex object states or manage external dependencies. You can simply pass different inputs to the functions and verify their outputs against expected results.</p>
          <p>In contrast, functions with side effects, such as those interacting with databases or modifying global variables, are more difficult to test due to their reliance on external resources and potential interference with the test environment. These functions often require complex setup and teardown procedures to isolate and restore the system state, which can complicate the testing process.</p>
          <p>By separating pure functions and making them static, you create modular and testable units of code that are easier to verify in isolation. This promotes code maintainability, reusability, and testability, allowing you to write more effective and reliable tests for your applications.</p>]]></description>
    </item>
    <item>
      <title>Chasing the Mythical 80%: When Code Coverage Becomes a Treasure Hunt</title>
      <link>https://vandiest.biz/post/2023/06/28/chasing-the-mythical-80-when-code-coverage-becomes-a-treasure-hunt/</link>
      <guid isPermaLink="true">https://vandiest.biz/post/2023/06/28/chasing-the-mythical-80-when-code-coverage-becomes-a-treasure-hunt/</guid>
      <pubDate>Wed, 28 Jun 2023 08:00:00 GMT</pubDate>
      <category>Testing</category>
      <description><![CDATA[<p><img src="https://vandiest.biz/img/2023/chasing-the-mythical-80-when-code-coverage-becom-01.jpg" alt width="602" height="301" loading="lazy"></p>
          <p>Ah, the famous 80% code coverage - the golden number that many developers I've encountered seem to believe has magical powers to keep all the coding gremlins away. It means that 80% of the new code should be put to the test, as if saying, &ldquo;Prove your worth, code!&rdquo; This bar is high, no doubt, and is set to make sure that most of our codebase is given a good check.</p>
          <p>However, let&rsquo;s not get too starry-eyed about this 80%. Sometimes, depending on the type of project or part of the code, it&rsquo;s like trying to fit a square peg in a round hole. It just doesn&rsquo;t work! In such cases, it&rsquo;s smarter to focus on creating solid, quality tests rather than running a race to hit that 80%.</p>
          <p>Now, when deciding how much testing is just right, we need to think about complexity and risk. Imagine them as partners in crime &ndash; when complexity goes up, risk is right there with it. That means more chances of things going wrong. To set the right target for code coverage, you have to keep an eye on how complicated the code is. The SonarQube Coverage Overview report is like a helpful friend in this mission. It&rsquo;s easy on the eyes and shows the connection between code coverage and complexity.</p>
          <p><img src="https://vandiest.biz/img/2023/chasing-the-mythical-80-when-code-coverage-becom-02.png" alt width="404" height="287" loading="lazy"></p>
          <p>Here&rsquo;s the quick tour of what the report shows:</p>
          <ul>
          <li> Bottom-right corner: This is where complex but well-tested code hangs out. Think of it like a tough puzzle that&rsquo;s been solved.</li>
          <li> Bottom-left corner: The dream zone! Code here is simple and has been tested a lot. It&rsquo;s where you&rsquo;d want most of your code to be.</li>
          <li> Top-right corner: Red alert area! Code here is like a tangled mess and hasn&rsquo;t been tested enough. It&rsquo;s risky and needs attention ASAP.</li>
          <li> Top-left corner: This area has simple code, but it hasn&rsquo;t been tested much. It&rsquo;s not ideal, but not a total disaster.</li>
          </ul>

          <p>So, in summary, this report is like a handy map for your code. It helps you see what needs your attention and testing effort. This way, you can make sure your software is in good shape. But always remember, 80% is a guiding light, not a magic spell!</p>]]></description>
    </item>
    <item>
      <title>Orchestrating Success: The Harmonious Ensemble Behind Automated Testing!</title>
      <link>https://vandiest.biz/post/2023/06/21/orchestrating-success-the-harmonious-ensemble-behind-automated-testing/</link>
      <guid isPermaLink="true">https://vandiest.biz/post/2023/06/21/orchestrating-success-the-harmonious-ensemble-behind-automated-testing/</guid>
      <pubDate>Wed, 21 Jun 2023 08:00:00 GMT</pubDate>
      <category>Testing</category>
      <category>Agile</category>
      <description><![CDATA[<p><img src="https://vandiest.biz/img/2023/orchestrating-success-the-harmonious-ensemble-be-01.png" alt width="1191" height="641" loading="lazy"></p>
          <p>Hello everyone! Before we dive deep into the technical side of automated testing in my upcoming articles, I want to hit the pause button and shine a spotlight on something super important: writting automated test is a team sport!</p>
          <p>Yes, as developers, sometimes we feel like testing is our own little kingdom. But you know what? Building amazing software is like painting a beautiful picture - it needs different brushes and colors. That&rsquo;s why testing should be a high-five moment with the whole team involved. We developers must open the doors and roll out the red carpet for everyone to join in. So, let&rsquo;s bring everyone into the spotlight and see how this teamwork makes the dream work! Onward to the backstage tour!</p>
          <h2>Developers and Analysts as the Architects of Software</h2>
          <p>As developers, we are like the master builders of the software world. We draw up nifty blueprints &ndash; which are, of course, our much-loved automated tests &ndash; making sure the software is solid from the ground up. But here&rsquo;s the thing: we&rsquo;ve got teammates! Analysts are the awesome folks who help set the stage. They&rsquo;re like the planners who make sure everything&rsquo;s in the right place. They use their smarts and know-how to create a shared lingo &ndash; kind of like a secret code that everyone on the team can understand. This means everyone&rsquo;s singing from the same song sheet, which makes everything tick along nicely.</p>
          <h2>The Testers&rsquo; Detective Work in Polishing the Software Masterpiece</h2>
          <p>Next up, let&rsquo;s hear it for the testers &ndash; our software&rsquo;s very own detective squad! They swing into action when it's time to put the software through its paces, sniffing out any sneaky bugs or issues. They have a knack for spotting the little things that might trip us up, and they&rsquo;re great at making sure everything is shipshape. And it's even better when they join forces with developers and analysts to make sure our software is top-notch.</p>
          <h2>Scrum Masters and Project Managers Harmonizing the Ensemble</h2>
          <p>Hang on, we&rsquo;re not done with the shout-outs! We can&rsquo;t forget the Scrum Masters and Project Managers. These superstars are like the conductors of our orchestra. They keep the tunes flowing, helping everyone to play in sync. They&rsquo;re the ones who make sure the communication lines are open, that roadblocks are smashed, and that everyone's on the same page with testing goals.</p>
          <h2>The secret sauce</h2>
          <p>Now, for the secret sauce that glues everything together &ndash; SpecFlow! Especially for the .Net folks out there, SpecFlow is like a magic wand in the world of Behavior-Driven Development (BDD). It&rsquo;s an ace tool for cooking up automated and testable game plans. What&rsquo;s cool is that SpecFlow speaks a language called Gherkin, which is easy-peasy for everyone to understand, even if you&rsquo;re not a coding whiz. So everyone can jump in and help shape things up, making the team bond even stronger.</p>
          <p>So, my friends, before we get our geek on with all the techy details of automated testing, let&rsquo;s remember this: testing is all about teamwork. It's the special moment where everyone &ndash; analysts, testers, Scrum Masters, Project Managers &ndash; all huddle up for a group high-five, aiming to build the best software ever. Together, with smarts, teamwork, and nifty tools like SpecFlow, we&rsquo;re unstoppable. Trust me, this team adventure is the real deal, and you don&rsquo;t want to miss it!</p>]]></description>
    </item>
    <item>
      <title>Building Code Like a Skyscraper: The Scaffolding Magic of Automated Testing!</title>
      <link>https://vandiest.biz/post/2023/06/19/building-code-like-a-skyscraper-the-scaffolding-magic-of-automated-testing/</link>
      <guid isPermaLink="true">https://vandiest.biz/post/2023/06/19/building-code-like-a-skyscraper-the-scaffolding-magic-of-automated-testing/</guid>
      <pubDate>Mon, 19 Jun 2023 08:00:00 GMT</pubDate>
      <category>Testing</category>
      <description><![CDATA[<p><img src="https://vandiest.biz/img/2023/building-code-like-a-skyscraper-the-scaffolding-01.jpg" alt width="1200" height="700" loading="lazy"></p>
          <p>Today, I want to chat about how automated testing in software development is like scaffolding in building construction. Why? Because both of them give amazing support and strength to what we are building.</p>
          <ul>
          <li><strong>The Strong Support:</strong> Just like how a building needs scaffolding to keep it steady, our software needs automated testing for support. Scaffolding holds up the building while it&rsquo;s being built. In the same way, automated testing holds our code together, making sure it's strong and safe.</li>
          <li><strong>The Watchful Eyes</strong>: Scaffolding helps workers see and reach every part of the building. Similarly, automated testing keeps an eye on the software. It checks every nook and cranny to make sure that everything is running smoothly and correctly.</li>
          <li><strong>The Safety Net:</strong> When constructing a building, scaffolding acts as a safety net. If something goes wrong, it's there to catch it. Likewise, automated testing catches little problems in our code before they become big headaches.</li>
          <li><strong>The Flexible Friend:</strong> Scaffolding can be adjusted and moved as the building goes up. This is just like automated testing, which we can change and adapt as our software grows and evolves.</li>
          <li><strong>Building The Master Plan:</strong> In construction, scaffolding is part of the big plan to build something amazing. For us developers, automated testing is a crucial part of our master plan for our software. It guides us and helps us make changes safely.</li>
          <li><strong>Not A One-Man Show:</strong> But wait, scaffolding can&rsquo;t build a building on its own, and automated testing can&rsquo;t make perfect software by itself. That&rsquo;s why we have the construction workers, and in coding, we have manual testers. They are like the heroes who check everything up close and find anything that might have been missed.</li>
          <li><strong>Making It Future-Proof:</strong> Using scaffolding means that the building will be built right, so it will last a long time. In the same way, when we use automated testing, our code is solid. This means it will keep working great even as time goes by, and we can add new features without worries.</li>
          </ul>
          <p>Now, let&rsquo;s not end this chat without looking ahead. This post is like saying &ldquo;Hello&rdquo; before we start an awesome adventure. Over the next few weeks, I&rsquo;ll be sharing even more posts. We&rsquo;ll talk about the best ways to do things, tools that make life easier, and examples from real life that show how all of this stuff works.</p>
          <p>So, if you&rsquo;re excited to learn how to write even better code, make sure you keep coming back to check out what&rsquo;s new. Whether you&rsquo;re new to coding or have been doing it for years, there will be something for you. Can&rsquo;t wait to see you here! Stay tuned, friends!</p>]]></description>
    </item>
    <item>
      <title>What is the best UI technology: Angular or Blazor ?</title>
      <link>https://vandiest.biz/post/2022/11/02/what-is-the-best-ui-technology-angular-or-blazor/</link>
      <guid isPermaLink="true">https://vandiest.biz/post/2022/11/02/what-is-the-best-ui-technology-angular-or-blazor/</guid>
      <pubDate>Wed, 02 Nov 2022 08:00:00 GMT</pubDate>
      <category>Angular</category>
      <category>ASP.NET</category>
      <category>JavaScript</category>
      <description><![CDATA[<p><em>What is the best UI technology: Angular or Blazor ?&nbsp; </em></p>
          <p><em>(When you build enterprise business apps using DotNet)</em></p>
          <p><em><img src="https://vandiest.biz/img/2022/what-is-the-best-ui-technology-angular-or-blazor-01.jpg" alt width="624" height="373" loading="lazy"></em></p>
          <h2>Introduction</h2>
          <p>This post compares and explains in detail the pros and cons of the 2 most used SPA frameworks by .net developers, Angular and Blazor.</p>
          <p>My customers are enterprises that use .Net for building their line of business applications. I compared these 2 UI technologies, to find which one is the best fit for them and when they should use them instead of traditional ASP.NET MVC.</p>
          <p>This study is primarily based on my many years of experience in architecting and developing solutions based on Angular and .NET. It is based on research over the internet and the feedback of other architects and IT consultants. Because I had no formal experience with Blazor I invested time in studying the Blazor framework and in building some small applications with Blazor. I also interviewed other developers that had experience with Blazor and integrated their feedback into this study.</p>
          <h2>Single Page Applications</h2>
          <p>There are two general approaches to building web applications today: traditional web applications also called Multi Page Applications (MPA&rsquo;s) because they are made of multiple pages. These perform most of the application logic on the server. On the other hand, you have single-page applications (SPAs) that perform most of the user interface logic in a web browser, communicating with the web server primarily using web APIs. A hybrid approach is also possible, the simplest being to host one or more rich SPA-like sub-applications within a larger traditional web application.</p>
          <p><img src="https://vandiest.biz/img/2022/what-is-the-best-ui-technology-angular-or-blazor-02.png" alt width="624" height="773" loading="lazy"></p>
          <p>MPA reloads the entire page and displays the new one when a user interacts with the web app.&nbsp;</p>
          <p>A SPA is more like a traditional application because it&rsquo;s initially loaded and then it refreshes part of the page without reloading it. When a user interacts with the application, it displays content without the need to be fully updated since different pieces of content are downloaded automatically as per request. It is possible thanks to AJAX technology. In single-page applications, there is only one HTML page, and this one page downloads a bunch of assets, CSS, and images but typically also a lot of JavaScript. Speaking of the latter, the code will then listen to clicks on links and then re-render parts of the DOM in the loaded page whenever a user needs something.&nbsp;</p>
          <p>Although SPA applications are extremely popular because they usually account for a better user experience it does not mean that every web application should be built with a SPA architecture. Let&rsquo;s take, for instance, a news portal like CNN. It is a good example of a multi-page application. How can we see it? Just click any link and watch the reload icon on the top of your browser. You see that reloading has started because a browser is now reaching out to our public server and fetching that page and all the resources needed. The interesting thing about multi-page applications is that every new page is downloaded. Every request we send to the server, like whenever we type a new URL or click on a link, leads to a new page being sent back from the server. Notable examples of multiple-page applications are giants like Amazon or eBay. Using them, you always get a new file for every request.</p>
          <p>ASP. Net Core MVC is an excellent choice for websites that are better developed as MPAs (see later). But as explained here above SPAs usually supplies a better user experience when our application is more like an interactive application.&nbsp; &nbsp;</p>
          <h2>Angular</h2>
          <p>Angular still is by far the most used SPA framework by .NET developers. This is because it has a lot of similarities with the .Net ecosystem.</p>
          <ul>
          <li>TypeScript: Angular use TypeScript by default. This means that all documentation, articles, and source code are provided in TypeScript. TypeScript is a superset of JavaScript that compiles JavaScript. C# and Typescript are both designed by Anders Hejlsberg, and in many ways, TypeScript feels like the result of adding parts of C# to JavaScript. They share similar features and syntax, and both use the compiler to check for type safety. So, it's more straightforward to learn TypeScript for a C# dev as JavaScript.</li>
          <li>MVVM: Angular uses the MVVM pattern. This Pattern was invented for WPF &amp; Silverlight (.NET). So many developers coming from .NET that wanted to do Web development have turned to Angular because it used the same pattern.</li>
          <li>All in the box: Unlike other Js frameworks like Vue or React, Angular is a complete framework that tends to supply everything you need, libs, and tooling and spare you of the dependency hell so that you can focus on your application. Just like the .NET framework.&nbsp;</li>
          </ul>
          <p>&nbsp;Most of the other Js frameworks are exactly the opposite. They only focus on one aspect but leave you with a lot of choices. Although focusing on one thing results in frameworks that are simpler to learn, .NET enterprise developers are annoyed when they must make a lot of choices. They prefer opinionated frameworks that help them in getting productive faster with better supportability.</p>
          <p>Angular is a part of the&nbsp;<a href="https://www.altexsoft.com/blog/engineering/javascript-ecosystem-38-tools-for-front-and-back-end-development/">JavaScript ecosystem</a>&nbsp;and one of the most popular SPA frameworks today. AngularJs the preceding version, was introduced by Google in 2009 and was largely adopted by the development community.</p>
          <p>In September 2016, Google released Angular 2. It was a complete rewrite of the framework by the same team. The recent version of the framework uses TypeScript as a programming language.&nbsp;<a href="https://www.sitepoint.com/typescript-tutorial-for-beginners/">TypeScript</a>&nbsp;is a typed superset of JavaScript which has been built and maintained by Microsoft. The presence of types makes the code written in TypeScript less prone to run-time errors. As explained here above, TypeScript is the primary reason Angular is so popular in the .NET community.</p>
          <p>The difference between the old Angular and the second version is so radical that you can&rsquo;t update from AngularJs (the older version). Migrating the applications to Angular requires too many modifications due to different syntax and architecture. This means that upgrading our existing AngularJs apps to the new Angular framework will lead to a complete rewrite.&nbsp;</p>
          <p>TypeScript compiles JavaScript and integrates perfectly with the JavaScript ecosystem. So Angular is part of the JavaScript ecosystem and uses npm to manage dependencies. It can count on the largest community and number of libraries today. With Angular, you use the largest open-source ecosystem where you can find everything that you need.&nbsp;</p>
          <p>But the vast number of packages and their fast-changing rate is also a challenge in the enterprise. Making these packages available is the biggest challenge. Take a new Angular application as a sample. An empty new Angular application has the following direct dependencies:</p>
          <ul>
          <li><em>@angular</em></li>
          <li><em>@angular/cli</em></li>
          <li><em>@ngrx</em></li>
          <li><em>rxjs</em></li>
          <li><em>typescript</em></li>
          <li><em>tslint</em></li>
          <li><em>codelyzer</em></li>
          <li><em>zone.js</em></li>
          <li><em>core-js</em></li>
          </ul>

          <p>And now let&rsquo;s install all the dependencies by typing:</p>
          <p>npm i<br> ... one eternity later ...<br> added 1174 packages in 116.846s</p>
          <p>A new regular Angular application without any add-on uses already 1200 packages!&nbsp;</p>
          <p>When your ecosystem has this number of dependencies, you can&rsquo;t inspect them all yourself. You can&rsquo;t hope they&rsquo;re all secure. You can&rsquo;t assume they&rsquo;ve all got permissive licenses. You can&rsquo;t maintain your registry by hand. You can&rsquo;t formally approve packages by comity, and you can&rsquo;t maintain this as many packages you&rsquo;re using will get an update every single hour. You must move from manual, discrete processes to automated, continuous processes.&nbsp;</p>
          <p>Angular comes with great tools, supported by a large community. Angular has been around for over a decade, while Blazor has been in the market for only a few years. Angular is a production-ready framework with full support for MVVM applications and it is being used by many large companies. Blazor, while being used by some large brands, is early in its lifecycle. Angular and other popular Js frameworks like Razor or View come with much more content in the form of courses, books, blogs, videos, and other materials. Angular has dedicated tech events held worldwide, a huge community, and it supplies a broad choice of third-party integrations.</p>
          <p>To illustrate the difference in adoption, consider the graphic here that compares the number of jobs offered by frontend technology. Angular comes second when Blazor is somewhere between the others:</p>
          <p><img src="https://vandiest.biz/img/2022/what-is-the-best-ui-technology-angular-or-blazor-03.png" alt width="624" height="354" loading="lazy"></p>
          <p>As illustrated here above, despite being one of the oldest and one of the most mature SPA frameworks Angular is not the most used or loved framework in the JavaScript community. This is primarily due to his complexity. Even for seasoned JavaScript developers, Angular is difficult to master. His strength of being an opinionated framework using TypeScript comes at the expense of a steep learning curve.&nbsp;</p>
          <p>This is even more challenging for most .NET developers who usually have only a marginal knowledge of JavaScript. They not only have to learn a new language they also must learn an entirely new ecosystem and framework. In my own experience, this is the greatest challenge for organizations using .NET. This can be solved by recruiting Angular specialists that focus on the frontend part. Having a frontend specialist usually improves the user experience but it comes at the expense of having to segregate your devs in frontend (Angular) and backend devs (.NET).&nbsp;</p>
          <p>Mixing two technologies coming from different worlds diminishes the flexibility of resource allocation. People must specialize and the potential number of tasks they can work on diminishes. It also increases development time as you need to put more effort into integrating both technologies.</p>
          <p>My own experience with working with .net developers having to learn Angular confirms that the learning curve is very steep for them. In the past, to cope with this problem, we recruited Angular specialists to coach our .Net teams. We also had to invest in our CI/CD pipeline to support our new technology stack. As a .Net shop that desired to adopt Angular, we had to make a considerable investment in recruiting the right profiles, supplying the right guidance, and investing in building and adapting our package management process and tools to the high velocity and number of packages that are typical for the Javascript ecosystem. Also recruiting the right skills was a challenge for the reasons explained here above and because Angular is somewhat losing traction in the Js community.</p>
          <h3><strong>When to choose Angular over ASP.NET MVC?</strong></h3>
          <p>If the team is unfamiliar with JavaScript or TypeScript but is familiar with pure server-side MVC development, then they will probably be able to deliver a traditional MVC App more quickly than an Angular app. Unless the team has already learned Angular or unless the user experience afforded by Angular is needed, the traditional MVC is the more productive choice.&nbsp;</p>
          <h2>Blazor</h2>
          <p>Blazor is a relatively new Microsoft ASP.NET Core web framework that allows developers to write code for browsers in C#. Blazor is based on existing web technologies like HTML and CSS but then allows the developer to use C# and Razor syntax instead of JavaScript. Razor is the template markup syntax used by ASP.NET MVC and MVC developers can reuse most of their skills to build apps with Blazor.&nbsp;</p>
          <p>Blazor is very promising for .NET developers as it enables them to create SPA applications using C#, without the need for significant JavaScript skills.&nbsp;</p>
          <p>An advantage of Blazor is that it uses the latest web standards and does not need added plugins or add-ons to run in different deployment models. It can run in the browser using WebAssembly and offers a server-side ASP.NET Core option.&nbsp;&nbsp;</p>
          <p>Learning Blazor for .NET developers is nothing compared to Angular or other Js frameworks. First, they don&rsquo;t need to master JavaScript and TypeScript but also, but they can use the enterprise-friendly .NET ecosystem they already know. For a .NET dev already accustomed to web development through MVC, we can say that developers can be productive in a matter of days/weeks compared to months/years with Angular.</p>
          <p>But the biggest advantage of Blazor is its productivity. It allows .NET developers to build entire applications with only C#. It feels more like building desktop apps than web apps. When using the server-side version developers don&rsquo;t have to think about data transfer between client and server. Also, with the client-side version, it&rsquo;s a lot easier than with Angular as it&rsquo;s all integrated within one .NET solution.&nbsp;</p>
          <p>Blazor seems even more productive than traditional MVC as it doesn&rsquo;t require the developer to cope with server page refreshes and can use client rendering and events. It feels like what was developing with Silverlight or WPF but using Razor, Html, and CSS instead of Xaml. This was also confirmed by our discussion with a consultant at Gartner. They confirmed that based on experiences with several customers, Blazor was the most productive technology for building UIs inside the .NET ecosystem, even compared to MVC.</p>
          <p>The adoption of Blazor outside the .NET community will probably remain low. Blazor is hard to sell to the current web developer because it means leaving behind many of the libraries and technologies that have been up for over a decade of modern JavaScript development. This means that Blazor will probably stay a niche technology. Blazor is and will be used by teams that know .NET for building internal business applications but will probably not be adopted outside this community. That means that the community and tools will mostly be supported by Microsoft. The tool and libraries support from the open-source community and vendors will remain less compared to Angular or other mainstream Js frameworks.&nbsp;</p>
          <p>One of the impediments to Blazor adoption by the .NET community is that Blazor will be the next Silverlight. They fear that it will be around only for a brief period. More than 10 years after the final Microsoft Silverlight release, some developers&nbsp;<a href="https://visualstudiomagazine.com/articles/2021/08/17/silverlighted.aspx">still fear being 'Silverlighted,'</a>&nbsp;or seeing a development product in which they have invested heavily abandoned by Microsoft. This is because both technologies are similar, both are developed by Microsoft and allow us to use the .NET platform to create web applications. They both share the same core idea: Instead of using JavaScript, we can use C# as the programming language to create web applications.&nbsp;</p>
          <p>Inside the enterprise, this risk is largely mitigated by the fact that Microsoft has always supplied relatively long support periods. Even for Silverlight Microsoft continued to support it for nearly 10 years before retiring it. This risk has also a lot less impact as Blazor is not dependent on a browser plugin. Microsoft learned a lot from this story. First, ASP.NET and later ASP.NET Core do not require any browser plugins to run. Also, Microsoft decided to only use open web standards for Blazor. As long as the browsers support those open web standards, they will also support Blazor.</p>

          <h3><strong>Client Side (Wasm) vs. Server Side Blazor</strong></h3>
          <p>Blazor has a separation between how it calculates UI changes (app/component model) and how those changes are applied (renderer). This decouples the renderer from the platform and allows to have the same programming model for different platforms.&nbsp;</p>
          <p><img src="https://vandiest.biz/img/2022/what-is-the-best-ui-technology-angular-or-blazor-04.png" alt width="231" height="257" loading="lazy"></p>
          <p>Currently, Microsoft supports only 2 renderers, Blazor client (WebAssembly Renderer) and Blazor Sever (Remote Renderer). The implementation code is the same but what differs is the configuration of the hosting middleware. A good practice is to isolate your code from changes to the hosting model by using a class library where you put all the rendering code. This will ease the job if you later must migrate to another hosting model or when you need to support another supplementary model.</p>
          <h3><strong>Blazor Server</strong></h3>
          <p>With Blazor Server you use a classic ASP.NET Core website like ASP.NET MVC but the UI refreshes are handled over a SignalR connection.&nbsp;&nbsp;</p>
          <p><img src="https://vandiest.biz/img/2022/what-is-the-best-ui-technology-angular-or-blazor-05.png" alt width="265" height="168" loading="lazy"></p>
          <p>As for a SPA, one page is displayed on the browser and then parts of it are refreshed. On the server at the first request, the Html is generated from the Razor pages. When UI updates are triggered by user interaction and app events a UI diff is calculated and sent to the client over a SignalR connection. Then, the client-side Js library used by Blazor server applies the changes in the browser.&nbsp;&nbsp;</p>
          <p>Using Blazor Server behind load balancers or proxy servers requires special attention. As described in the&nbsp;<a href="https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/proxy-load-balancer?view=aspnetcore-6.0&amp;viewFallbackFrom=aspnetcore-2.2#other-proxy-server-and-load-balancer-scenarios">article here</a>, forwarded headers should be enabled (default configuration in IIS) and the load balancers should support sticky sessions. You can also use Redis to create a backplane as described&nbsp;<a href="https://medium.com/it-dead-inside/lets-learn-blazor-blazor-server-with-signalr-457bec513d99">here</a>.&nbsp;</p>
          <h3><strong>Blazor Client (WebAssembly)</strong></h3>
          <p><strong><img src="https://vandiest.biz/img/2022/what-is-the-best-ui-technology-angular-or-blazor-06.png" alt width="255" height="215" loading="lazy"></strong></p>
          <p>Here .Net code is running inside the browsers. The application code is compiled in WebAssembly (a sort of Assembly language for the web) and sent together with the .Net runtime to the browser. The Blazor WebAssembly runtime uses JavaScript interop to handle DOM manipulation and browser API calls.</p>
          <p>WebAssembly is an open web standard and is supported by all modern web browsers. Blazor is not the only technology using WebAssembly, most of the other big vendors develop tools and platforms that make use of WebAssembly.</p>

          <h3><strong>Blazor Server vs. client recommendation</strong></h3>
          <p>For traditional enterprise business applications, Blazor Server seems usually the best option but for certain scenarios, Blazor Wasm has clear advantages :</p>
          <p>-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; When the responsiveness of your app must be optimal, the low latency of Blazor server will optimize the responsiveness.</p>
          <p>-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; You need offline support.</p>
          <p>-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; You expect many users (&gt;100). Blazor Server is a less scalable solution as many users require server resources to handle multiple client connections and client states.</p>
          <table>
          <tbody>
          <tr>
          <td>&nbsp;</td>
          <td>
          <p><strong>Weight</strong></p>
          </td>
          <td>
          <p><strong>Blazor Server</strong></p>
          </td>
          <td>
          <p><strong>Score</strong></p>
          </td>
          <td>
          <p><strong>Total</strong></p>
          </td>
          <td>
          <p><strong>Blazor Client</strong></p>
          </td>
          <td>
          <p><strong>Score</strong></p>
          </td>
          <td>
          <p><strong>Total</strong></p>
          </td>
          </tr>
          <tr>
          <td>
          <p><strong>Learning curve</strong></p>
          </td>
          <td>
          <p>5</p>
          </td>
          <td>
          <p>Easier to configuration because security model.</p>
          </td>
          <td>
          <p>8</p>
          </td>
          <td>
          <p>40</p>
          </td>
          <td>
          <p>More complex, same problems as with SPA but not significantly harder as server side Blazor.</p>
          </td>
          <td>
          <p>7</p>
          </td>
          <td>
          <p>35</p>
          </td>
          </tr>
          <tr>
          <td>
          <p><strong>Productivity</strong></p>
          </td>
          <td>
          <p>5</p>
          </td>
          <td>
          <p>Enable to build 2 tier apps that demand less effort as 3 tier apps.</p>
          </td>
          <td>
          <p>9</p>
          </td>
          <td>
          <p>45</p>
          </td>
          <td>
          <p>Enforce 3 tier and use of API.</p>
          </td>
          <td>
          <p>7</p>
          </td>
          <td>
          <p>35</p>
          </td>
          </tr>
          <tr>
          <td>
          <p><strong>Supportability </strong></p>
          </td>
          <td>
          <p>5</p>
          </td>
          <td>
          <p>The same as traditional MVC apps.</p>
          </td>
          <td>
          <p>9</p>
          </td>
          <td>
          <p>45</p>
          </td>
          <td>
          <p>More difficult to support due to security model.</p>
          </td>
          <td>
          <p>7</p>
          </td>
          <td>
          <p>35</p>
          </td>
          </tr>
          <tr>
          <td>
          <p><strong>Maturity</strong></p>
          </td>
          <td>
          <p>4</p>
          </td>
          <td>
          <p>First version with production support.</p>
          </td>
          <td>
          <p>7</p>
          </td>
          <td>
          <p>28</p>
          </td>
          <td>
          <p>Came later but now it's supported in production by Microsoft.</p>
          </td>
          <td>
          <p>7</p>
          </td>
          <td>
          <p>28</p>
          </td>
          </tr>
          <tr>
          <td>
          <p><strong>Community</strong></p>
          </td>
          <td>
          <p>3</p>
          </td>
          <td>
          <p>For the moment adoption seems better.</p>
          </td>
          <td>
          <p>6</p>
          </td>
          <td>
          <p>18</p>
          </td>
          <td>
          <p>WebAssembly has highest buzz so probably the most adoption in the long run.</p>
          </td>
          <td>
          <p>7</p>
          </td>
          <td>
          <p>21</p>
          </td>
          </tr>
          <tr>
          <td>
          <p><strong>Enforce Best practice</strong></p>
          </td>
          <td>
          <p>3</p>
          </td>
          <td>
          <p>Does not enforce 3 tiers or MVVM.</p>
          </td>
          <td>
          <p>4</p>
          </td>
          <td>
          <p>12</p>
          </td>
          <td>
          <p>Enforce 3 tiers, using 3 tiers is usually a good practice.</p>
          </td>
          <td>
          <p>8</p>
          </td>
          <td>
          <p>24</p>
          </td>
          </tr>
          <tr>
          <td>
          <p><strong>Performance</strong></p>
          </td>
          <td>
          <p>2</p>
          </td>
          <td>
          <p>Better performance at load time but a lot less scalable.</p>
          </td>
          <td>
          <p>6</p>
          </td>
          <td>
          <p>12</p>
          </td>
          <td>
          <p>Longer load time but a lot more scalable.</p>
          </td>
          <td>
          <p>7</p>
          </td>
          <td>
          <p>14</p>
          </td>
          </tr>
          <tr>
          <td>&nbsp;</td>
          <td>

          </td>
          <td>

          </td>
          <td>

          </td>
          <td>

          </td>
          <td>

          </td>
          <td>&nbsp;</td>
          <td>

          </td>
          </tr>
          <tr>
          <td>
          <p><strong>Risk</strong></p>
          </td>
          <td>
          <p>Impact</p>
          </td>
          <td>

          </td>
          <td>

          </td>
          <td>

          </td>
          <td>

          </td>
          <td>&nbsp;</td>
          <td>

          </td>
          </tr>
          <tr>
          <td>
          <p><strong>Security </strong></p>
          </td>
          <td>
          <p>4</p>
          </td>
          <td>
          <p>No more risk as a classical MVC.</p>
          </td>
          <td>
          <p>0</p>
          </td>
          <td>
          <p>0</p>
          </td>
          <td>
          <p>Higher risk for misconfiguration.</p>
          </td>
          <td>
          <p>-1</p>
          </td>
          <td>
          <p>-4</p>
          </td>
          </tr>
          <tr>
          <td>
          <p><strong>Lifecycle support</strong></p>
          </td>
          <td>
          <p>5</p>
          </td>
          <td>
          <p>No dependency on browser support.</p>
          </td>
          <td>
          <p>0</p>
          </td>
          <td>
          <p>0</p>
          </td>
          <td>
          <p>Browsers need to continue to support WebAssembly standard, but the probability is low.</p>
          </td>
          <td>
          <p>-1</p>
          </td>
          <td>
          <p>-5</p>
          </td>
          </tr>
          <tr>
          <td>

          </td>
          <td>

          </td>
          <td>

          </td>
          <td>

          </td>
          <td>

          </td>
          <td>

          </td>
          <td>

          </td>
          <td>

          </td>
          </tr>
          <tr>
          <td>
          <p><strong>Total (Max 360)</strong></p>
          </td>
          <td>

          </td>
          <td>

          </td>
          <td>

          </td>
          <td>
          <p><strong>200</strong></p>
          </td>
          <td>

          </td>
          <td>

          </td>
          <td>
          <p><strong>183</strong></p>
          </td>
          </tr>
          </tbody>
          </table>

          <h3><strong>When to choose Blazor over ASP.NET MVC ?</strong></h3>
          <p>As developing or maintaining apps using Blazor is more productive than ASP.NET MVC and easy to learn, Blazor is usually the best choice except when:</p>
          <p><em>-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Your application has simple, possibly read-only requirements.</em></p>
          <p>Many web applications are primarily consumed in a read-only fashion by most of their users. Read-only (or read-mostly) applications tend to be much simpler than those applications that maintain and manipulate a great deal of state. For example, our intranet or public websites where anonymous users can easily make requests, and there is little need for client-side logic. This type of public-facing web site usually consists mainly of content with little client-side behavior. Such applications are easily built as traditional ASP.NET MVC web applications, which perform logic on the web server and render HTML to be displayed in the browser. The fact that each unique page of the site has its own URL that can be bookmarked and indexed by search engines (by default, without having to add this functionality as a separate feature of the application) is also a clear benefit in such scenarios.</p>
          <p>-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Your application ecosystem is based on MVC and is too small or too simple to invest in Blazor.</p>
          <p>When your applications consist mainly of server-side processes and your UI is quite simple (e.g., Dashboard, back-office CRUD apps, &hellip;) and you&rsquo;ve already made a strong investment in MVC then adding Blazor could not be worth the investment.</p>
          <h2>Angular vs. Blazor Score Card</h2>
          <p>Here we selected several criteria and attributed weight to the function of the specific needs and environment of Enterprises using .Net as their main technology stack. But, each organization is unique and adapt the scoring based on its specific needs.&nbsp; &nbsp;</p>
          <p>The weight ranges from 1 to 5. 5 indicates an especially important criterion, starting from 1 as minor importance to 5 as the top priority.</p>
          <p>The score for each criterion ranges from 0 to 10. 10 shows an exceptionally high-quality attribute, 1 is the minimum.</p>
          <p>We also take account of Risks at the bottom of the scorecard. Here the weight indicates the risk potential impact and the score the probability. Staring from 0 as 0% probability to -10 indicates a risk that will occur with 100% probability.</p>
          <ul>
          <li>Learning curve: the amount time a developer, on average, needs to invest to become productive with the technology.</li>
          <li>Productivity: how productive the developers are with the given platform once it has mastered the technology.</li>
          <li>Supportability: how well can we efficiently support the platform?</li>
          <li>Community: how valuable, responsive and broad is the developer's community? When a platform has a strong community the organization, its developers and its users all benefit.</li>
          <li>Maturity: is the platform used for long enough that most of its first faults and inherent problems have been removed or reduced?</li>
          <li>Full Stack efficiency: how efficient is it for the project to work and to cope with the separation of the frontend and the backend? Does the adoption of this technology diminish the efficiency of the project teams and the organization because they must deal with different skill sets for backend and frontend?</li>
          <li>Finding resources: how easy is it to recruit developers on the market?</li>
          <li>Libraries choices: a good library well suited for the needs and well maintained improves productivity while minimizing the TCO. A large choice of libraries increases the probability of finding a library that satisfies the needs.</li>
          <li>Opinionated: a platform that believes a certain way of approaching a problem is inherently better and enables the developer to spend less time finding the right way of solving a problem.</li>
          <li>Performance: which platform has the fastest render time, load time, and refresh time, and consumes a minimal number of resources?</li>
          </ul>

          <table>
          <tbody>
          <tr>
          <td>&nbsp;</td>
          <td>
          <p><strong>Weight</strong></p>
          </td>
          <td>
          <p><strong>Blazor</strong></p>
          </td>
          <td>
          <p><strong>Score</strong></p>
          </td>
          <td>
          <p><strong>Total</strong></p>
          </td>
          <td>
          <p><strong>Angular</strong></p>
          </td>
          <td>
          <p><strong>Score</strong></p>
          </td>
          <td>
          <p><strong>Total</strong></p>
          </td>
          </tr>
          <tr>
          <td>
          <p><strong>Learning curve</strong></p>
          </td>
          <td>
          <p>5</p>
          </td>
          <td>
          <p>Easy to learn for .Net devs, especially with MVC/Razor experience.&nbsp; Non-included features require Js integration (limited).</p>
          </td>
          <td>
          <p>9</p>
          </td>
          <td>
          <p>45</p>
          </td>
          <td>
          <p>Steep learning curve even for experimented Js devs.</p>
          </td>
          <td>
          <p>3</p>
          </td>
          <td>
          <p>15</p>
          </td>
          </tr>
          <tr>
          <td>
          <p><strong>Productivity</strong></p>
          </td>
          <td>
          <p>5</p>
          </td>
          <td>
          <p>More tuned for RAD as Angular, even more productive as bare MVC (Gartner)</p>
          </td>
          <td>
          <p>9</p>
          </td>
          <td>
          <p>45</p>
          </td>
          <td>
          <p>Lot of ceremony but good tooling is provided (e.g., Visual Studio Code).</p>
          </td>
          <td>
          <p>4</p>
          </td>
          <td>
          <p>20</p>
          </td>
          </tr>
          <tr>
          <td>
          <p><strong>Supportability </strong></p>
          </td>
          <td>
          <p>5</p>
          </td>
          <td>
          <p>Included in. Net6 &amp; Visual Studio, Server Blazor Server is same as MVC web app towards supportability. (Blazor server is included in .Net 6 =&gt; not a Nuget package)</p>
          </td>
          <td>
          <p>9</p>
          </td>
          <td>
          <p>45</p>
          </td>
          <td>
          <p>Big challenges with lifecycle due to number of packages.</p>
          </td>
          <td>
          <p>2</p>
          </td>
          <td>
          <p>10</p>
          </td>
          </tr>
          <tr>
          <td>
          <p><strong>Community</strong></p>
          </td>
          <td>
          <p>4</p>
          </td>
          <td>
          <p>Backed by Micrsosoft but small community made out exclusively of .NET devs.</p>
          </td>
          <td>
          <p>3</p>
          </td>
          <td>
          <p>12</p>
          </td>
          <td>
          <p>Backed by Google and exceptionally large community.</p>
          </td>
          <td>
          <p>8</p>
          </td>
          <td>
          <p>32</p>
          </td>
          </tr>
          <tr>
          <td>
          <p><strong>Maturity</strong></p>
          </td>
          <td>
          <p>4</p>
          </td>
          <td>
          <p>Is supported since .NET core 3.1.&nbsp; and is considered by community as mature since .NET 6.</p>
          </td>
          <td>
          <p>6</p>
          </td>
          <td>
          <p>24</p>
          </td>
          <td>
          <p>Very mature &amp; battle tested.</p>
          </td>
          <td>
          <p>9</p>
          </td>
          <td>
          <p>36</p>
          </td>
          </tr>
          <tr>
          <td>
          <p><strong>Full Stack efficiency</strong></p>
          </td>
          <td>
          <p>3</p>
          </td>
          <td>
          <p>Although some devs specialize in backend or frontend work, Blazor is an integrated environment with 1 stack where 1 dev can easily work on both with high efficiency.</p>
          </td>
          <td>
          <p>9</p>
          </td>
          <td>
          <p>27</p>
          </td>
          <td>
          <p>Some Angular/.NET real full stack devs are available. Experience shows that when 2 different stacks (.NET &amp; Angular) are used this often leads to a segregation of devs in frontend and backend devs what diminish efficiency.</p>
          </td>
          <td>
          <p>4</p>
          </td>
          <td>
          <p>12</p>
          </td>
          </tr>
          <tr>
          <td>
          <p><strong>Finding resources</strong></p>
          </td>
          <td>
          <p>3</p>
          </td>
          <td>
          <p>Finding experienced Blazor devs is more difficult as finding Angular devs. Nevertheless, for MVC devs, learning Blazor is easy.</p>
          </td>
          <td>
          <p>4</p>
          </td>
          <td>
          <p>12</p>
          </td>
          <td>
          <p>Angular is one of the mainstream Js frameworks but a lot of JS frameworks exist and finding Angular devs that know .Net is not easy.</p>
          </td>
          <td>
          <p>6</p>
          </td>
          <td>
          <p>18</p>
          </td>
          </tr>
          <tr>
          <td>
          <p><strong>Libraries choices</strong></p>
          </td>
          <td>
          <p>3</p>
          </td>
          <td>
          <p>Limited choice of Open-source library. Major vendors Teleric or DevExpress have Blazor component UI libraries.&nbsp;</p>
          </td>
          <td>
          <p>3</p>
          </td>
          <td>
          <p>9</p>
          </td>
          <td>
          <p>Large choice open source and commercial libraries.</p>
          </td>
          <td>
          <p>8</p>
          </td>
          <td>
          <p>24</p>
          </td>
          </tr>
          <tr>
          <td>
          <p><strong>Opinionated</strong></p>
          </td>
          <td>
          <p>2</p>
          </td>
          <td>
          <p>Although it's a full framework with databinding, Devs have more choices to make as with Angular.</p>
          </td>
          <td>
          <p>7</p>
          </td>
          <td>
          <p>14</p>
          </td>
          <td>
          <p>Angular is very opinionated and complete.</p>
          </td>
          <td>
          <p>8</p>
          </td>
          <td>
          <p>16</p>
          </td>
          </tr>
          <tr>
          <td>
          <p><strong>Performance</strong></p>
          </td>
          <td>
          <p>2</p>
          </td>
          <td>
          <p>Less performant but still very acceptable</p>
          </td>
          <td>
          <p>7</p>
          </td>
          <td>
          <p>14</p>
          </td>
          <td>
          <p>Framework was tuned over the years towards performance.</p>
          </td>
          <td>
          <p>8</p>
          </td>
          <td>
          <p>16</p>
          </td>
          </tr>
          <tr>
          <td>&nbsp;</td>
          <td>

          </td>
          <td>

          </td>
          <td>

          </td>
          <td>

          </td>
          <td>

          </td>
          <td>&nbsp;</td>
          <td>

          </td>
          </tr>
          <tr>
          <td>
          <p><strong>Risk</strong></p>
          </td>
          <td>

          </td>
          <td>

          </td>
          <td>

          </td>
          <td>

          </td>
          <td>

          </td>
          <td>&nbsp;</td>
          <td>

          </td>
          </tr>
          <tr>
          <td>
          <p>Abandon technology within 5J</p>
          </td>
          <td>
          <p>4</p>
          </td>
          <td>
          <p>Taking Ms lifecycle into consideration the chance is extremely low for 5Y.</p>
          </td>
          <td>
          <p>-1</p>
          </td>
          <td>
          <p>-4</p>
          </td>
          <td>
          <p>Chances are low but AngularJs has proven that even when the tech is successful vendors can abandon it.</p>
          </td>
          <td>
          <p>-1</p>
          </td>
          <td>
          <p>-4</p>
          </td>
          </tr>
          <tr>
          <td>
          <p>Adoption falls within the community</p>
          </td>
          <td>
          <p>3</p>
          </td>
          <td>
          <p>The probability is low but exists that .NET devs don't continue to adopt the tech due to "Silverlight" history.</p>
          </td>
          <td>
          <p>-2</p>
          </td>
          <td>
          <p>-6</p>
          </td>
          <td>
          <p>Angular has a large community but it's losing traction.</p>
          </td>
          <td>
          <p>-1</p>
          </td>
          <td>
          <p>-3</p>
          </td>
          </tr>
          <tr>
          <td>
          <p><strong>Total (Max 360)</strong></p>
          </td>
          <td>

          </td>
          <td>

          </td>
          <td>

          </td>
          <td>
          <p><strong>237</strong></p>
          </td>
          <td>

          </td>
          <td>

          </td>
          <td>
          <p><strong>192</strong></p>
          </td>
          </tr>
          </tbody>
          </table>]]></description>
    </item>
    <item>
      <title>What Is a Single Page Application and when should we use it?</title>
      <link>https://vandiest.biz/post/2022/09/10/when-to-choose-for-a-spa-single-page-application-architecture/</link>
      <guid isPermaLink="true">https://vandiest.biz/post/2022/09/10/when-to-choose-for-a-spa-single-page-application-architecture/</guid>
      <pubDate>Sat, 10 Sep 2022 08:00:00 GMT</pubDate>
      <category>Single-page apps</category>
      <description><![CDATA[<p>There are two general approaches to building web applications today: traditional web applications also called Multi Page Applications (MPA&rsquo;s) because they are made of multiple pages. These perform most of the application logic on the server.&nbsp; On the other hand, you have single-page applications (SPAs) that perform most of the user interface logic in a web browser, communicating with the web server primarily using web APIs. A hybrid approach is also possible, the simplest being to host one or more rich SPA-like sub applications within a larger traditional web application.</p>
          <p>MPA reloads the entire page and displays the new one when a user interacts with the web app.&nbsp;</p>
          <p>A SPA is more like a traditional application because it&rsquo;s initially loaded and then it refreshes part of the page without reloading it. When a user interacts with the application, it displays content without the need to be fully updated since different pieces of content are downloaded automatically as per request. It is possible thanks to AJAX technology. In single-page applications, there is only one HTML page, and this one page downloads a bunch of assets, CSS, images but typically also a lot of JavaScript. Speaking of the latter, the code will then listen to clicks on links and then re-render parts of the DOM in the loaded page whenever a user need something.</p>
          <p>Although SPA applications are extremely popular because they usually account for a better user experience it does not mean that every web application should be built with a SPA architecture. Let&rsquo;s take, for instance, a news site like CNN. It is a good example of a multi-page application. How can we see it? Just click any link and watch the reload icon on the top of your browser. You see that reloading has started because a browser is now reaching out to our public server and fetching that page and all the resources needed. The interesting thing about multi-page applications is that every new page is downloaded. Every request we send to the server, like whenever we type a new URL or click on a link, leads to a new page being sent back from the server. Notable examples of multiple page applications are giants like Amazon or eBay. Using them, you always get a new file for every request.</p>
          <p>MPA is an excellent choice for websites that provides mostly read-only content. SPAs usually supplies a better user experience when our application is more like an interactive application.</p>]]></description>
    </item>
    <item>
      <title>OIDC with auth0, Angular and DotNet Core 3.1</title>
      <link>https://vandiest.biz/post/2020/01/06/oidc-with-auth0-angular-and-dotnet-core-3-1/</link>
      <guid isPermaLink="true">https://vandiest.biz/post/2020/01/06/oidc-with-auth0-angular-and-dotnet-core-3-1/</guid>
      <pubDate>Mon, 06 Jan 2020 08:00:00 GMT</pubDate>
      <category>Angular</category>
      <category>JavaScript</category>
      <description><![CDATA[<p>On <a href="https://github.com/geobarteam/auth0-angular-netcore31-demo">my github repo you&rsquo;ll</a> find a demo app based on auth0 SPA Angular quickstart. It is integrated with a .Net Core 3.1 backend. The purpose is to demonstrate how to use auth0 to secure Api calls to a NetCore31 app from an Angular 8+ SPA.</p>
          <p>To make it work you need to:</p>
          <ol>
          <li>Have a auth0 subscription.</li>
          <li>Create an Application and a API.</li>
          <li>Edit following files with your auth0 configuration info:</li>
          </ol>
          <ul>
          <li>\auth0-backend-demo\appsettings.config</li>
          <li>\auth0-angular-demo\src\app\auth.service.ts</li>
          </ul>
          <ol>
          <li>In the angular app install the npm packages: cd .\auth0-angular-demo<br> npm install</li>
          <li>start the angular app: npm start</li>
          <li>Compile and run the .NetCore Api app: cd .\auth0-backend-demo<br> dotnet restore dotnet run</li>
          <li>Open your browser and navigate to&nbsp;http://http://localhost:3000/</li>
          </ol>
          <p>To better understand how to configure your app it's recommended to first complete the auth0&nbsp;<a href="https://auth0.com/docs/quickstart/spa/angular2">angular2</a>&nbsp;quickstart.</p>
          <p>Have fun!</p>]]></description>
    </item>
    <item>
      <title>Is my dream team a feature-team?</title>
      <link>https://vandiest.biz/post/2018/10/16/is-my-dream-team-a-feature-team/</link>
      <guid isPermaLink="true">https://vandiest.biz/post/2018/10/16/is-my-dream-team-a-feature-team/</guid>
      <pubDate>Tue, 16 Oct 2018 08:00:00 GMT</pubDate>
      <category>Agile</category>
      <description><![CDATA[<p><img src="https://vandiest.biz/img/2018/is-my-dream-team-a-feature-team-01.png" alt width="446" height="299" loading="lazy"></p>
          <p>Feature teams is one of the ways of scaling agile over a larger organization, you divide your organization in many feature-teams.&nbsp; The core idea is that the team structure is not organized by component or technical layer. &nbsp;The team is long-lived, it says together but the work is cross-functional and cross-component. &nbsp;The team work on a complete customer-centric feature across all components and disciplines. &nbsp;Feature teams organization will diminish the need for handoffs and so waiting time what will result in faster delivery.&nbsp; My personal feeling is that adopting feature teams is certainly not an easy goal to achieve certainly not for maintenance teams.&nbsp; I&rsquo;m also afraid that it can also lead to unmaintainable systems over time.</p>
          <p>The first big problem I see with feature teams is the lack of accountability.&nbsp; Having everyone being responsible can rapidly become indistinguishable from having no one responsible.</p>
          <p>Another big problem is that following &nbsp;<a href="https://en.wikipedia.org/wiki/Conway%27s_law">Conways Law</a> this type of organization will produce a big fat ball of mud architecture =&gt; a system without clean interfaces.&nbsp; A system without clean interface is also a system you can't&nbsp;&nbsp;redesign or change without having to deeply understand the neighboring modules. In the end it makes the system unmaintainable.</p>
          <p>So when you want to benefit from the improved productivity of feature teams, you also need to take care of two things: discipline and design.&nbsp;&nbsp; you need to follow the principles of <a href="https://martinfowler.com/bliki/CodeOwnership.html">weak code ownership</a>.&nbsp; Basically this is the open source model of collaboration. Everyone can participate but they need to follow the rules of a despot that owns the module.&nbsp; This despot is by the way also taking responsibility of the support and will ensure that good practices as &nbsp;continuous integratione and unit testing will be followed and that the code quality stays a priority.&nbsp; No one can check-in code without his approval.&nbsp; If you need to integrate changes inside a module you need to request a Pull-Request to the owner.&nbsp; You need also a strong Architecture team&nbsp;responsible to define how&nbsp;the system is divided in modules. This team is responsible to define clear boundaries and&nbsp;to ensure that the feature-teams respect these boundaries.&nbsp;</p>]]></description>
    </item>
    <item>
      <title>Automate your DB deployment with DbUp</title>
      <link>https://vandiest.biz/post/2018/09/24/automate-your-db-deployment-with-dbup/</link>
      <guid isPermaLink="true">https://vandiest.biz/post/2018/09/24/automate-your-db-deployment-with-dbup/</guid>
      <pubDate>Mon, 24 Sep 2018 08:00:00 GMT</pubDate>
      <category>DevOps</category>
      <description><![CDATA[<p>Databases and applications should coexist in the same deployment pipeline. They should be versioned and packaged in the same way and they should share a single development lifecycle. This will enable the team to treat DB deployment like any other software deployment.&nbsp; To achieve this with SQLServer you can use <a href="https://dbup.github.io/">DbUp</a>, it&rsquo;s a simple but very effective migration tool.&nbsp; Have a look at they getting started doc: <a href="https://dbup.readthedocs.io/en/latest/">https://dbup.readthedocs.io/en/latest/</a></p>]]></description>
    </item>
    <item>
      <title>The monolith database as a primary cause of slow delivery</title>
      <link>https://vandiest.biz/post/2018/09/10/bottlenecks-linked-with-big-systems/</link>
      <guid isPermaLink="true">https://vandiest.biz/post/2018/09/10/bottlenecks-linked-with-big-systems/</guid>
      <pubDate>Mon, 10 Sep 2018 08:00:00 GMT</pubDate>
      <category>Testing</category>
      <description><![CDATA[<p><img src="https://vandiest.biz/img/2018/bottlenecks-linked-with-big-systems-01.png" alt width="215" height="217" loading="lazy"></p>
          <p>Large monolitic&nbsp;applications are characterized by big bottlenecks that slows down the entire process.&nbsp;&nbsp;The dependencies causes a significant overhead on your deployment throughput. Cycle times are longer because of the slower builds and automated and manual tests required to validate the entire app. This creates a lot of friction in the pipeline. Dependencies brings complexity what increases the development time.&nbsp;Complex code bases have more opportunities for bugs what causes many waste. The dependencies also lead to less opportunities for parallelizing the work.&nbsp;To be able to work in group, developers need to create branches so that developers can work in isolation but this demand also to merge more often what also demand a considerable effort.&nbsp;</p>
          <p>For me bottleneck number one is Manual Testing! Manual Testing is a tedious process when the test cycles are repeated frequently. Therefore many teams adopt test automation but before they can do test automation they first need to create test data. Only when teams have adequate test data they can run automated tests, and can create that data on demand, they see better IT performance, lower change failure rates, and lower levels of deployment pain and rework. The problem with large monolitic applications is that they also go with&nbsp;big databases and how bigger the database how more difficult it is to manage.&nbsp;When the test data is difficult to setup or reason about then it become difficult to do any meaningful test automation.</p>
          <p>Conclusion, if your database is a monolith your delivery process we&rsquo;ll suffer from bottlenecks caused by lack of test automation and also by communication/coordination overhead even if you have a service oriented or microservice architecture on top of it.&nbsp;</p>]]></description>
    </item>
    <item>
      <title>Best practices on how to architect for faster and better delivery in the Enterprise</title>
      <link>https://vandiest.biz/post/2018/09/10/best-practices-on-how-to-architect-for-faster-and-better-delivery-in-the-enterprise/</link>
      <guid isPermaLink="true">https://vandiest.biz/post/2018/09/10/best-practices-on-how-to-architect-for-faster-and-better-delivery-in-the-enterprise/</guid>
      <pubDate>Mon, 10 Sep 2018 08:00:00 GMT</pubDate>
      <category>Architecture</category>
      <description><![CDATA[<p><a href="https://vandiest.biz/files/Continuous-Delivery-in-the-Enterprise.pptx"><img src="https://vandiest.biz/img/2018/best-practices-on-how-to-architect-for-faster-an-01.png" alt width="2256" height="1504" loading="lazy"></a></p>
          <p>Link to my presentation about "Best practices on how to architect for faster and better delivery in the Enterprise".</p>
          <p><a href="https://vandiest.biz/files/Continuous-Delivery-in-the-Enterprise.pptx">Continuous Delivery in the Enterprise.pptx (3.79 mb)</a></p>]]></description>
    </item>
    <item>
      <title>Continous Delivery as a paradigm shift</title>
      <link>https://vandiest.biz/post/2018/01/10/continous-delivery-as-a-paradigm-shift/</link>
      <guid isPermaLink="true">https://vandiest.biz/post/2018/01/10/continous-delivery-as-a-paradigm-shift/</guid>
      <pubDate>Wed, 10 Jan 2018 08:00:00 GMT</pubDate>
      <category>DevOps</category>
      <category>Agile</category>
      <category>Testing</category>
      <description><![CDATA[<p><img src="https://vandiest.biz/img/2018/continous-delivery-as-a-paradigm-shift-01.png" alt width="623" height="507" loading="lazy"></p>
          <p>In the classical view of software delivery, you need to make a trade-off between quality and speed. The traditional IT delivery process paradigm postulate that building quality software will demand longer to deliver. You trade quality with getting things done rapidly. Now, studies on teams that have adopted Continuous Delivery (CD) demonstrate exactly the opposite. Teams able to deploy by the push of a button not only deliver faster but they deliver better quality in a more reliable way.</p>

          <p>Being able to push changes fast and reliably to production changes the rule of the game and transforms many aspects of the IT delivery process.&nbsp; It reflects on aspects like:</p>
          <p><strong>Increased Availability &amp; Manageability</strong></p>
          <p>Because you can push changes a lot faster you can make smaller changes with less downtime you don&rsquo;t need to come in the middle of the night or weekends to deploy the system.&nbsp; This means less overwork and stress for the team&rsquo;s members. It also frees up a considerable amount of time as teams don&rsquo;t need to participate at long lasting release planning's sessions to coordinate huge deployments.</p>
          <p>Because you know you can push a change fast you&rsquo;re also able to remediate faster and you don&rsquo;t need to take account of being able to reverse a giant change. In place, you can adopt release strategies where you always advance forwards. In place of reverting a change you can be confident that you can fix any problem fast. This simplify the release process and lower the investment needed in process, infrastructure and tools to be able to revert back a giant change.</p>
          <p>Reports also demonstrates that teams practicing CD sees their number of big incidents diminishing drastically. Apart from the better intrinsic quality that CD brings this is also due to the automation that CD demands.&nbsp; The automated deployment pipeline eliminates errors that might result from manual configuration and error-prone practices.&nbsp;</p>
          <p>Increased efficiency and cycle time<br> Because you make smaller changes your tester and analyst can work in a constant flow of work.&nbsp; They don&rsquo;t need to test everything in a great batch at the end of the release but can continuously work in at constant pace together with the developers.</p>
          <p>Because bugs are also detected sooner they are cheaper to fix.&nbsp; When practicing continuous delivery automated tests are run immediately after each check-in what detects many bugs immediately.&nbsp; The developer can get immediate feedback even before he starts working on another task and so he knows where to search for the bug.&nbsp; It&rsquo;s proved by many studies that bugs found before or immediately after the check-in are orders in magnitude cheaper to fix (sometimes more than 100 times).&nbsp;&nbsp; *https://www.researchgate.net/publication/255965523_Integrating_Software_Assurance_into_the_Software_Development_Life_Cycle_SDLC</p>
          <p><strong>Improved testability </strong></p>
          <p>To be able to deploy small changes independently the systems needs to be decoupled.&nbsp; This decoupling enables also to test the system more easily in isolation.&nbsp; Most of the changes can be tested without deploying to an integration environment. This lowers the number of environment needed and allows for a more rapid feedback.&nbsp;</p>
          <p><strong>Improved intrinsic product quality</strong></p>
          <p>Because the tremendous acceleration of time to market of a change, from once every 3 months to many times a day, product owners can experiment a lot faster enabling new product evolution strategies based on experimentation and continuous improvement.&nbsp; The book Lean Enterprises describes in detail how modern software companies takes advantages of continuous delivery to design products based on experimentation.</p>
          <p><strong>Improved Customer Satisfaction</strong></p>
          <p>CD brings confidence to the customers and other stakeholders on the delivery of their new features.&nbsp; CD enables to deliver new features at a constant and predictable pace and increase the visibility of the delivery process.&nbsp; By practicing feature toggles and decoupling the deployment from the actual release of the feature it enables to deploy a part or a complete new capability to early adopters.&nbsp;&nbsp;</p>

          <p>Because organizations are more effective they can spend more time on value adding activities.&nbsp; This creates a virtuous circle of continuous improvements that brings a decisive competitive advantage.&nbsp; Continuous Delivery is becoming the norm in the enterprise. IT organization that still stick to the old delivery model will be forced to change or will simply disappear.&nbsp; Following <a href="https://puppet.com/resources/whitepaper/2015-state-devops-report">the 2015 State of DevOps practices study</a>, the companies who their IT team&rsquo;s practices continuous delivery has 50 % higher market capitalization growth over the 3 last years than the other and <a href="http://www.gartner.com/binaries/content/assets/events/keywords/infrastructure-operations-management/iome5/gartner-predicts-for-it-infrastructure-and-operations.pdf">Gartner predicts</a> that by 2020 50% of CIO&rsquo;s that have not transformed their capabilities will be displaced.&nbsp;&nbsp;</p>]]></description>
    </item>
    <item>
      <title>Continous delivery can't be bought in a box!</title>
      <link>https://vandiest.biz/post/2017/11/10/continous-delivery-can-t-be-bought-in-a-box/</link>
      <guid isPermaLink="true">https://vandiest.biz/post/2017/11/10/continous-delivery-can-t-be-bought-in-a-box/</guid>
      <pubDate>Fri, 10 Nov 2017 08:00:00 GMT</pubDate>
      <category>DevOps</category>
      <description><![CDATA[<p><img src="https://vandiest.biz/img/2017/continous-delivery-can-t-be-bought-in-a-box-01.jpg" alt width="606" height="403" loading="lazy"></p>
          <p>Continuous delivery is the ability to get changes into production safely and quickly in a sustainable way.&nbsp;<br>It&rsquo;s not only about deploying code in production but it&rsquo;s about bringing all type of changes that helps to deliver value faster.&nbsp;This includes all types of IT infrastructure changes, software configuration changes, deploying data and schema migrations, &hellip;<br>It is a capability an enterprise need to develop and learn. Because it&rsquo;s based on new paradigms that demands deep organizational changes, it&rsquo;s not something an enterprise can acquire in a box but it need to develop it through a change journey.</p>
          <p>It took Amazon or Facebook years to change their organization and architecture so that they could be sustainable by delivering faster. It can demand tremendous efforts and changes to the culture and architecture.&nbsp;<br>Like any big change in an organization it&rsquo;s not linear, how you will achieve the goal is largely unpredictable. Therefore like any change in the enterprise, it need to be carefully managed to ensure that current operations are not adversely affected.</p>]]></description>
    </item>
    <item>
      <title>How to design your SPA to work with Angular CLI and DoNet Core</title>
      <link>https://vandiest.biz/post/2017/09/10/how-to-design-your-spa-to-work-with-angular-cli-and-donet-core1/</link>
      <guid isPermaLink="true">https://vandiest.biz/post/2017/09/10/how-to-design-your-spa-to-work-with-angular-cli-and-donet-core1/</guid>
      <pubDate>Sun, 10 Sep 2017 08:00:00 GMT</pubDate>
      <category>Angular</category>
      <category>JavaScript</category>
      <category>Single-page apps</category>
      <description><![CDATA[<p>When building new apps, you need both client and server-side pieces.&nbsp; This article focusses on how to structure a SPA that is made out of angular for the client and dotnet core for the server.</p>
          <p>There are numerous questions I needed to answers when mixing these technologies.</p>
          <ul>
          <li>Should I host my angular app inside an MVC View or just serve the angular app from static files.</li>
          <li>Should I host the angular app inside the same application that serve the Web-Api or serve it from a separate site?</li>
          <li>How to setup a productive development environment?</li>
          <li>How should I integrate angular-cli together with a ASP.NET Core app?</li>
          <li>Should I use Vs.Code or VisualStudio ?</li>
          <li>How to package and deploy the application for on prem or in the cloud?</li>
          </ul>
          <p>Of course, the answer to all these questions is always the same: it depends! After experimenting with several options, I finally decided that I should go for the simplest solution that could possibly work and started with just serving static files inside a Web-Api application.&nbsp; Yet, during development, what I found the most productive, is to completely separate the Angular app from the Web-Api solution. I like that my Angular and the ASP.NET Web-Api apps live separate folders beside each other. It&rsquo;s only at build time that Msbuild assemble the parts by calling &ldquo;ng build&rdquo;. &nbsp;To do that I needed to configured angular-cli to output the build in the \wwwroot folder of the ASP.NET app.</p>
          <p>Concerning the IDE, what I found the most productivity was to develop my angular app using Vs.Code. &nbsp;Thus far, for the Web-Api part I tend to prefer VisualStudio and Resharper. So, I optimized my Angular app for working with Vs.Code and my Web-Api project for VisualStudio.&nbsp; During development I usually run the backend and launch the Webpack dev server through an &ldquo;npm start&rdquo; command.&nbsp; At publish everything is nicely packaged together inside a ASP.NET app, so deploying to Azure can simply be done by using the VisualStudio publish wizard.&nbsp;&nbsp;</p>
          <p>On my github project <a href="https://github.com/geobarteam/core-angular-example">core-angular-example</a>, you can download the solution template I use for making SPA&rsquo;s with Angular-cli and DotNet Core.</p>
          <p>Here I explain the steps you need to follow to setup the basis of such an app:</p>
          <p>0) First you need to install:</p>
          <ul>
          <li><a href="https://code.visualstudio.com/">Code</a></li>
          <li><a href="https://www.microsoft.com/net/core#windowscmd">Donetcore</a></li>
          <li><a href="https://nodejs.org/en/">js</a></li>
          <li><a href="https://github.com/angular/angular-cli#installation">Angular-cl</a>i</li>
          </ul>
          <p>1) From a powershell console:</p>
          <pre><code class="language-powershell">mkdir angular-dotnetcore
cd angular-dotnetcore
mkdir web-api
cd web-api
dotnet new webapi
cd ..
ng new angular-app</code></pre>
          <p>2) Open VisualStudio code and open the angular-dotnetcore folder</p>
          <p><img src="https://vandiest.biz/img/2017/how-to-design-your-spa-to-work-with-angular-cli-01.png" alt width="290" height="563" loading="lazy"></p>
          <p>3) Disable TypeScript compilation<br> Since we will eventually have TypeScript files in our project, we should also disable any TypeScript compilation errors. Open the web-api.csproj file and modify the top PropertyGroup:</p>

          <pre><code class="language-html">&lt;PropertyGroup&gt;

  &lt;TargetFramework&gt;netcoreapp2.0&lt;/TargetFramework&gt;

  &lt;TypeScriptCompileBlocked&gt;true&lt;/TypeScriptCompileBlocked&gt;

  &lt;TypeScriptToolsVersion&gt;Latest&lt;/TypeScriptToolsVersion&gt;

  &lt;IsPackable&gt;false&lt;/IsPackable&gt;

&lt;/PropertyGroup&gt;</code></pre>

          <p>4) Configure Startup.cs<br> As our web-api app will also serve the static files we need to configure the Stratup.cs file as follow:</p>
          <pre><code class="language-html">public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    app.UseDefaultFiles();
    app.UseStaticFiles();

    app.Use(async (context, next) =&gt; {
        await next();
        if (context.Response.StatusCode == 404 &amp;&amp;
            !Path.HasExtension(context.Request.Path.Value) &amp;&amp;
            !context.Request.Path.Value.StartsWith("/api/")) {
                context.Request.Path = "/index.html";
                await next();
            }
    });

    // Configure application for usage as API
    // with deafult route '/api/[Controller]'
    app.UseMvcWithDefaultRoute();

    // Serve static files
    app.UseStaticFiles();
}</code></pre>

          <p>5) Configure angular-cli to output the build inside the wwroot of the web-api app =&gt; <br> edit the .angular-cli.json file and change the OutDir parameter:</p>
          <pre><code class="language-javascript"></code></pre>
          <pre><code class="language-javascript">"apps": [
    {
      "root": "src",
      "outDir": "../web-api/wwwroot",</code></pre>
          <p>Now when running ng build inside the angular-app angular-cli should output the result inside the wwroot folder of the web-api app.</p>
          <p><img src="https://vandiest.biz/img/2017/how-to-design-your-spa-to-work-with-angular-cli-02.png" alt width="1752" height="1000" loading="lazy">6) Modify the angular app to consume data from your web-api<br> Edit the following files under /angular-app/src/app file=&gt;</p>
          <p>App.module.ts:</p>
          <pre><code class="language-javascript">import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppComponent } from './app.component';
import { HttpModule } from '@angular/http';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    HttpModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }</code></pre>
          <p>Modify app.component.ts:</p>
          <pre><code class="language-javascript">import { Component, OnInit } from '@angular/core';
import { Http } from '@angular/http'
@Component({
   selector: 'app-root',
   templateUrl: './app.component.html',
   styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
   constructor(private _httpService: Http) { }
   title = "DotNet Core Angular Example";
   apiValues: string[] = [];
   ngOnInit() {
      this._httpService.get('/api/values').subscribe(values =&gt; {
         this.apiValues = values.json() as string[];
      });
   }
}</code></pre>
          <p>Edit app.component.html and add this as last lines of the html file:</p>
          <pre><code class="language-javascript">  &lt;li&gt;
    &lt;h2&gt;Values from ValuesController&lt;/h2&gt;
    &lt;p *ngFor="let value of apiValues"&gt;{{value}}&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;</code></pre>
          <p>7) Redirect api calls to your DotNet core app</p>
          <p>To be able to redirect api calls from your angular app when running&nbsp; in the webpack dev server you should create a proxy.conf.json file on the root of your angular app.&nbsp; Here my dotnet webapi runs on port 5000:</p>
          <pre><code class="language-javascript">{
    "/api": {
      "target": "http://localhost:5000/",
      "secure": false
    }
}</code></pre>
          <p>You can already test your app:</p>
          <ul>
          <li>launch the web-api app with</li>
          </ul>
          <p>dotnet run</p>
          <ul>
          <li>Launch the angular app and pass the proxy configuration:</li>
          </ul>
          <p>ng serve --proxy-config proxy.conf.json</p>
          <ul>
          <li>Launch Chrome and open http://localhost:4200/</li>
          </ul>
          <p><img src="https://vandiest.biz/img/2017/how-to-design-your-spa-to-work-with-angular-cli-03.png" alt width="1805" height="783" loading="lazy"></p>
          <p>8)&nbsp;To be able to use npm script commands edit the package.json of the angular-app and edit the script section:</p>
          <pre><code class="language-javascript">"scripts": {
    "ng": "ng",
    "start": "ng serve --proxy-config proxy.conf.json",
    "build": "ng build",
    "build-prd": "ng build --prod --env=prod",</code></pre>
          <p>9) To be able to invoke these commands from you web-api, add a package.json inside the root of the web-api app containing.&nbsp; Here we declare the same commands but first cd to the angular-app:</p>
          <pre><code class="language-javascript">{
    "name": "core-angular-example",
    "version": "0.0.0",
    "license": "MIT",
    "scripts": {
        "install": "cd ../angular-app &amp; npm install",
        "start": "cd ../angular-app &amp; npm start",
        "build": "cd ../angular-app &amp; npm run-script build",
        "build-prd": "cd ../angular-app &amp; npm run-script build-prd"
    }
}</code></pre>
          <p>10) Finally, we configure our web-api MsBuild script to invoke the ng build script when running a release build. Therefore, we add the following targets to it:</p>
          <pre><code class="language-xml">&lt;Target Name="EnsureNode"&gt;
  &lt;Exec Command="node --version" ContinueOnError="true"&gt;
    &lt;Output TaskParameter="ExitCode" PropertyName="ErrorCode" /&gt;
  &lt;/Exec&gt;
  &lt;Error Condition="'$(ErrorCode)' != '0'" Text="Node.js is required to build and run this project. To continue, please install Node.js from https://nodejs.org/, and then restart your command prompt or IDE." /&gt;
&lt;/Target&gt;

&lt;Target Name="ReleaseRunNgBuild" BeforeTargets="Build" Condition=" '$(Configuration)' == 'Release' "&gt;
  &lt;CallTarget Targets="EnsureNode" /&gt;
  &lt;Message Importance="high" Text="Install packages..." /&gt;
  &lt;Exec Command="npm install" /&gt;
  &lt;Message Importance="high" Text="Performing ng build for prd build..." /&gt;
  &lt;Exec Command="npm run-script build-prd" /&gt;
&lt;/Target&gt;</code></pre>
          <p>Now when you launch a Release build everything will be build together.&nbsp;</p>
          <p><img src="https://vandiest.biz/img/2017/how-to-design-your-spa-to-work-with-angular-cli-04.png" alt width="1083" height="411" loading="lazy"></p>
          <p>You can get a package containing all your app by using the publish command.</p>
          <p>If you want to publish your site to azure simply use the VisualStudio publish wizard or the Azure Powershell Publish commands.</p>
          <p><img src="https://vandiest.biz/img/2017/how-to-design-your-spa-to-work-with-angular-cli-05.png" alt width="1083" height="603" loading="lazy"></p>]]></description>
    </item>
    <item>
      <title>How to design your SPA to work with Angular CLI and DoNet Core</title>
      <link>https://vandiest.biz/post/2017/09/10/how-to-design-your-spa-to-work-with-angular-cli-and-donet-core/</link>
      <guid isPermaLink="true">https://vandiest.biz/post/2017/09/10/how-to-design-your-spa-to-work-with-angular-cli-and-donet-core/</guid>
      <pubDate>Sun, 10 Sep 2017 08:00:00 GMT</pubDate>
      <category>Angular</category>
      <category>Single-page apps</category>
      <category>ASP.NET</category>
      <description><![CDATA[<p>When building new apps, you need both client and server-side pieces.&nbsp; This article focusses on how to structure a SPA that is made out of angular for the client and dotnet core for the server.</p>
          <p>There are numerous questions I needed to answers when mixing these technologies.</p>
          <ul>
          <li>Should I host my angular app inside an MVC View or just serve the angular app from static files.</li>
          <li>Should I host the angular app inside the same application that serve the Web-Api or serve it from a separate site?</li>
          <li>How to setup a productive development environment?</li>
          <li>How should I integrate angular-cli together with a ASP.NET Core app?</li>
          <li>Should I use Vs.Code or VisualStudio ?</li>
          <li>How to package and deploy the application for on prem or in the cloud?</li>
          </ul>
          <p>Of course, the answer to all these questions is always the same: it depends! After experimenting with several options, I finally decided that I should go for the simplest solution that could possibly work and started with just serving static files inside a Web-Api application.&nbsp; Yet, during development, what I found the most productive, is to completely separate the Angular app from the Web-Api solution. I like that my Angular and the ASP.NET Web-Api apps live separate folders beside each other. It&rsquo;s only at build time that Msbuild assemble the parts by calling &ldquo;ng build&rdquo;. &nbsp;To do that I needed to configured angular-cli to output the build in the \wwwroot folder of the ASP.NET app.</p>
          <p>Concerning the IDE, what I found the most productivity was to develop my angular app using Vs.Code. &nbsp;Thus far, for the Web-Api part I tend to prefer VisualStudio and Resharper. So, I optimized my Angular app for working with Vs.Code and my Web-Api project for VisualStudio.&nbsp; During development I usually run the backend and launch the Webpack dev server through an &ldquo;npm start&rdquo; command.&nbsp; At publish everything is nicely packaged together inside a ASP.NET app, so deploying to Azure can simply be done by using the VisualStudio publish wizard.&nbsp;&nbsp;</p>
          <p>On my github project <a href="https://github.com/geobarteam/core-angular-example">core-angular-example</a>, you can download the solution template I use for making SPA&rsquo;s with Angular-cli and DotNet Core.</p>
          <p>Here I explain the steps you need to follow to setup the basis of such an app:</p>
          <p>0) First you need to install:</p>
          <ul>
          <li><a href="https://code.visualstudio.com/">Code</a></li>
          <li><a href="https://www.microsoft.com/net/core#windowscmd">Donetcore</a></li>
          <li><a href="https://nodejs.org/en/">js</a></li>
          <li><a href="https://github.com/angular/angular-cli#installation">Angular-cl</a>i</li>
          </ul>
          <p>1) From a powershell console:</p>
          <pre><code class="language-powershell">mkdir angular-dotnetcore
cd angular-dotnetcore
mkdir web-api
cd web-api
dotnet new webapi
cd ..
ng new angular-app</code></pre>
          <p>2) Open VisualStudio code and open the angular-dotnetcore folder</p>]]></description>
    </item>
  </channel>
</rss>
