<?xml version="1.0" encoding="UTF-8" standalone="no"?><rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" version="2.0">

<channel>
	<title>Jesse Liberty - Silverlight Geek</title>
	<atom:link href="https://jesseliberty.com/feed/" rel="self" type="application/rss+xml"/>
	<link>https://jesseliberty.com</link>
	<description>More Signal - Less Noise</description>
	<lastBuildDate>Sat, 28 Feb 2026 19:44:16 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.1</generator>
	<item>
		<title>Boston Code Camp</title>
		<link>https://jesseliberty.com/2026/02/28/boston-code-camp/</link>
		
		<dc:creator><![CDATA[Jesse Liberty]]></dc:creator>
		<pubDate>Sat, 28 Feb 2026 19:44:15 +0000</pubDate>
				<category><![CDATA[Essentials]]></category>
		<guid isPermaLink="false">https://jesseliberty.com/?p=13199</guid>

					<description></description>
										<content:encoded><![CDATA[
<figure class="wp-block-image size-full is-resized"><a href="https://bostoncodecamp.com"><img fetchpriority="high" decoding="async" width="748" height="757" src="https://jesseliberty.com/wp-content/uploads/2026/02/Boston-Code-Camp-1.jpg" alt="" class="wp-image-13201" style="aspect-ratio:0.988109229173868;width:370px;height:auto" srcset="https://jesseliberty.com/wp-content/uploads/2026/02/Boston-Code-Camp-1.jpg 748w, https://jesseliberty.com/wp-content/uploads/2026/02/Boston-Code-Camp-1-296x300.jpg 296w, https://jesseliberty.com/wp-content/uploads/2026/02/Boston-Code-Camp-1-148x150.jpg 148w" sizes="(max-width: 748px) 100vw, 748px" /></a></figure>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>HyperVideo – GitHub Issues</title>
		<link>https://jesseliberty.com/2026/02/23/hypervideo-github-issues/</link>
		
		<dc:creator><![CDATA[Jesse Liberty]]></dc:creator>
		<pubDate>Mon, 23 Feb 2026 18:07:12 +0000</pubDate>
				<category><![CDATA[Essentials]]></category>
		<guid isPermaLink="false">https://jesseliberty.com/?p=13187</guid>

					<description><![CDATA[In examining my HyperVideo project I decided that the Bunny video was a great placeholder, but what I really wanted was my own videos. What better place to start than the videos I have up on YouTube. How am I &#8230; <a href="https://jesseliberty.com/2026/02/23/hypervideo-github-issues/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
										<content:encoded><![CDATA[
<p>In examining my HyperVideo project I decided that the Bunny video was a great placeholder, but what I really wanted was <a href="https://youtube.com/jesseliberty">my own videos.</a> What better place to start than the videos I have up on YouTube. How am I going to do that?<br /><br />To get started, I asked CoPilot what it would take. CoPilot generated a plan of action. I took that and opened an issue on GitHub which will document what I have to do. </p>



<span id="more-13187"></span>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>Replace the current HTML5 video player implementation with YouTube embedded player to integrate videos from the <a href="https://github.com/JesseLiberty">@JesseLiberty</a> YouTube channel while maintaining all existing interactive learning features.</p>



<p>Proposed Changes</p>



<ol class="wp-block-list">
<li>Replace HTML5 Video Player with YouTube Embed<br />• Implement YouTube IFrame embed in VideoLearning.razor<br />• Source videos from <a href="https://youtube.com/jesseliberty">https://youtube.com/jesseliberty</a> channel<br />• Replace</li>



<li>Maintain Existing Functionality<br />All current features must remain operational:<br />• <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Pause &amp; Inquire: Pause video and open question panel<br />• <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Interactive Transcript: Clickable, time-synchronized segments<br />• <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Time Tracking: Real-time display of current playback position<br />• <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Seeking: Click transcript to jump to timestamp<br />• <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Search Integration: Wikipedia, Wiktionary, YouTube, Stack Overflow, MDN</li>



<li>Use YouTube IFrame Player API<br />Implement programmatic video control using YouTube IFrame Player API:<br />• pauseVideo() &#8211; Pause playback<br />• playVideo() &#8211; Resume playback<br />• getCurrentTime() &#8211; Get current timestamp<br />• seekTo(seconds) &#8211; Jump to specific time<br />• getDuration() &#8211; Get video length<br />• Event listeners: onStateChange, onReady</li>



<li>Video Selection &amp; Configuration<br />• Video Dropdown: Add UI to select from multiple videos in <a href="https://github.com/JesseLiberty">@JesseLiberty</a> channel<br />• Video Catalog: Create a list/configuration of available educational videos with:<br />• YouTube video ID<br />• Title/description<br />• Associated transcript segments<br />• State Management: Store selected video ID in component state<br />• Configuration Options:<br />• Hardcode initial video list in component<br />• OR store in appsettings.json<br />• OR create a separate service/data file<br />Technical Considerations<br />JavaScript Interop Changes<br />• Load YouTube IFrame API script: <a href="https://www.youtube.com/iframe_api">https://www.youtube.com/iframe_api</a><br />• Create typed JavaScript interop wrapper for YouTube Player API<br />• Replace direct eval() calls with proper YouTube API method invocations<br />• Handle async initialization of YouTube player (onYouTubeIframeAPIReady)<br />Example JS Interop Pattern:</li>
</ol>



<p>// VideoLearning.razor.js<br />export function initializeYouTubePlayer(videoId, dotNetHelper) {<br />const player = new YT.Player(&#8216;player&#8217;, {<br />videoId: videoId,<br />events: {<br />&#8216;onReady&#8217;: onPlayerReady,<br />&#8216;onStateChange&#8217;: onPlayerStateChange<br />}<br />});<br />// Store player reference and expose methods<br />}</p>



<p>export function pauseVideo(player) {<br />player.pauseVideo();<br />}</p>



<p>export function getCurrentTime(player) {<br />return player.getCurrentTime();<br />}</p>



<p>Transcript Handling<br />• Option 1: Manually create transcript segments per video (current approach)<br />• Option 2: Fetch captions using YouTube Data API v3 (requires API key)<br />• Option 3: Store transcripts in separate JSON files mapped to video IDs<br />• Consider caption format: YouTube provides .srt or .vtt formats<br />Render Mode Compatibility<br />• Ensure YouTube IFrame API works with @rendermode InteractiveServer<br />• Test SignalR reconnection behavior with YouTube player state<br />• Verify StateHasChanged() triggers UI updates correctly<br />Files to Modify</p>



<ol class="wp-block-list">
<li>VideoLearning.razor<br />• Replace</li>



<li>Components/Pages/VideoLearning.razor.js (new file)<br />• Create typed JS module for YouTube Player API<br />• Export functions: initPlayer, pauseVideo, playVideo, seekTo, getCurrentTime<br />• Handle player events and callbacks to Blazor</li>



<li>Models/VideoInfo.cs (optional, new file)</li>
</ol>



<p>public record VideoInfo(string VideoId, string Title, List Transcript);</p>



<ol start="4" class="wp-block-list">
<li>appsettings.json (optional)<br />• Store video catalog configuration<br />Implementation Steps</li>



<li>Add YouTube IFrame API script reference to App.razor or VideoLearning.razor</li>



<li>Create VideoLearning.razor.js with YouTube Player API wrapper</li>



<li>Update VideoLearning.razor markup to use YouTube embed</li>



<li>Refactor C# interop calls to use YouTube-specific methods</li>



<li>Add video selection UI and catalog</li>



<li>Test all interactive features (pause/resume, seeking, transcript sync)</li>



<li>Update transcripts for selected YouTube videos from <a href="https://github.com/JesseLiberty">@JesseLiberty</a> channel<br />Success Criteria<br />• <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Videos from <a href="https://github.com/JesseLiberty">@JesseLiberty</a> YouTube channel play in the application<br />• <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Users can switch between multiple videos<br />• <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> All existing interactive features work identically<br />• <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Pause &amp; Inquire functionality remains intact<br />• <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Interactive transcript synchronization works<br />• <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Time tracking displays correctly<br />• <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Search integration functions as before<br />• <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> No degradation in user experience<br />Resources<br />• YouTube IFrame Player API Reference<br />• YouTube Data API v3 &#8211; Captions (for transcript retrieval)<br />• Blazor JavaScript Interop</li>
</ol>



<p>Labels: enhancement, feature, blazor, javascript-interop<br />Priority: High</p>
</blockquote>



<p>Those look like a great set of sub-tasks. I&#8217;m not sure how to attack them, but breaking them down will greatly simplify matters.</p>



<p>Here&#8217;s a first attempt at some of the sub-issues arising out of the work&#8230;</p>



<figure class="wp-block-image size-large"><img decoding="async" width="800" height="410" src="https://jesseliberty.com/wp-content/uploads/2026/02/image-800x410.png" alt="" class="wp-image-13188" srcset="https://jesseliberty.com/wp-content/uploads/2026/02/image-800x410.png 800w, https://jesseliberty.com/wp-content/uploads/2026/02/image-300x154.png 300w, https://jesseliberty.com/wp-content/uploads/2026/02/image-150x77.png 150w, https://jesseliberty.com/wp-content/uploads/2026/02/image-768x394.png 768w, https://jesseliberty.com/wp-content/uploads/2026/02/image.png 1150w" sizes="(max-width: 800px) 100vw, 800px" /></figure>



<p>You can see that this is far from perfect. Some of these are small and some are huge. It will be necessary to flesh out some and to break others down to smaller tasks, but this is a good start. The trick is not to get overwhelmed. OK, I&#8217;m overwhelmed. But my buddy CoPilot and I are ready to tackle this.</p>



<p></p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>copilot-instructions.md</title>
		<link>https://jesseliberty.com/2026/02/22/copilot-instructions-md/</link>
		
		<dc:creator><![CDATA[Jesse Liberty]]></dc:creator>
		<pubDate>Sun, 22 Feb 2026 20:18:02 +0000</pubDate>
				<category><![CDATA[Essentials]]></category>
		<guid isPermaLink="false">https://jesseliberty.com/?p=13184</guid>

					<description><![CDATA[When working with CoPilot in Visual Studio, the single most important thing is context. Context tells CoPilot what it is working on, what it should already know, what language conventions to use, etc., etc. CoPilot wants to help, but its &#8230; <a href="https://jesseliberty.com/2026/02/22/copilot-instructions-md/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
										<content:encoded><![CDATA[
<p>When working with CoPilot in Visual Studio, the single most important thing is <em>context</em>. Context tells CoPilot what it is working on, what it should already know, what language conventions to use, etc., etc. CoPilot wants to help, but its memory is not the best, and like the character in <a href="https://en.wikipedia.org/wiki/Memento_(film)">Memento</a>, the trick is to write everything down and remind it all the time&#8230; hence context.</p>



<p>The best way to get ahead of this is to create a copilot-instructions.md file in your .github directory (which you may need to create). In this file, you&#8217;ll want to put anything that CoPilot should know right from the get-go and for every session. </p>



<p>But how do you know what to put in there?</p>



<span id="more-13184"></span>



<p>Here&#8217;s the trick&#8230; let CoPilot build it for you! I cribbed a set of instructions from the &#8216;net that tells CoPilot how to create the file based on the existing code in my project. I tweaked it just a bit, as did a buddy (Teja) and all you need to do is copy it into the CoPilot window in Visual Studio, let CoPilot cook, and hey! presto! you have just what you want: a fully developed copilot-instructions.md file in just the right place. Even better, if you already have one, this will merge cleanly.</p>



<p>Here&#8217;s the instructions I give CoPilot:<br /><br /></p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>Analyze this codebase to generate or update .github/<a href="http://copilot-instructions.md">copilot-instructions.md</a> for guiding AI coding agents. Focus on discovering the essential knowledge that would help an AI agent be immediately productive in this codebase. Consider aspects like: The &#8220;big picture&#8221; architecture that requires reading multiple files to understand &#8211; major components, service boundaries, data flows, and the &#8220;why&#8221; behind structural decisions Critical developer workflows (builds, tests, debugging) especially commands that aren&#8217;t obvious from file inspection alone Project-specific conventions and patterns that differ from common practices Integration points, external dependencies, and cross-component communication patterns  Guidelines (read more at <a href="https://aka.ms/vscode-instructions-docs">https://aka.ms/vscode-instructions-docs</a>): <br /><br />If .github/<a href="http://copilot-instructions.md">copilot-instructions.md</a> exists, merge intelligently &#8211; preserve valuable content while updating outdated sections Write concise, actionable instructions (~20-50 lines) using markdown structure Include specific examples from the codebase when describing patterns Avoid generic advice (&#8220;write tests&#8221;, &#8220;handle errors&#8221;) &#8211; focus on THIS project&#8217;s specific approaches Document only discoverable patterns, not aspirational practices Maintaining a history or features listing with a detailed description of all the information of the project into a <a href="http://README.md">README.md</a> file whenever there is a new change. Update the file accordingly with a brief summary. Before each task implementation, go through the <a href="http://README.md">README.md</a> file, then start implementing, understanding the whole picture of the requirements. Reference key files/directories that exemplify important patterns Update .github/<a href="http://copilot-instructions.md">copilot-instructions.md</a> for the user, then ask for feedback on any unclear or incomplete sections to iterate.</p>



<p>When writing unit tests use xunit and moq</p>
</blockquote>



<p>And here is the markdown file that CoPilot generated for me (put this into a file and open it in VS to see it displayed nicely):</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<pre class="wp-block-preformatted"><strong># GitHub Copilot Instructions for HyperVideo</strong>

HyperVideo is an interactive video learning platform built with <strong>**.NET 10**</strong> and <strong>**Blazor Server**</strong>. Users can pause videos, ask questions, and search multiple knowledge sources for learning resources.

<strong>## Project Structure</strong>

```
HyperVideo/
├── Components/
│   ├── App.razor               # Root component (HTML shell, Bootstrap, Blazor script)
│   ├── Routes.razor            # Router with 404 fallback and default layout
│   ├── _Imports.razor          # Global using directives for all components
│   ├── Layout/
│   │   ├── MainLayout.razor    # Two-column layout: sidebar NavMenu + main content area
│   │   ├── NavMenu.razor       # Navigation links (Home, Video Learning, Counter, Weather)
│   │   └── ReconnectModal.razor # SignalR reconnection overlay
│   └── Pages/
│       ├── Home.razor          # Landing page with link to video learning
│       ├── VideoLearning.razor # Core feature: interactive video player with inquiry panel
│       ├── Counter.razor       # Simple stateful counter demo
│       ├── Weather.razor       # Async data fetch demo with streaming render
│       ├── Error.razor         # Error boundary page
│       └── NotFound.razor      # 404 page
├── wwwroot/
│   ├── app.css                 # Global styles
│   └── lib/                   # Bootstrap 5 CSS/JS
├── Program.cs                  # App startup and middleware pipeline
├── appsettings.json
└── HyperVideo.csproj
```

<strong>## Technology Stack</strong>

- <strong>**Runtime**</strong>: .NET 10 (`net10.0`)
- <strong>**UI**</strong>: Blazor Server with Interactive Server rendering (`@rendermode InteractiveServer`)
- <strong>**Real-time**</strong>: ASP.NET Core SignalR (used implicitly by Blazor Server)
- <strong>**CSS**</strong>: Bootstrap 5, Bootstrap Icons (`bi-*`), component-scoped CSS files (`.razor.css`)
- <strong>**JS Interop**</strong>: `IJSRuntime` for controlling the HTML5 `&lt;video&gt;` element
- <strong>**Nullable**</strong>: enabled; <strong>**ImplicitUsings**</strong>: enabled

<strong>## Coding Conventions</strong>

<strong>### Blazor Components</strong>
- Place all pages under `Components/Pages/` with `@page "/route"` directives.
- Place shared layout under `Components/Layout/`.
- Use `@rendermode InteractiveServer` on pages that need interactivity (event handlers, JS interop).
- Component-scoped CSS goes in a sibling file named `ComponentName.razor.css`.
- Use `@code { }` blocks at the bottom of `.razor` files for C# logic.

<strong>### C# Style</strong>
- Follow standard C# conventions: PascalCase for types and methods, camelCase for private fields.
- Prefer `record` types for immutable data (e.g., `TranscriptSegment`).
- Use nullable reference types; annotate `string?` where null is expected.
- Use `string.Empty` instead of `""` for empty-string defaults.
- Prefer `async Task` over `async void` for event handlers.

<strong>### JavaScript Interop</strong>
- Inject `IJSRuntime` with `@inject IJSRuntime JS`.
- Prefer `JS.InvokeVoidAsync` for fire-and-forget calls and `JS.InvokeAsync&lt;T&gt;` when a return value is needed.
- Avoid `eval()` for new interop code; instead, expose named JavaScript functions in `wwwroot/` and call them by name.
- Always wrap JS interop calls in `try/catch` because the circuit may not be ready.

<strong>### State Management</strong>
- Use private fields and `StateHasChanged()` for local component state.
- Keep models close to where they are used; only extract to a separate file when shared across multiple components.

<strong>### Error Handling</strong>
- Wrap async event handlers in `try/catch` and log errors with `Console.WriteLine` or an injected `ILogger&lt;T&gt;`.
- Use the `Error.razor` page as the global error boundary (already wired via `&lt;ErrorBoundary&gt;`).

<strong>## Key Feature: VideoLearning.razor</strong>

`VideoLearning.razor` is the core component. Its responsibilities:

| Concern | Implementation |
|---|---|
| Video playback | HTML5 `&lt;video&gt;` element; JS interop to call `.pause()`, `.play()`, seek |
| Pause &amp; Inquire | Sets `isPaused = true`, shows inquiry panel with timestamp |
| Question search | Builds URL links to Wikipedia, Wiktionary, YouTube, Stack Overflow, MDN |
| Transcript | `List&lt;TranscriptSegment&gt;` displayed as clickable rows; active row highlighted |
| Time tracking | `@ontimeupdate` reads `currentTime` via JS interop |

When extending this component:
- Add new search sources by appending to `searchResults` inside `SearchForAnswer()`.
- Add transcript segments by adding `new TranscriptSegment(start, end, "text")` entries.
- Use `ElementReference` + `JS.InvokeVoidAsync("blazorHelpers.seekVideo", videoElement, time)` style helpers rather than bare `eval()`.

<strong>## Common Patterns</strong>

<strong>### Adding a New Page</strong>
```razor
@page "/my-page"
@rendermode InteractiveServer

&lt;PageTitle&gt;My Page&lt;/PageTitle&gt;

&lt;!-- markup --&gt;

@code {
    // component logic
}
```
Then add a `&lt;NavLink&gt;` entry in `NavMenu.razor`.

<strong>### Injecting a Service</strong>
Register in `Program.cs`:
```csharp
builder.Services.AddScoped&lt;IMyService, MyService&gt;();
```
Inject in component:
```razor
@inject IMyService MyService
```

<strong>### JS Interop Helper Pattern</strong>
Add a function to `wwwroot/app.js` (create if it does not exist):
```javascript
window.blazorHelpers = {
    seekVideo: (element, time) =&gt; { element.currentTime = time; }
};
```
Call from C#:
```csharp
await JS.InvokeVoidAsync("blazorHelpers.seekVideo", videoElement, time);
```

<strong>## Build &amp; Run</strong>

```bash
# Restore and run
 dotnet run --project HyperVideo/HyperVideo.csproj

# Build only
dotnet build HyperVideo/HyperVideo.csproj
```

Default URLs: `http://localhost:5092` / `https://localhost:7288`

<strong>## What to Avoid</strong>

- Do <strong>**not**</strong> use `@rendermode InteractiveWebAssembly` — this project is Blazor Server only.
- Do <strong>**not**</strong> add NuGet packages without checking compatibility with `net10.0`.
- Do <strong>**not**</strong> use bare `eval()` strings for new JS interop; prefer named helper functions.
- Do <strong>**not**</strong> use `async void` event handlers; use `async Task` and handle exceptions.
- Do <strong>**not**</strong> store secrets in `appsettings.json`; use environment variables or the .NET Secret Manager.
</pre>
</blockquote>



<p>This is based on the skeleton program written in the past couple of postings, so it didn&#8217;t have all that much to go on. Nonetheless, it created a great context file with very specific instructions for CoPilot that will create a consistent interface and programming approach.</p>



<p>More to come as we develop HyperVideo but this will put us on the right track straight away.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Python??</title>
		<link>https://jesseliberty.com/2026/02/20/python/</link>
		
		<dc:creator><![CDATA[Jesse Liberty]]></dc:creator>
		<pubDate>Fri, 20 Feb 2026 11:51:54 +0000</pubDate>
				<category><![CDATA[Essentials]]></category>
		<guid isPermaLink="false">https://jesseliberty.com/?p=13180</guid>

					<description><![CDATA[This series on AI is going to take two paths: If you are writing apps, you have a couple choices (well, more than a couple). One choice is to stay in the Azure/.NET world, in which case C# is just &#8230; <a href="https://jesseliberty.com/2026/02/20/python/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
										<content:encoded><![CDATA[
<p>This series on AI is going to take two paths:</p>



<ul class="wp-block-list">
<li>Writing prompts</li>



<li>Writing apps that integrate AI (see <a href="https://jesseliberty.com/2026/01/20/an-ai-project/">Hypervideo</a>)</li>
</ul>



<p>If you are writing apps, you have a couple choices (well, more than a couple). One choice is to stay in the Azure/.NET world, in which case C# is just fine. The other (set of) choice(s) is to work in the rest of AI development, in which case Python is the language of choice.</p>



<p>I&#8217;m taking a course on agentics from Johns Hopkins University, and that course is in Python. It turns out that learning Python is pretty easy if you are a C# programmer, and it is even easier if you are a C# programmer with access to CoPilot and/or ChatGPT for Python. </p>



<p>I resisted learning Python all these years, but I&#8217;ve come to believe that if you are serious about AI then Python is in your future. So, might as well bite the bullet and get started now.</p>



<span id="more-13180"></span>



<h2 class="wp-block-heading">It&#8217;s Just Syntax</h2>



<p>All the concepts are the same (more or less). There are variables, loops, dictionaries, tuples and so forth. Some of the syntax is different (no braces) and some of the rules vary a bit (tuples are immutable), but it won&#8217;t take you long to learn. Let&#8217;s start with the basics.</p>



<p><strong>DataTypes</strong>: Python has only three numeric datatypes: int, float and complex. It also has str (string) and bool. One thing to watch for is truthy v true. True requires a bool but truthy is anything that evaluates to true. Huh? If a variable is of type bool, it can only. have one of two values: true or false. But an expression can evaluate to true (e.g., 5+7 = 12) which is truthy.</p>



<p>There are also collections. some are ordered: list (which is mutable), tuple (which is not mutable) and range (which is an immutable arithmetic progression). In addition, there is dict &#8212; a dictionary which works much like a dictionary in c#</p>



<p>If you need a dictionary with unique values, there is the set, which comes in two flavors: set (mutable) and frozenset (immutable)</p>



<p>Finally, None does the work of void</p>



<p>According to ChatGPT the core types you&#8217;ll use 90% of the time are:</p>



<ul class="wp-block-list">
<li>int</li>



<li>float</li>



<li>str</li>



<li>bool</li>



<li>list</li>



<li>dict</li>



<li>tuple</li>



<li>set</li>



<li>none</li>
</ul>



<p>which pretty much corresponds to the big types in c# (except, perhaps set).</p>



<p><strong>Indentation</strong> One big thing for C# programmers to watch out for is indentation. Python does not use braces in if statements, functions, etc.; it uses indentation.</p>



<pre class="wp-block-code"><code>if (myAge > 5)
   print("You are a big boy")
else
   print("You still have some growing to do")
   print("But don't worry, we all went through it")
print("this line is not in the if/else")</code></pre>



<p>Notice that the last print statement is not in the if/else construct. Oh, there is also an elif statement so that you can have if, elif, elif, else.</p>



<p><strong>Function definition</strong> You define a function with the keyword <strong>def</strong> and you use indentation to set the contents of the function:</p>



<pre class="wp-block-code"><code>def greet(name):
    print("Hello")
    print(name)
</code></pre>



<p> Some quick rules on indentation:</p>



<ul class="wp-block-list">
<li>Typically you indent 4 spaces (but that is not really a rule)</li>



<li>All indentation must be the same amount</li>



<li>You cannot mix tabs and spaces</li>
</ul>



<p>Don&#8217;t forget to nest your indentation</p>



<pre class="wp-block-code"><code>def check_age(age):
    if age >= 21:
        print("Can drink")
    else:
        print("Cannot drink")
</code></pre>



<p><strong>Lists</strong> Lists are created with square brackets</p>



<pre class="wp-block-code"><code>numbers = &#91;10, 20, 30, 40]</code></pre>



<p>Notice you do not put the type. You never declare the type, it is inferred. (this is true for all declarations, not just collections)</p>



<p>You iterate over lists with a very similar syntax to C#&#8217;s foreach</p>



<pre class="wp-block-code"><code>for number in numbers:
   print(number)</code></pre>



<p>Don&#8217;t forget the colon!</p>



<p><strong>Dictionaries</strong> Dictionaries are similar to lists, but you declare them with curly braces. You iterate over them using the key and the value:</p>



<pre class="wp-block-code"><code>students = {
    1001: "Alice",
    1002: "Bob"
}

for student_id, name in students.items():
    print(student_id, name)
</code></pre>



<p>This will print</p>



<pre class="wp-block-code"><code>1001 Alice
1002 Bob</code></pre>



<p>You can access both with .items() or the keys with .keys() or the values with .values()</p>



<p><strong>Tuples</strong> Tuples can be declared with or without parentheses</p>



<pre class="wp-block-code"><code>coordinates = (15, 20)
colors = ("red", "greeen", "blue", "yellow")
point = 5,10</code></pre>



<p>You can iterate over a tuple</p>



<pre class="wp-block-code"><code>for color in colors:
   print(color)</code></pre>



<p>A common thing to do with tuples is called unpacking. For example, using the point from above, you can write</p>



<pre class="wp-block-code"><code>point = (10, 20)

x, y = point
print(x)  # 10
print(y)  # 20</code></pre>



<p>Or you can use this in loops, which is a common construct</p>



<pre class="wp-block-code"><code>pairs = ((1, "A"), (2, "B"), (3, "C"))

for number, letter in pairs:
    print(number, letter)</code></pre>



<p>That&#8217;s plenty to get you started with Python. Of course there are more advanced constructs, and we&#8217;ll tackle them as we go, but these are the fundamentals.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>HyperVideo Features</title>
		<link>https://jesseliberty.com/2026/01/24/hypervideo-features/</link>
		
		<dc:creator><![CDATA[Jesse Liberty]]></dc:creator>
		<pubDate>Sat, 24 Jan 2026 18:32:56 +0000</pubDate>
				<category><![CDATA[AI]]></category>
		<guid isPermaLink="false">https://jesseliberty.com/?p=13175</guid>

					<description><![CDATA[In a previous post, I laid out the basic idea of an AI demonstration project I call HyperVideo. In this short post, I&#8217;d like to review some of the features I imagine for this project. Some will be implemented in &#8230; <a href="https://jesseliberty.com/2026/01/24/hypervideo-features/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
										<content:encoded><![CDATA[
<p>In <a href="https://jesseliberty.com/2026/01/20/an-ai-project/">a previous post,</a> I laid out the basic idea of an AI demonstration project I call HyperVideo. In this short post, I&#8217;d like to review some of the features I imagine for this project. Some will be implemented in the MVP, some after the initial &#8220;release,&#8221; and some are simply aspirational.</p>



<span id="more-13175"></span>



<ol class="wp-block-list">
<li>Run the video and show a transcript in real time</li>



<li>Pause the video and type in any question on related or other topics (first cut)</li>



<li>Add speech recognition</li>



<li>Pause the video and be offered questions related to what was just said in the video</li>



<li>Ask a question and see a (hyper) video in response (a button appears to take you back one video)</li>



<li>Add text-to-speech to answer questions</li>



<li>Add history of previous questions to serve as context for future questions</li>



<li>Add quizzes at the end of the video</li>



<li>Add ability to scroll back in the transcript and be offered questions about the selected time slice or hypervideo link from there</li>



<li>Add hyperlinks to the transcript. Following the link potentially brings up new videos</li>
</ol>



<p>More to follow, I&#8217;m sure, but that&#8217;s a pretty good starting list.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Essential Programmer Utilities</title>
		<link>https://jesseliberty.com/2026/01/22/essential-programmer-utilities/</link>
		
		<dc:creator><![CDATA[Jesse Liberty]]></dc:creator>
		<pubDate>Thu, 22 Jan 2026 11:10:47 +0000</pubDate>
				<category><![CDATA[Essentials]]></category>
		<guid isPermaLink="false">https://jesseliberty.com/?p=13170</guid>

					<description><![CDATA[Every two years or so I publish a list of the utilities I find indispensable. It is interesting to me which ones last year after year (e.g., Evernote) and which fall off the list—often because the functionality I want has &#8230; <a href="https://jesseliberty.com/2026/01/22/essential-programmer-utilities/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
										<content:encoded><![CDATA[
<p>Every two years or so I publish a list of the utilities I find indispensable. It is interesting to me which ones last year after year (e.g., Evernote) and which fall off the list—often because the functionality I want has been absorbed into Microsoft software I&#8217;m already using.</p>



<p>Here is 2026&#8217;s list&#8230;</p>



<span id="more-13170"></span>



<p><em>Lists are always subjective, but it is helpful, I think, to exchange favorites now and again. Here’s my list <strong>in no particular order</strong></em></p>



<ol class="wp-block-list">
<li><a href="https://visualstudio.microsoft.com/">Visual Studio 202</a>6 – goes without saying that this is the world’s best IDE for .NET developers. I use it 8-12 hours a day; it just keeps getting better, especially with CoPilot.</li>



<li><a href="https://github.com/features/copilot">Copilot</a> – AI done right. Integrated with Visual Studio, this add-on is the most powerful and useful tool I have. It can get in the way a little, but when it does its stuff, watch out. Point it at a class and ask for documentation and/or unit tests and watch it go! More powerful is CoPilot Agents which will cook up substantial amounts of excellent code. But kids, don’t try this at home—it needs adult supervision.</li>



<li><a href="https://www.prestosoft.com/edp_examdiffpro.asp?flm=1">ExamDiffPro</a> – there are lots of comparison/merge utilities. Get a good one.</li>



<li><a href="https://marketplace.visualstudio.com/items?itemName=MadsKristensen.Tweaks">Tweaks </a>by Mads Kristensen — small but very valuable addition to VS</li>



<li><a href="https://evernote.com/">Evernote</a>: I use it to log everything I do, including every fix for recurring issues. It is quick, powerful, and easy to use. Although it has limitations, overall, I’ve found it helpful and reliable and it now holds thousands of notes for me. It does have a new transcription ability, but I find it somewhat limited (no summary, etc.) for that I use&#8230;</li>



<li>TalkNotes. This incredibly flexible iOS utility does just what it says: it takes notes and then will produce those notes in any number of ways, including custom reports. I use only a small portion of its potential, but they are critical to my workflow. (1) I use it to summarize meetings and tell me the action items, and (2) I dictate to it as I work so that I can ask, &#8220;What did I do 5 minutes ago that made this all break?&#8221; Note: this application type is exploding and there are many great competitors. One I have my eye on is Wave, which offers the ability to make inquiries about your meeting and which uses generative AI to provide meaningful answers.</li>



<li><a href="https://wisprflow.ai/">WhisprFlow</a> &#8211; by far the best dictation software I&#8217;ve seen. Only caveat is that this is for relatively brief dictation &#8211; enough to send a message, not enough to replace TalkNotes or Wave. On the other hand, WhisprFlow is available on iOS, Android, Mac, and Windows and works well on all (I don&#8217;t have Android, so can&#8217;t swear to that). </li>



<li><a href="https://www.roboform.com/">Roboform</a> my password manager. I’d be lost without it. There are many, but this one is extremely straightforward and has been reliable for a very long time.</li>



<li><a href="https://bsky.app/profile/jesseliberty.com">BlueSky</a>—the single best social media site for .NET programmers</li>



<li>A note about Resharper. Many C# developers find it indispensable. Probably because I never became really proficient with it, and because VS has so many refactorings built in, I do not have it on my computer. But you may want to.</li>
</ol>



<p>Note that nearly all of these are free. I do remember the days when a program like Lotus 123 cost something in the hundreds of dollars. It makes me laugh when I see people look at an app and say, “$4, naah, too expensive.” On the other hand, watch out for the AI apps, they can be over $100 / year.</p>



<p><em>I am not affiliated with any of these.</em></p>



<p></p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>CoPilot Gets Us Started</title>
		<link>https://jesseliberty.com/2026/01/21/copilot-gets-us-started/</link>
		
		<dc:creator><![CDATA[Jesse Liberty]]></dc:creator>
		<pubDate>Wed, 21 Jan 2026 12:11:53 +0000</pubDate>
				<category><![CDATA[AI]]></category>
		<guid isPermaLink="false">https://jesseliberty.com/?p=13167</guid>

					<description><![CDATA[In yesterday&#8217;s post I described the project I want to build: HyperVideo. This morning I created a new Blazor application and the first thing I did was to open CoPilot and give it a prompt. Specifically, I asked it to &#8230; <a href="https://jesseliberty.com/2026/01/21/copilot-gets-us-started/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
										<content:encoded><![CDATA[
<p>In <a href="https://jesseliberty.com/2026/01/20/an-ai-project/">yesterday&#8217;s post</a> I described the project I want to build: HyperVideo. This morning I created a new Blazor application and the first thing I did was to open CoPilot and give it a prompt. Specifically, I asked it to build what I described yesterday.</p>



<span id="more-13167"></span>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>Create an application with a rectangle in which a video is playing (typically news or an instructional video, but it can be anything). Next to the video is a transcript. Under the video are a few buttons. One pauses the video and lets you inquire about something that was just said (or anything that comes to mind for that matter). The video player now takes you to Wikipedia, Wiktionary and any other public source that might best answer your question. It may even begin a related video. A second button resumes the video. There is a button to jump back 15 seconds and another to jump ahead 30 seconds.</p>
</blockquote>



<p>CoPilot went to work. Literally five minutes later I had the skeleton of a working program</p>



<figure class="wp-block-image size-large"><img decoding="async" width="800" height="462" src="https://jesseliberty.com/wp-content/uploads/2026/01/image-800x462.png" alt="" class="wp-image-13168" srcset="https://jesseliberty.com/wp-content/uploads/2026/01/image-800x462.png 800w, https://jesseliberty.com/wp-content/uploads/2026/01/image-300x173.png 300w, https://jesseliberty.com/wp-content/uploads/2026/01/image-150x87.png 150w, https://jesseliberty.com/wp-content/uploads/2026/01/image-768x444.png 768w, https://jesseliberty.com/wp-content/uploads/2026/01/image-1536x887.png 1536w, https://jesseliberty.com/wp-content/uploads/2026/01/image-2048x1183.png 2048w" sizes="(max-width: 800px) 100vw, 800px" /></figure>



<p>With that in place, I have a framework to add the AI features. In fact, CoPilot even prompted me on which features I need. Here&#8217;s what CoPilot says it implemented:<br /></p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p><strong>What I&#8217;ve Created:</strong></p>



<p>Optional: Add Azure OpenAI for intelligent Q&amp;A response<br /></p>



<p>VideoLearning.razor &#8211; Main component with<br />• HTML5 video player with all requested controls<br />• Interactive transcript with timestamp navigation<br />• Pause &amp; Inquire feature with multi-source search<br />• Jump back 15s and forward 30s buttons<br />• Resume functionality</p>



<p>VideoLearning.razor.css &#8211; Comprehensive styling with:<br />• Responsive grid layout<br />• Professional card designs<br />• Smooth animations<br />• Mobile-friendly design<br />• Scroll customization</p>



<p>NavMenu.razor snippet &#8211; Navigation integration</p>



<p>README-VideoLearning.md &#8211; Complete setup and usage guide</p>



<p><br /><strong>Next Steps:</strong></p>



<p>Add your video: Place a video file in wwwroot/videos/sample-video.mp4</p>



<p>Customize transcript: Replace sample data with your actual transcript</p>



<p>Optional: Integrate with Wikipedia/YouTube APIs for richer search results</p>



<p></p>
</blockquote>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>An AI Project</title>
		<link>https://jesseliberty.com/2026/01/20/an-ai-project/</link>
		
		<dc:creator><![CDATA[Jesse Liberty]]></dc:creator>
		<pubDate>Tue, 20 Jan 2026 21:44:52 +0000</pubDate>
				<category><![CDATA[AI]]></category>
		<category><![CDATA[Essentials]]></category>
		<guid isPermaLink="false">https://jesseliberty.com/?p=13165</guid>

					<description><![CDATA[As part of my work of rapidly coming up to speed with creating AI applications, I&#8217;ve decided to start an ambitious project, and to bring you with me. I call it HyperVideo. Picture a rectangle in which a video is &#8230; <a href="https://jesseliberty.com/2026/01/20/an-ai-project/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
										<content:encoded><![CDATA[
<p>As part of my work of rapidly coming up to speed with creating AI applications, I&#8217;ve decided to start an ambitious project, and to bring you with me. I call it <em>HyperVideo</em>.</p>



<span id="more-13165"></span>



<p>Picture a rectangle in which a video is playing (typically news or an instructional video, but it can be anything). Next to the video is a transcript. Under the video are a few buttons. One pauses the video and lets you inquire about something that was just said (or anything that comes to mind for that matter). The video player now takes you to Wikipedia, Wiktionary and any other public source that might best answer your question. It may even begin a related video (hence the name HyperVideo). </p>



<p>This touches on a number of the more interesting aspects of Azure AI, among them speech to text, generative AI and I&#8217;ll probably build it out to handle some additional functionality as I go (translation to other languages? text to speech?)</p>



<p>I expect this to be developed with a timeline like a hockey stick. That is, slow going at first, and then rapid development as I learn more and as the project comes along.</p>



<p>First step: ask CoPilot to take a crack at it, using Blazor as the front end and some combination of SQL Server and Blob storage as the back end. SQL Server may be overkill, I&#8217;ll look at some lighter weight alternatives as well.</p>



<p>I&#8217;ll keep you up to date as I go&#8230;</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Learning AI</title>
		<link>https://jesseliberty.com/2026/01/04/learning-ai/</link>
		
		<dc:creator><![CDATA[Jesse Liberty]]></dc:creator>
		<pubDate>Sun, 04 Jan 2026 17:01:44 +0000</pubDate>
				<category><![CDATA[AI]]></category>
		<category><![CDATA[ChatGPT]]></category>
		<category><![CDATA[Essentials]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Vibe Coding]]></category>
		<category><![CDATA[Certification]]></category>
		<guid isPermaLink="false">https://jesseliberty.com/?p=13144</guid>

					<description><![CDATA[Very excited to say that I have purchased 3-4 books in preparation for the Azure AI 900 certification from Microsoft. I will be creating another series here, distilling down what I learn, even while we keep the API series going. &#8230; <a href="https://jesseliberty.com/2026/01/04/learning-ai/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
										<content:encoded><![CDATA[
<p>Very excited to say that I have purchased 3-4 books in preparation for the <em>Azure AI 900 certification</em> from Microsoft. I will be creating another series here, distilling down what I learn, even while we keep the API series going.</p>



<p>This is the first certification I&#8217;ve ever tried for&#8230; no that&#8217;s a lie, I was a certified Xamarin developer. But beside that one, this is the first. I&#8217;ve never been sure that they are worthwhile (as an autodidact), but AI is so new and changing so fast and so bloody interesting that I couldn&#8217;t resist. </p>



<p>I&#8217;ll begin with the obvious question: which AI engine? The answer to that was easy; I&#8217;ve spent the past three decades working in what is now .NET, and I worked for Microsoft, and I currently work in an all-Microsoft shop. So Azure it is.</p>



<span id="more-13144"></span>



<h2 class="wp-block-heading">The Bits and Pieces</h2>



<p>Azure offers 7 major AI services:</p>



<ul class="wp-block-list">
<li>AI Search</li>



<li>Document Intelligence</li>



<li>Azure OpenAI Service</li>



<li>Vision</li>



<li>Speech</li>



<li>Language</li>
</ul>



<p>Over the course of these blog posts, I hope to cover Large Language Models (LLMs) and the following AI sub-topics:</p>



<ul class="wp-block-list">
<li>Retrieval-Augmented Generation (RAG)</li>



<li>Prompt Engineering</li>



<li>Natural Language Processing (NLP)</li>
</ul>



<h2 class="wp-block-heading">Generative AI</h2>



<p>Generative AI is what all the excitement is about. Generative AI can create new content that it has never seen before—even content that has never <em>existed</em> before. Show it a bunch of books (including <a href="https://secure.anthropiccopyrightsettlement.com/lookup">29 of mine,</a> thank you very much, Anthropic!) and after a while it can be an intelligent CoPilot<img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2122.png" alt="™" class="wp-smiley" style="height: 1em; max-height: 1em;" /> for your work.</p>



<p>Programming with Generative AI, and especially Agentic Generative AI, is an incredible experience. It allows you to articulate what you want, and then it goes off and does it for you. Specifically, you can say something like (from the <a href="https://jesseliberty.com/2025/12/25/creating-the-project-with-copilot/">previous</a> blog post):<br /><br /><em>Create an ASP.net API with a sqlserver* backend. SQLServer will have two tables: bookList and Authors, with the booklist id as a foreign key in Authors. Unit tests using xUnit and Moq. Set up Azurite to provide a message queue, have the GET endpoint create a durable function to listen to the message queue, and have POST use an Azure function to add records to the database. Create service classes for the logic and Repository classes for interacting with the Database</em></p>



<p>and it does. Fast. Incredibly, the code is usually good. I&#8217;d say it gets you about 80% of the way there, which is pretty fantastic. You can even tell it general design requirements (even coding styles, etc.) with a copilot-instructions.md file that you put into the .github directory.</p>



<p>The more <em>context </em>you can provide the better, where context includes the assumptions you make such as which unit testing framework to use, what you&#8217;ll be doing with this code, how it relates to other code CoPilot knows about, etc. </p>



<p>I&#8217;ll come back to Generative AI in future blog posts. I&#8217;ll also touch on ASI &#8212; Artificial SuperIntelligence, and I&#8217;ll point you to the book <em>If Anyone Builds It, Everyone Dies</em>. Which is an amazing title and a book that will scare your pants off.</p>



<p>Finally, I should mention that AI is the foundational technology for <em>Vibe Coding</em>. For more on what this is, see <a href="https://www.youtube.com/watch?v=CYd4FXd7EsE&amp;pp=0gcJCU0KAYcqIYzv">this video</a> or <a href="https://jesseliberty.com/2025/10/10/james-montemagno-on-vibe-coding/">this podcast</a> of the video sound track.  You may also want to check out Jeff Blankenburg&#8217;s series of blog posts, <em>31 Days of Vibe Coding</em> which starts <a href="https://buttondown.com/jeffblankenburg/archive/31-days-of-vibe-coding-day-1/">here</a>.</p>



<p>More to come in the next post in this series.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p>* So, is it &#8220;a SQL&#8221; or &#8220;an SQL&#8221;? Since I pronounce it SeeQuill, I&#8217;ll go with &#8220;a SQL.&#8221;</p>



<p></p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Azure Functions &amp; Durable Functions</title>
		<link>https://jesseliberty.com/2025/12/25/creating-the-project-with-copilot/</link>
		
		<dc:creator><![CDATA[Jesse Liberty]]></dc:creator>
		<pubDate>Thu, 25 Dec 2025 14:35:51 +0000</pubDate>
				<category><![CDATA[AI]]></category>
		<category><![CDATA[API]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[CoPilot]]></category>
		<category><![CDATA[Durable Functions]]></category>
		<category><![CDATA[Essentials]]></category>
		<category><![CDATA[Learning]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Bookstore]]></category>
		<guid isPermaLink="false">https://jesseliberty.com/?p=13071</guid>

					<description><![CDATA[Creating the Project with CoPilot As part of the API tutorials we decided we needed a Database with two tables: Book and Author. Books will join Author and there can be as many authors as we want. Rather than writing &#8230; <a href="https://jesseliberty.com/2025/12/25/creating-the-project-with-copilot/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading">Creating the Project with CoPilot</h2>



<p class="has-blue-color has-text-color has-link-color wp-elements-96dc160d694108e3639b356fad66cfc5">As part of the API tutorials we decided we needed a Database with two tables: Book and Author. Books will join Author and there can be as many authors as we want. Rather than writing this however, I went on GitHub and used the agents.</p>



<p class="has-blue-color has-text-color has-link-color wp-elements-447b185642cf1b9926651d80bd56db5b">I prompted:<br /><em>ASP.net API with a sqlserver backend. SQLServer will have two tables: bookList and Authors, with the booklist id as a foreign key in Authors. Unit tests using xunit and moq. Set up Azurite to provide a message queue, have the GET endpoint create a durable function to listen to the message queue, and have POST use an Azure function to add records to the database. Create service classes for the logic and Repository classes for interacting with the Database</em><br /><br /></p>



<span id="more-13071"></span>



<p class="has-blue-color has-text-color has-link-color wp-elements-d1bc50bd24ca3b97135005b9ece327ac">CoPilot responded with a huge amount of work. Here is its description of what it did in reaction to this prompt.</p>



<p>Bootstrapped a complete ASP.NET Core Web API with SQL Server backend, Azure Functions for async processing, and Azurite-backed message queues. The architecture separates GET operations (handled by durable functions) from POST operations (handled by queue-triggered functions).</p>



<h2 class="wp-block-heading">Database Schema</h2>



<ul class="wp-block-list">
<li><strong>Books</strong>&nbsp;table: Id, Title, ISBN, PublishedDate, Price</li>



<li><strong>Authors</strong>&nbsp;table: Id, FirstName, LastName, Email, BookId (FK → Books)</li>



<li>One-to-many relationship via BookId foreign key</li>



<li>EF Core migrations included</li>
</ul>



<h2 class="wp-block-heading">Architecture Layers</h2>



<p><strong>Repository Pattern</strong></p>



<ul class="wp-block-list">
<li><code>IBookRepository</code>&nbsp;/&nbsp;<code>BookRepository</code>&nbsp;&#8211; Book data access</li>



<li><code>IAuthorRepository</code>&nbsp;/&nbsp;<code>AuthorRepository</code>&nbsp;&#8211; Author data access</li>



<li>Async operations, includes navigation properties</li>
</ul>



<p><strong>Service Layer</strong></p>



<ul class="wp-block-list">
<li><code>IBookService</code>&nbsp;/&nbsp;<code>BookService</code>&nbsp;&#8211; Book business logic</li>



<li><code>IAuthorService</code>&nbsp;/&nbsp;<code>AuthorService</code>&nbsp;&#8211; Author business logic</li>



<li><code>QueueService</code>&nbsp;&#8211; Azure Queue message dispatch</li>
</ul>



<p><strong>API Controllers</strong></p>



<ul class="wp-block-list">
<li><code>BooksController</code>&nbsp;&#8211; GET triggers durable function via&nbsp;<code>book-get-queue</code>, POST queues to&nbsp;<code>book-queue</code></li>



<li><code>AuthorsController</code>&nbsp;&#8211; Standard CRUD operations</li>
</ul>



<h2 class="wp-block-heading">Azure Functions</h2>



<p><strong>BookQueueProcessor</strong>&nbsp;(Queue-triggered)</p>



<pre class="wp-block-preformatted">[Function("ProcessBookCreation")]
public async Task Run(
    [QueueTrigger("book-queue", Connection = "AzureWebJobsStorage")] string queueMessage)
{
    var bookMessage = JsonSerializer.Deserialize&lt;BookMessage&gt;(queueMessage);
    // Creates book + authors in single transaction
}</pre>



<p><strong>BookDurableFunction</strong>&nbsp;(Orchestrator)</p>



<pre class="wp-block-preformatted">[Function("BookDurableOrchestrator")]
public async Task&lt;string&gt; RunOrchestrator([OrchestrationTrigger] TaskOrchestrationContext context)
{
    var result = await context.CallActivityAsync&lt;string&gt;("ProcessGetRequest", input);
    return result;
}</pre>



<h2 class="wp-block-heading">Message Queue Flow</h2>



<pre class="wp-block-code"><code>GET  /api/books → book-get-queue → Durable Function → Orchestration
POST /api/books → book-queue     → Azure Function   → DB Insert
</code></pre>



<h2 class="wp-block-heading">Testing</h2>



<p>16 xUnit tests with Moq covering:</p>



<ul class="wp-block-list">
<li>Service layer behavior (BookService)</li>



<li>Repository CRUD operations (InMemory EF provider)</li>
</ul>



<h2 class="wp-block-heading">Configuration</h2>



<ul class="wp-block-list">
<li><code>appsettings.json</code>&nbsp;&#8211; SQL Server connection, Azurite queue config</li>



<li><code>local.settings.json</code>&nbsp;&#8211; Azure Functions runtime settings</li>



<li>Two queues:&nbsp;<code>book-queue</code>,&nbsp;<code>book-get-queue</code></li>



<li>Connection string:&nbsp;<code>UseDevelopmentStorage=true</code>&nbsp;(Azurite)</li>
</ul>



<h2 class="wp-block-heading">Documentation</h2>



<ul class="wp-block-list">
<li>SETUP.md &#8211; Installation and database setup</li>



<li>QUICKSTART.md &#8211; Three-terminal startup sequence</li>



<li>ARCHITECTURE.md &#8211; System diagrams and data flows</li>
</ul>



<p><a href="https://github.com/apps/copilot-swe-agent"></a>Note that along the way I added to my prompt, and CoPilot adjusted what it was doing accordingly.</p>



<p></p>



<h2 class="wp-block-heading">Merge info</h2>



<h3 class="wp-block-heading" id=":r14:">Review requested</h3>



<p>Review has been requested on this pull request. It is not required to merge.&nbsp;<a href="https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/requesting-a-pull-request-review">Learn more about requesting a pull request review.</a></p>



<h3 class="wp-block-heading" id=":r18:">No conflicts with base branch</h3>



<p>Merging can be performed automatically.</p>



<h3 class="wp-block-heading" id=":r19:">This pull request is still a work in progress</h3>



<p>CoPilot then asked me to review the code (Pull Request):<br /></p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="800" height="451" src="https://jesseliberty.com/wp-content/uploads/2025/12/image-800x451.png" alt="" class="wp-image-13072" srcset="https://jesseliberty.com/wp-content/uploads/2025/12/image-800x451.png 800w, https://jesseliberty.com/wp-content/uploads/2025/12/image-300x169.png 300w, https://jesseliberty.com/wp-content/uploads/2025/12/image-150x85.png 150w, https://jesseliberty.com/wp-content/uploads/2025/12/image-768x433.png 768w, https://jesseliberty.com/wp-content/uploads/2025/12/image.png 1342w" sizes="auto, (max-width: 800px) 100vw, 800px" /></figure>



<p>I then clicked on Merge and it provided me with a form for the title and description, already filled out for me to modify and approve</p>



<h2 class="wp-block-heading">Readme file</h2>



<p>CoPilot documented what it did in the ReadMe&#8230;<br /></p>



<h2 class="wp-block-heading">Architecture Overview<a href="https://github.com/JesseLiberty/BookstoreAPI#architecture-overview"></a></h2>



<p>This is an ASP.NET Core Web API with a SQL Server backend, using Azure Functions and Azure Storage Queues (via Azurite) for asynchronous processing.</p>



<h3 class="wp-block-heading">Key Components<a href="https://github.com/JesseLiberty/BookstoreAPI#key-components"></a></h3>



<ol class="wp-block-list">
<li><strong>ASP.NET Web API</strong>&nbsp;&#8211; RESTful API with controllers</li>



<li><strong>SQL Server Database</strong>&nbsp;&#8211; Two tables: Books and Authors</li>



<li><strong>Azure Functions</strong>&nbsp;&#8211; Asynchronous processing
<ul class="wp-block-list">
<li><strong>Durable Function</strong>&nbsp;for GET requests</li>



<li><strong>Queue-triggered Function</strong>&nbsp;for POST requests</li>
</ul>
</li>



<li><strong>Azure Storage Queues</strong>&nbsp;(via Azurite) &#8211; Message bus</li>



<li><strong>Service Layer</strong>&nbsp;&#8211; Business logic</li>



<li><strong>Repository Pattern</strong>&nbsp;&#8211; Data access layer</li>



<li><strong>Unit Tests</strong>&nbsp;&#8211; xUnit with Moq</li>
</ol>



<h3 class="wp-block-heading">Database Schema</h3>



<p><a href="https://github.com/JesseLiberty/BookstoreAPI#database-schema"></a></p>



<ul class="wp-block-list">
<li><strong>Books Table</strong>: Id, Title, ISBN, PublishedDate, Price</li>



<li><strong>Authors Table</strong>: Id, FirstName, LastName, Email, BookId (FK)</li>
</ul>



<p>Relationship: One Book can have many Authors</p>



<h3 class="wp-block-heading">Request Flow<a href="https://github.com/JesseLiberty/BookstoreAPI#request-flow"></a></h3>



<h4 class="wp-block-heading">GET /api/books<a href="https://github.com/JesseLiberty/BookstoreAPI#get-apibooks"></a></h4>



<ol class="wp-block-list">
<li>API receives request</li>



<li>Sends message to&nbsp;<code>book-get-queue</code>&nbsp;(Azurite)</li>



<li><strong>Durable Function</strong>&nbsp;reads from queue and orchestrates processing</li>



<li>API returns book data from database</li>
</ol>



<h4 class="wp-block-heading">POST /api/books<a href="https://github.com/JesseLiberty/BookstoreAPI#post-apibooks"></a></h4>



<ol class="wp-block-list">
<li>API receives BookMessage</li>



<li>Sends message to&nbsp;<code>book-queue</code>&nbsp;(Azurite)</li>



<li><strong>Azure Function</strong>&nbsp;(queue-triggered) reads from queue</li>



<li>Function creates book and authors in database</li>



<li>API returns 202 Accepted</li>
</ol>



<h2 class="wp-block-heading">Quick Start<a href="https://github.com/JesseLiberty/BookstoreAPI#quick-start"></a></h2>



<p>See SETUP.md for detailed setup instructions.</p>



<h2 class="wp-block-heading">Running Tests</h2>



<p><a href="https://github.com/JesseLiberty/BookstoreAPI#running-tests"></a></p>



<pre class="wp-block-preformatted">cd BookstoreAPI.Tests
dotnet test</pre>



<p>All 16 unit tests cover:</p>



<ul class="wp-block-list">
<li>Service layer (8 tests)</li>



<li>Repository layer (8 tests)</li>
</ul>



<h2 class="wp-block-heading">Starting</h2>



<p>CoPilot gave me step by step instructions on how to use this&#8230;</p>



<h1 class="wp-block-heading">Bookstore API Setup Guide</h1>



<p><a href="https://github.com/JesseLiberty/BookstoreAPI/blob/main/SETUP.md#bookstore-api-setup-guide"></a></p>



<h2 class="wp-block-heading">Prerequisites</h2>



<p><a href="https://github.com/JesseLiberty/BookstoreAPI/blob/main/SETUP.md#prerequisites"></a></p>



<ul class="wp-block-list">
<li>.NET 8.0 or later SDK</li>



<li>SQL Server or LocalDB</li>



<li>Node.js (for Azurite)</li>



<li>Azurite (Azure Storage Emulator)</li>
</ul>



<h2 class="wp-block-heading">Setup Instructions<a href="https://github.com/JesseLiberty/BookstoreAPI/blob/main/SETUP.md#setup-instructions"></a></h2>



<h3 class="wp-block-heading">1. Install Azurite<a href="https://github.com/JesseLiberty/BookstoreAPI/blob/main/SETUP.md#1-install-azurite"></a></h3>



<pre class="wp-block-preformatted">npm install -g azurite</pre>



<h3 class="wp-block-heading">2. Start Azurite<a href="https://github.com/JesseLiberty/BookstoreAPI/blob/main/SETUP.md#2-start-azurite"></a></h3>



<p>Run Azurite to provide local Azure Storage emulation (including queues):</p>



<pre class="wp-block-preformatted">azurite --silent --location ./azurite --debug ./azurite/debug.log</pre>



<p>This will start:</p>



<ul class="wp-block-list">
<li>Blob service on&nbsp;<code>http://127.0.0.1:10000</code></li>



<li>Queue service on&nbsp;<code>http://127.0.0.1:10001</code>&nbsp;(message bus)</li>



<li>Table service on&nbsp;<code>http://127.0.0.1:10002</code></li>
</ul>



<h3 class="wp-block-heading">3. Database Setup<a href="https://github.com/JesseLiberty/BookstoreAPI/blob/main/SETUP.md#3-database-setup"></a></h3>



<p>Update the connection string in&nbsp;<code>appsettings.json</code>&nbsp;if needed:</p>



<pre class="wp-block-preformatted">"DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=BookstoreDb;Trusted_Connection=True;MultipleActiveResultSets=true"</pre>



<p>Create the database:</p>



<pre class="wp-block-preformatted">cd BookstoreAPI
dotnet ef migrations add InitialCreate
dotnet ef database update</pre>



<h3 class="wp-block-heading">4. Run the Application<a href="https://github.com/JesseLiberty/BookstoreAPI/blob/main/SETUP.md#4-run-the-application"></a></h3>



<h4 class="wp-block-heading">Start the API<a href="https://github.com/JesseLiberty/BookstoreAPI/blob/main/SETUP.md#start-the-api"></a></h4>



<pre class="wp-block-preformatted">cd BookstoreAPI
dotnet run</pre>



<h4 class="wp-block-heading">Start the Azure Functions<a href="https://github.com/JesseLiberty/BookstoreAPI/blob/main/SETUP.md#start-the-azure-functions"></a></h4>



<pre class="wp-block-preformatted">cd BookstoreAPI.Functions
func start</pre>



<p>Or if using&nbsp;<code>dotnet</code>&nbsp;directly:</p>



<pre class="wp-block-preformatted">cd BookstoreAPI.Functions
dotnet run</pre>



<h3 class="wp-block-heading">5. Run Tests<a href="https://github.com/JesseLiberty/BookstoreAPI/blob/main/SETUP.md#5-run-tests"></a></h3>



<pre class="wp-block-preformatted">cd BookstoreAPI.Tests
dotnet test</pre>



<h2 class="wp-block-heading">Architecture<a href="https://github.com/JesseLiberty/BookstoreAPI/blob/main/SETUP.md#architecture"></a></h2>



<h3 class="wp-block-heading">Components<a href="https://github.com/JesseLiberty/BookstoreAPI/blob/main/SETUP.md#components"></a></h3>



<ol class="wp-block-list">
<li><strong>ASP.NET Web API</strong>&nbsp;(<code>BookstoreAPI</code>)
<ul class="wp-block-list">
<li>Controllers for Books and Authors</li>



<li>Service layer for business logic</li>



<li>Repository layer for data access</li>



<li>Queue service for message bus integration</li>
</ul>
</li>



<li><strong>Azure Functions</strong>&nbsp;(<code>BookstoreAPI.Functions</code>)
<ul class="wp-block-list">
<li><strong>GET Durable Function</strong>: Listens to&nbsp;<code>book-get-queue</code>&nbsp;via Azurite</li>



<li><strong>POST Azure Function</strong>: Processes book creation from&nbsp;<code>book-queue</code></li>
</ul>
</li>



<li><strong>Database</strong>
<ul class="wp-block-list">
<li>SQL Server with two tables:&nbsp;<code>Books</code>&nbsp;and&nbsp;<code>Authors</code></li>



<li><code>Authors</code>&nbsp;has a foreign key to&nbsp;<code>Books</code>&nbsp;(BookId)</li>
</ul>
</li>
</ol>



<ol class="wp-block-list">
<li>Client →&nbsp;<code>GET /api/books</code></li>



<li>API sends message to&nbsp;<code>book-get-queue</code>&nbsp;(Azurite)</li>



<li>Durable Function reads from queue</li>



<li>API returns book data from database</li>
</ol>



<h4 class="wp-block-heading">POST Request Flow<a href="https://github.com/JesseLiberty/BookstoreAPI/blob/main/SETUP.md#post-request-flow"></a></h4>



<ol class="wp-block-list">
<li>Client →&nbsp;<code>POST /api/books</code>&nbsp;with BookMessage</li>



<li>API sends message to&nbsp;<code>book-queue</code>&nbsp;(Azurite)</li>



<li>Azure Function reads from queue</li>



<li>Azure Function adds book to database</li>



<li>API returns 202 Accepted</li>
</ol>



<h2 class="wp-block-heading">Queue Configuration<a href="https://github.com/JesseLiberty/BookstoreAPI/blob/main/SETUP.md#queue-configuration"></a></h2>



<ul class="wp-block-list">
<li><strong>GET Queue</strong>:&nbsp;<code>book-get-queue</code>&nbsp;&#8211; Used by Durable Function</li>



<li><strong>POST Queue</strong>:&nbsp;<code>book-queue</code>&nbsp;&#8211; Used by regular Azure Function</li>
</ul>



<p>Both queues use Azurite as the message bus with connection string:</p>



<pre class="wp-block-code"><code>UseDevelopmentStorage=true
</code></pre>



<h2 class="wp-block-heading">API Endpoints<a href="https://github.com/JesseLiberty/BookstoreAPI/blob/main/SETUP.md#api-endpoints"></a></h2>



<h3 class="wp-block-heading">Books<a href="https://github.com/JesseLiberty/BookstoreAPI/blob/main/SETUP.md#books"></a></h3>



<ul class="wp-block-list">
<li><code>GET /api/books</code>&nbsp;&#8211; Get all books (triggers durable function)</li>



<li><code>GET /api/books/{id}</code>&nbsp;&#8211; Get book by ID</li>



<li><code>POST /api/books</code>&nbsp;&#8211; Create book (via Azure Function)</li>



<li><code>PUT /api/books/{id}</code>&nbsp;&#8211; Update book</li>



<li><code>DELETE /api/books/{id}</code>&nbsp;&#8211; Delete book</li>
</ul>



<h3 class="wp-block-heading">Authors<a href="https://github.com/JesseLiberty/BookstoreAPI/blob/main/SETUP.md#authors"></a></h3>



<ul class="wp-block-list">
<li><code>GET /api/authors</code>&nbsp;&#8211; Get all authors</li>



<li><code>GET /api/authors/{id}</code>&nbsp;&#8211; Get author by ID</li>



<li><code>GET /api/authors/book/{bookId}</code>&nbsp;&#8211; Get authors by book</li>



<li><code>POST /api/authors</code>&nbsp;&#8211; Create author</li>



<li><code>PUT /api/authors/{id}</code>&nbsp;&#8211; Update author</li>



<li><code>DELETE /api/authors/{id}</code>&nbsp;&#8211; Delete author</li>
</ul>



<h2 class="wp-block-heading">Testing<a href="https://github.com/JesseLiberty/BookstoreAPI/blob/main/SETUP.md#testing"></a></h2>



<p>The solution includes unit tests using xUnit and Moq for:</p>



<ul class="wp-block-list">
<li>Service layer</li>



<li>Repository layer</li>



<li>Controllers</li>
</ul>



<h2 class="wp-block-heading">Clone</h2>



<p class="has-blue-color has-text-color has-link-color wp-elements-86d6ea4237673bdd5f8bf0edf0317e40">Once I got over being stunned by all this, I cloned the repository</p>



<h2 class="wp-block-heading">Quick Start</h2>



<p>I then took a look at the Quick Start Guid</p>



<h2 class="wp-block-heading">Prerequisites Check<a href="https://github.com/JesseLiberty/BookstoreAPI/blob/main/QUICKSTART.md#prerequisites-check"></a></h2>



<pre class="wp-block-preformatted"># Check .NET SDK
dotnet --version  # Should be 10.0 or later

# Check Node.js (for Azurite)
node --version

# Install Azurite globally
npm install -g azurite</pre>



<p>Oops, didn&#8217;t have node. No problem, a quick trip to Google revealed where I could get it and installation was a snap. Note that the Node installer also installed chocolatey, which took a few minutes.</p>



<h2 class="wp-block-heading">Step-by-Step Startup<a href="https://github.com/JesseLiberty/BookstoreAPI/blob/main/QUICKSTART.md#step-by-step-startup"></a></h2>



<h3 class="wp-block-heading">1. Start Azurite (Terminal 1)<a href="https://github.com/JesseLiberty/BookstoreAPI/blob/main/QUICKSTART.md#1-start-azurite-terminal-1"></a></h3>



<pre class="wp-block-preformatted">cd BookstoreAPI
mkdir -p azurite
azurite --silent --location ./azurite --debug ./azurite/debug.log</pre>



<p>Azurite will start on:</p>



<ul class="wp-block-list">
<li>Blob:&nbsp;<a href="http://127.0.0.1:10000/">http://127.0.0.1:10000</a></li>



<li>Queue:&nbsp;<a href="http://127.0.0.1:10001/">http://127.0.0.1:10001</a>&nbsp;←&nbsp;<strong>Message bus for our app</strong></li>



<li>Table:&nbsp;<a href="http://127.0.0.1:10002/">http://127.0.0.1:10002</a></li>
</ul>



<h3 class="wp-block-heading">2. Start Azure Functions (Terminal 2)<a href="https://github.com/JesseLiberty/BookstoreAPI/blob/main/QUICKSTART.md#2-start-azure-functions-terminal-2"></a></h3>



<pre class="wp-block-preformatted">cd BookstoreAPI.Functions

# Copy template if needed
cp local.settings.template.json local.settings.json

# Run functions
dotnet run</pre>



<p>Functions will listen to:</p>



<ul class="wp-block-list">
<li><code>book-queue</code>&nbsp;&#8211; For POST requests</li>



<li><code>book-get-queue</code>&nbsp;&#8211; For GET requests</li>
</ul>



<h3 class="wp-block-heading">3. Start Web API (Terminal 3)<a href="https://github.com/JesseLiberty/BookstoreAPI/blob/main/QUICKSTART.md#3-start-web-api-terminal-3"></a></h3>



<pre class="wp-block-preformatted">cd BookstoreAPI

# Run the API
dotnet run</pre>



<p>API will start on:&nbsp;<a href="http://localhost:5000/">http://localhost:5000</a>&nbsp;(or check console for actual port)</p>



<h3 class="wp-block-heading">4. Test the API<a href="https://github.com/JesseLiberty/BookstoreAPI/blob/main/QUICKSTART.md#4-test-the-api"></a></h3>



<p>Using curl:</p>



<pre class="wp-block-preformatted"># GET all books (triggers durable function)
curl http://localhost:5000/api/books

# POST a new book (uses Azure Function)
curl -X POST http://localhost:5000/api/books \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Test Book",
    "isbn": "123-456-789",
    "publishedDate": "2024-01-01T00:00:00",
    "price": 29.99,
    "authors": [
      {
        "firstName": "John",
        "lastName": "Doe",
        "email": "john@example.com"
      }
    ]
  }'

# GET a specific book
curl http://localhost:5000/api/books/1

# GET all authors
curl http://localhost:5000/api/authors

# GET authors for a specific book
curl http://localhost:5000/api/authors/book/1</pre>



<p>Using the .http file (VS Code with REST Client extension):</p>



<pre class="wp-block-code"><code># Open BookstoreAPI/BookstoreAPI.http
# Click "Send Request" on any request
</code></pre>



<h2 class="wp-block-heading">Troubleshooting<a href="https://github.com/JesseLiberty/BookstoreAPI/blob/main/QUICKSTART.md#troubleshooting"></a></h2>



<h3 class="wp-block-heading">Database Not Found<a href="https://github.com/JesseLiberty/BookstoreAPI/blob/main/QUICKSTART.md#database-not-found"></a></h3>



<pre class="wp-block-preformatted">cd BookstoreAPI

# Install EF Core tools
dotnet tool install --global dotnet-ef

# Create/update database
dotnet ef database update</pre>



<h3 class="wp-block-heading">Azurite Not Starting<a href="https://github.com/JesseLiberty/BookstoreAPI/blob/main/QUICKSTART.md#azurite-not-starting"></a></h3>



<pre class="wp-block-preformatted"># Kill any running instances
pkill azurite

# Clear data and restart
rm -rf azurite
mkdir azurite
azurite --silent --location ./azurite</pre>



<h3 class="wp-block-heading">Functions Not Triggering<a href="https://github.com/JesseLiberty/BookstoreAPI/blob/main/QUICKSTART.md#functions-not-triggering"></a></h3>



<ol class="wp-block-list">
<li>Check Azurite is running</li>



<li>Check connection string in&nbsp;<code>local.settings.json</code>:&nbsp;<code>UseDevelopmentStorage=true</code></li>



<li>Check function logs for errors</li>



<li>Verify queues exist in Azurite (can use Azure Storage Explorer)</li>
</ol>



<h3 class="wp-block-heading">Build Errors<a href="https://github.com/JesseLiberty/BookstoreAPI/blob/main/QUICKSTART.md#build-errors"></a></h3>



<pre class="wp-block-preformatted"># Clean and rebuild
dotnet clean
dotnet restore
dotnet build</pre>



<h2 class="wp-block-heading">Running Tests<a href="https://github.com/JesseLiberty/BookstoreAPI/blob/main/QUICKSTART.md#running-tests"></a></h2>



<pre class="wp-block-preformatted">cd BookstoreAPI.Tests
dotnet test --verbosity normal</pre>



<p>Expected output:</p>



<pre class="wp-block-code"><code>Passed!  - Failed:     0, Passed:    16, Skipped:     0, Total:    16
</code></pre>



<h2 class="wp-block-heading">Monitoring<a href="https://github.com/JesseLiberty/BookstoreAPI/blob/main/QUICKSTART.md#monitoring"></a></h2>



<h3 class="wp-block-heading">View Queue Messages<a href="https://github.com/JesseLiberty/BookstoreAPI/blob/main/QUICKSTART.md#view-queue-messages"></a></h3>



<p><strong>Install Azure Storage Explorer to view:</strong></p>



<ul class="wp-block-list">
<li>Queue messages in&nbsp;<code>book-queue</code>&nbsp;and&nbsp;<code>book-get-queue</code></li>



<li>Connection: Use &#8220;Attach to local emulator&#8221;</li>
</ul>



<h3 class="wp-block-heading">View Function Logs<a href="https://github.com/JesseLiberty/BookstoreAPI/blob/main/QUICKSTART.md#view-function-logs"></a></h3>



<p>Azure Functions will output logs to the console showing:</p>



<ul class="wp-block-list">
<li>Queue triggers firing</li>



<li>Durable function orchestrations</li>



<li>Database operations</li>
</ul>



<h3 class="wp-block-heading">View API Logs<a href="https://github.com/JesseLiberty/BookstoreAPI/blob/main/QUICKSTART.md#view-api-logs"></a></h3>



<p>ASP.NET Core will output logs showing:</p>



<ul class="wp-block-list">
<li>HTTP requests</li>



<li>Controller actions</li>



<li>Queue message sends</li>
</ul>



<h2 class="wp-block-heading">Next Steps<a href="https://github.com/JesseLiberty/BookstoreAPI/blob/main/QUICKSTART.md#next-steps"></a></h2>



<ol class="wp-block-list">
<li><strong>Add Swagger/OpenAPI</strong>&nbsp;&#8211; Already configured, visit&nbsp;<code>/openapi</code>&nbsp;endpoints</li>



<li><strong>Add Authentication</strong>&nbsp;&#8211; Implement JWT or OAuth</li>



<li><strong>Add Validation</strong>&nbsp;&#8211; Data annotations on models</li>



<li><strong>Add More Tests</strong>&nbsp;&#8211; Controller tests, integration tests</li>



<li><strong>Deploy to Azure</strong>&nbsp;&#8211; Use Azure App Service + Azure Functions + Azure SQL</li>
</ol>



<h2 class="wp-block-heading">Project Structure</h2>



<p><a href="https://github.com/JesseLiberty/BookstoreAPI/blob/main/QUICKSTART.md#project-structure"></a></p>



<pre class="wp-block-code"><code>BookstoreAPI/
├── BookstoreAPI/              # Web API
│   ├── Controllers/           # API endpoints
│   ├── Models/                # Data models
│   ├── Services/              # Business logic
│   ├── Data/                  # EF Core + Repositories
│   └── Migrations/            # Database migrations
├── BookstoreAPI.Functions/    # Azure Functions
│   ├── BookQueueProcessor.cs  # POST handler
│   └── BookDurableFunction.cs # GET handler
└── BookstoreAPI.Tests/        # Unit tests
    ├── Services/              # Service tests
    └── Repositories/          # Repository tests</code></pre>



<p class="has-blue-color has-text-color has-link-color wp-elements-f65830830db1f8aead9f85b03fb9e718">Pretty impresive. More than impressive. If I were in my 20s and programming, I would think long and hard about the implications. It is still early days for LLMs, one can only imagine what will be in 5 years. </p>



<p class="has-blue-color has-text-color has-link-color wp-elements-644a4c37ba30236065596a57abdb7b7b">We&#8217;ll get this going and put in a bit of data in the next blog post.</p>



<p></p>



<p></p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>