Sr. Content Developer at Microsoft, working remotely in PA, TechBash conference organizer, former Microsoft MVP, Husband, Dad and Geek.
161869 stories
·
33 followers

Mark Michaelis: Mastering the Agentic Coding Workflow - Episode 421

1 Share

https://clearmeasure.com/developers/forums/

Today I'm joined by Mark Michaelis, founder, CEO and Chief Technical Architect of IntelliTect in Spokane, Washington, and a Microsoft Regional Director and C# MVP for more than twenty-five years. Mark is the lead author of the Essential C# book series and the free companion site essentialcsharp.com, and he's continued that work as a member of Microsoft's design review teams for C#, Azure, and Azure DevOps, and as an adjunct professor at Eastern Washington University. He's been on the conference circuit recently with talks like "Mastering the Agentic Coding Workflow" and "AI Axioms: Principles for Thriving in the Age of Artificial Intelligence," bringing his deep C# background to bear on how developers work with AI day to day. You can find him on GitHub as markmichaelis and connect with him on LinkedIn. This is Mark's first time on the show, and I'm excited to get his perspective on where AI-assisted development is headed.

Intellitect Website - https://intellitect.com/team/mark-michaelis/
Essential CSharp - https://essentialcsharp.com/
Github - https://github.com/markmichaelis
EssentialCSharp Github - https://github.com/IntelliTect/EssentialCSharp
LinkedIn - https://www.linkedin.com/in/markmichaelis/
Email - mark@IntelliTect.com

Want to Learn More?
Visit AzureDevOps.Show for show notes and additional episodes.





Download audio: https://traffic.libsyn.com/clean/secure/azuredevops/Episode_421.mp3?dest-id=768873
Read the whole story
alvinashcraft
just a second ago
reply
Pennsylvania, USA
Share this story
Delete

AWS Weekly Roundup: GPT-6 Sol and Luna, Claude Opus 5.5 on Amazon Bedrock, Strands harness, and more (September 28, 2026)

1 Share

If there’s one theme that defined last week, it’s choice. The frontier models keep arriving, and the interesting question is no longer just “how smart is it?” but “which model fits this step, at this cost, at this latency?” That’s exactly what landed on Amazon Bedrock over the past few days: GPT-6 Sol and GPT-6 Luna from OpenAI, giving you two new points on the intelligence-versus-efficiency curve, and Claude Opus 5.5 from Anthropic, the first of the Claude 5.5 family.

GPT-6 Sol is built for the demanding, recurring work of development and operations, while GPT-6 Luna makes focused, repeatable tasks practical at high volume, and both ship at significantly lower pricing than their GPT-5.6 predecessors. Claude Opus 5.5, meanwhile, does more with fewer tokens than Opus 5 and is tuned for agentic coding and long-running tasks. What I like about all three is that they push toward the same idea: match the model to the job instead of reaching for the biggest one every time. The other thread was observability catching up to this agentic world, including a launch I had the pleasure of writing about myself.

Now, let’s get into this week’s AWS news…

Last week’s launches
Here are some launches and updates from this past week that caught my attention:

  • Introducing Amazon CloudWatch Omni – You can now observe your applications and AI agents together in a single, collaborative experience. Amazon CloudWatch Omni is built on OpenTelemetry, so your existing telemetry shows up with nothing to reconfigure, and your whole team reaches it through one URL with enterprise SSO — no console access required. It auto-discovers your services, maps dependencies, and brings AWS DevOps Agent into investigation sessions to correlate signals and trace root causes. There’s a companion post on the agent-observability side, a deeper dive on the AWS Cloud Operations blog on what observability for the AI era looks like, and the announcement on What’s New with the specifics. If you want the bigger picture, Matt Wood’s Wrong, not broken is a great read on why correctness now has to be measured at the level of the run.
  • Enhanced custom event buses in Amazon EventBridge – Amazon EventBridge now offers an enhanced custom event bus purpose-built for organizations scaling event-driven applications across teams and accounts. You can now deploy a single centralized bus shared across every account in your organization through AWS RAM, with optional event ordering, a simplified Subscriber resource that bundles filtering, targets, and retries, content-based deduplication, and synchronous invocation for targets like AWS Lambda. A new ingress/egress pricing model replaces the compounding cross-account routing charges of multi-bus setups, and your existing buses keep working unchanged as “classic.”
  • Amazon SageMaker HyperPod Inference Gateway – You can now front your LLM inference on Amazon SageMaker HyperPod with a Kubernetes-native, GPU-aware routing layer that deploys as a single Amazon EKS managed add-on with zero application changes. Instead of round-robin load balancing, it routes on real-time inference signals — KV cache utilization, queue depth, prefix cache hits, predicted latency, and more — cutting first-token latency by up to 82% in mixed-hardware and bursty scenarios. It works with any OpenAI-compatible model server, including vLLM and SGLang.
  • AI agent skills for AWS End User Messaging and Amazon SES – You can now build and send messages by asking your AI coding agent in plain language. Amazon SES and AWS End User Messaging publish AI agent skills for the AWS MCP Server, giving your agent step-by-step, validated guidance for tasks like verifying a sending identity, sending a production email, or building a branded RCS agent with cards and buttons. The skills work with Claude Code, Codex, Cursor, and Kiro, so you can complete messaging workflows without hopping between docs and console screens.

For a full list of AWS announcements, be sure to keep an eye on the What’s New with AWS page.

Other AWS news
Here are some additional posts and resources that you might find interesting:

  • Introducing Strands harness – The Strands Agents team released Strands harness, a fully assembled, general-purpose agent harness you can run locally or deploy anywhere, under Apache 2.0. It takes one line of Python or TypeScript to wire up your model of choice across Amazon Bedrock, Anthropic, OpenAI, Google, or a local Ollama model, and it ships with sensible defaults for prompt caching and context management (truncating bulky tool results, compacting when the context window fills up, and keeping memory across runs). The team reports it costs about 28% less than comparable harnesses on the same models while holding accuracy steady.
  • Announcing the new AWS Reimagine report on AI – The AWS Executive in Residence team spent nine months interviewing 154 leaders across 27 countries about what separates organizations that turn AI into value from those that don’t. The report is candid (including where AI hasn’t worked at Amazon), and the recurring insight is that once building gets fast, the bottleneck moves to deciding, funding, and governing the work. Well worth a read if you’re thinking about how your teams adopt AI in practice.

For a full list of AWS blog posts, be sure to keep an eye on the AWS Blogs page.

Upcoming AWS events
Check your calendar and sign up for upcoming AWS events:

  • AWS re:Invent – AWS re:Invent returns to Las Vegas from November 30 to December 4, and session times, locations, and speakers are live. Reserved seating opens October 6, so register now and be ready to claim your spot in chalk talks, workshops, and builders’ sessions.
  • AWS Summits – With re:Invent on the horizon, the Summits are wrapping up for the year. The last stop is Dubai (September 30) at the Dubai World Trade Center, with 60+ sessions, an AWS Village, and hands-on workshops.

Join the AWS Builder Center to connect with builders, share solutions, and access content that supports your development. Browse here for upcoming AWS-led in-person and virtual events and developer-focused events. That’s all for this week. Check back next Monday for another Weekly Roundup!

— Daniel Abib

Read the whole story
alvinashcraft
12 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

Consortium Members elect Board of Directors

1 Share

We are pleased to announce that W3C Consortium Members have elected the following seven people, listed in alphabetical order, to the World Wide Web Consortium, Inc. Board of Directors:

  • Koichi Moriyama
  • Chris Needham
  • Theresa O'Connor
  • Florian Rivoal
  • Avneesh Singh
  • Léonie Watson
  • Hongru (Judy) Zhu

They join Directors appointed by our current Partners: Dominique Hazaël-Massieux, Chunming Hu, Tatsuya Kurosaka (respectively by ERCIM, France; Beihang University, China; and WCAP, Japan); and Tim Berners-Lee who acts as a non-voting Board member.

All Board terms are for two years, starting 30 September 2026 until 30 September 2028.

We thank the 12 candidates who ran in this election.

The W3C Board of Directors is the governing body of the Consortium. The Board has ultimate authority on its strategic direction, has legal obligations and fiduciary responsibility over W3C as a whole. Directors are formally responsible for the correct running of the organization; this is generally characterized in three broad “fiduciary” duties: (1) a duty of care (to exercise good business judgment of a reasonable person with their experience); (2) duty of loyalty (to serve the best interest of the organization); and (3) a duty of obedience (in accordance with all applicable laws and rules). W3C Board of Directors seats are non-paid positions. Learn more about the W3C Board of Directors responsibilities.

Read the whole story
alvinashcraft
25 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

Auth0 for ChatGPT - Build and Manage Identity by Just Asking

1 Share
The Auth0 plugin for ChatGPT can connect your account and configure applications, generate real SDK code matched to your tenant, and get accurate Auth0 answers — all without leaving your chat.

Read the whole story
alvinashcraft
42 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

A Simple Weather App in TypeScript

1 Share

Happy Sunday, my fellow nerds. If you've been keeping up with my posts (all 4 of you - thank you!) then you know I've been experimenting with TypeScript (and Vite) recently. This past week I decided to try building a real "app", and of course by app I mean something incredibly small, but a bit "real world"-ish to kind of get a feel for what the development process felt like.

I decided on a fairly simple weather application. The application would prompt you for a location. That location gets geocoded to longitude and latitude values. With those values, I'd grab a simple weather forecast. After you add one, you can add another, and delete as well. Finally, all the values are stored in local storage so that on reload you get the same values loaded immediately.

If you actually want to see this - you can head over to https://weather-app-ts-vite-alpine.netlify.app/. The complete source may be found here: https://github.com/cfjedimaster/typescript-stuff/tree/main/weather-demo. Now let me dig into what I discovered while building it.

The One Thing

Let me start off with what I think is the biggest thing that clicked with me. While building out the various parts and using (at least some) of the TypeScript features, I immediately ran into probably the biggest win - immediate feedback in my editor when I had screwed things up. And yea, I know that's something TypeScript provided since day one, but it's also a bit different to kind of see it in action while building the app. Especially since I had two or three times when I had to refactor how I was doing things. Having the types in place (specifically for my remote API calls) helped keep things in order across the different moving parts.

I also found myself being forced to think more about how those parts interacted. So for example, a wrapper to my geocoding service - I instinctively knew what I had wanted - but having it spelled out in code actually made me see possible issues - modify my approach - and so forth - all earlier than I would have in my usual development process.

Again - this wasn't necessarily a surprise to me. This all fell in line with what I knew about TypeScript and what I had expected. But never having built an app from scratch with TypeScript before it still felt pretty cool to actually see all this play out.

The Architecture

It feels a bit silly to talk about the architecture of such a small little app, but this is how I built out the parts.

  • Once again, I used Vite.
  • On top of that, I used Alpine.js. In case you missed it, my last blog post discussed how to do this.
  • The main page handles displaying the 'weather cards' along with a simple form to add new locations.
  • When a location is entered, I use the excellent Geocodio API to translate it into longitude and latitude.
  • With that information, I use the Pirate Weather API to get the forecast.

The UI

As mentioned above, this was a Vite and Alpine app, but I also decided to try adding a UI library in as well. I picked Web Awesome primarily for the Card element. I've used this library in the past (when it was called Shoelace) and I really like it, especially as it uses web components. They also make it easy to only import what you need.

In my main.ts, this came down to:

import '@awesome.me/webawesome/dist/styles/webawesome.css';
import '@awesome.me/webawesome/dist/components/card/card.js';
import '@awesome.me/webawesome/dist/components/input/input.js';
import '@awesome.me/webawesome/dist/components/button/button.js';

With this in place, it was a simple matter to use the components. As an example:

<div class="wa-flank:end wa-gap-xs">
<wa-input placeholder="New Location (America, Canada, Mexico, UK only)" x-model="newLocation"></wa-input>
<wa-button variant="brand" @click="addLocation(newLocation)">Add</wa-button>
</div>

Make note of the classes on the div tag there. That comes from Web Awesome's various utility classes and this was a place where I leaned on my AI tool to help.

Here's the entirety of the main HTML page which shows you both Web Awesome in play as well as my Alpine directives:

<!doctype html>
<html lang="en" class="wa-dark">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Weather Demo</title>
  </head>
  <body>
    <div x-data="app" class="container wa-stack">

      <header>
        <h1>Weather Demo</h1>
      </header>

      <template x-if="locations.length === 0"> 
        <p>No locations added yet. Add a location to get started.</p>
      </template>
      <div class="wa-grid wa-gap-1" style="--min-column-size: 30ch;">
      <template x-for="(location,index) in locations" :key="location.name">
        <wa-card class="card-basic" >
          <h3 slot="header" x-text="location.name"></h3>
          <wa-button appearance="plain" @click="removeLocation(index)" slot="header-actions">
            <wa-icon name="trash" variant="solid" label="Delete"></wa-icon>
          </wa-button>
          <template x-if="location.weather">
            <p x-html="`Temperature: ${location.weather.temperature}°F<br/>Condition: ${location.weather.summary}<br>Low: ${location.weather.low}°F<br/>High: ${location.weather.high}°F`"></p>
          </template>
          <template x-if="!location.weather">
            <p>Loading weather data...</p>
          </template>
        </wa-card>
      </template>
        </div>

      <div class="wa-flank:end wa-gap-xs">
      <wa-input placeholder="New Location (America, Canada, Mexico, UK only)" x-model="newLocation"></wa-input>
      <wa-button variant="brand" @click="addLocation(newLocation)">Add</wa-button>
      </div>

    </div>
    <script type="module" src="/src/main.ts"></script>
  </body>
</html>

The Three "Services"

I'm using "services" in quotes there as it feels a bit overly dramatic to name it as such, but I broke out three parts into their own files - one for geocoding, one for the weather, and one for storage. Let's start with storage first.

This file wraps the calls to LocalStorage and in theory - I could easily switch out to IndexedDB in the future - but I'd have to mark the functions async of course:

import type { SavedLocation } from './types';

// This is where it's stored in localStorage. 
const KEY = 'weather-locations';

// Type guard: checks at runtime that an unknown value really is a SavedLocation.
// localStorage can contain anything (old versions of your app, manual edits),
// so we validate instead of trusting it.
// Ray, in case you forget, the value is means that if the function returns true, 
// it's ok for TS to consider the value as of type SavedLocation
function isSavedLocation(value: unknown): value is SavedLocation {
  if (typeof value !== 'object' || value === null) return false;
  const v = value as Record<string, unknown>;
  return (
    typeof v.name === 'string' &&
    typeof v.longitude === 'number' &&
    typeof v.latitude === 'number'
  );
}

export function getLocations(): SavedLocation[] {
  const locations = localStorage.getItem(KEY);
  if(!locations) return [];

  const parsed = JSON.parse(locations);
  return Array.isArray(parsed) ? parsed.filter(isSavedLocation) : [];
}

export function addLocation(location: SavedLocation): void {
  const locations = getLocations();
  locations.push(location);
  localStorage.setItem(KEY, JSON.stringify(locations));
}

export function removeLocation(index: number): void {
  const locations = getLocations();
  locations.splice(index, 1);
  localStorage.setItem(KEY, JSON.stringify(locations));
}

The first thing you see on top is importing a type that defines what a 'stored location' is in terms of my application. From that file, here is the definition:

export type SavedLocation = {
  name: string;
  longitude: number;
  latitude: number, 
  weather?: WeatherData;
};

This should mostly make sense - the name comes from the user, the longitude and latitude from the geocoding service.

The weather part actually comes after the app loads in the existing values from local storage and hits the weather API. There's a part of me that looks at that and thinks maybe I should have a type for the "stored" value and one for the "live" value. I also think I could possibly cache the weather so that on reload it's quicker, but weather data is the kind of thing that gets stale pretty quickly.

Here's that file:

import type { WeatherData } from './types';

const KEY = import.meta.env.VITE_PIRATE_KEY as string;

export async function getWeather(lat: number, lng: number): Promise<WeatherData> {
    const req = await fetch(`https://api.pirateweather.net/forecast/${KEY}/${lat},${lng}?units=us&exclude=minutely,hourly,alerts,flags`);
    const res = await req.json();

    return {
        summary: res.currently.summary,
        temperature: res.currently.temperature,
        low: res.daily.data[0].temperatureLow,
        high: res.daily.data[0].temperatureHigh
    };

}

And here's the type:

export type WeatherData = {
  summary: string;
  temperature: number;
  low: number;
  high: number;
}

Finally, here's the geocoding wrapper:

import type { GeoCodedLocation } from './types';

// Only the fields we use from Geocodio's "simple" format
type GeocodioSimpleResponse = {
  lat?: number;
  lng?: number;
};

const KEY = import.meta.env.VITE_GEOCODIO_KEY as string;

export async function geoCode(location: string): Promise<GeoCodedLocation> {
    const req = await fetch(`https://api.geocod.io/v1.7/geocode?q=${encodeURIComponent(location)}&format=simple&api_key=${KEY}`);
    const res = (await req.json()) as GeocodioSimpleResponse;

    if(!res || !res.lat || !res.lng) {
        throw new Error('No results');
    }

    return { lat: res.lat, lng: res.lng };
}

You'll notice I do the HTTP call slightly different here. I've got an inline type here that defines what is being used from Geocodio. I'll be honest and say I want to think about these two approaches and figure out when I should use each. I mean, when you look at GeoCodedLocation:

export type GeoCodedLocation = {
  lat: number;
  lng: number;
};

I honestly don't get the point of the inline type as well. I was having AI help me a bit, so it's on me to nail down if this makes sense in the context of the file. I'll be returning to that soon.

The Core App

Now let's put it together with my core file - which is mainly Alpine.js specific:

import Alpine from 'alpinejs';

import '@awesome.me/webawesome/dist/styles/webawesome.css';
import '@awesome.me/webawesome/dist/components/card/card.js';
import '@awesome.me/webawesome/dist/components/input/input.js';
import '@awesome.me/webawesome/dist/components/button/button.js';
import './style.css';

import type { SavedLocation } from './types';
import { getLocations, addLocation, removeLocation } from './storage';
import { getWeather } from './weather';
import { geoCode } from './geocode';

Alpine.data('app', () => ({
  locations: [] as SavedLocation[],
  newLocation: '',
  init() {
    this.locations = getLocations();
    if(this.locations.length > 0) {
      this.hydrateWeather();
    }
  },
  async addLocation(location: string) {
    if(!location) return;
    const geo = await geoCode(location);
    const newLoc: SavedLocation = { name: location, longitude: geo.lng, latitude: geo.lat };
    this.locations.push(newLoc);
    // just noticed my method is addLocation as is the imported one. works but - eww. 
    addLocation(newLoc);
    this.newLocation = '';
    // in theory it is wasteful to hydrate ALL of them, but it's a super quick call
    this.hydrateWeather();
  }, 
  removeLocation(index: number) {
    this.locations.splice(index, 1);
    // same issue with naming - advice?
    removeLocation(index);
  },
  async hydrateWeather() {
    for(const loc of this.locations) {
      const weather = await getWeather(loc.latitude, loc.longitude);
      loc.weather = weather;
    }
  }
}));

Alpine.start();

If you actually took the time to read all that, you can see I wrote a few questions to myself. By accident, I ended up having Alpine methods with the same name as methods in my services, and that really bugs me... but it also works... so... yeah. It's kinda clear that this.something is Alpine and something is being imported in, but I still don't care for that. How would you rename things? (And I'd assume the rename would be on the Alpine side.)

I also notice a few lines where I didn't define my type. A quick search shows that by adding strict:true to my tsconfig.json would fix that. I know in the past I've seen that in projects and it's annoyed the heck out of me, so I get why Vite's scaffold doesn't include it, but for my next project, I'm going to turn that on so I can be a bit more precise in my learning with TypeScript.

Your Turn!

Ok, I know blogging is dead (again) and no humans read these posts again, but if you are reading this, and know TypeScript well, I'd love any and all feedback! Give me a comment below and help me keep learning this.

Photo by Patrick Fore on Unsplash

Read the whole story
alvinashcraft
55 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

Fido2 showdown: Security key form factors

1 Share

In my introduction to this FIDO2 showdown, I explained why you might want a security key and how registration and authentication work. Now it is time to look at the different shapes these keys come in, because the shape has quite some impact on how you will use it every day.

A bunch of FIDO2 security keys

Read the whole story
alvinashcraft
1 minute ago
reply
Pennsylvania, USA
Share this story
Delete
Next Page of Stories