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

Why Can't You Go Faster With AI? Focus on the Friction to Find Out

1 Share

If you are a manager, a lead engineer, or anyone growing into more responsibility, this throwback episode is built for you. We keep hearing the same question, now louder than ever: "Why can't this go faster?" AI and agentic coding have made the literal coding step dramatically cheaper, so product leaders reasonably expect the whole pipeline to speed up. But it hasn't—and in today's short, focused episode I explore why. The answer isn't new at all. It's the theory of constraints, and it has everything to do with friction you may not be looking at.

  • Speed Isn't the Story—Friction Is: When a fast component gets introduced into the pipeline, the instinct is to celebrate the velocity. But pay attention to what comes after. The real question is what keeps work from naturally flowing faster, and that lives in the friction, not the energy you're pouring in upfront.
  • The Universal Bottleneck: I rarely claim universal truths on this show, but here's one: anything that looks like a pipeline will have a bottleneck. If you're not paying attention to it, it doesn't matter how fast every other step gets. Faster coding just exposes where the constraint really sits.
  • The Two Places Friction Shows Up: For teams fully adopting agentic coding, the bottlenecks cluster in two spots—requirements gathering at the front, and verification, validation, and testing at the back. Rushed requirements upstream create even more painful rework downstream.
  • Why Agents Punish Vague Specs: Human engineers fill in gaps by being close to the work. Agents fill in gaps too, but sometimes incorrectly. If your requirements aren't detailed, the agent guesses, and you pay for it in review. Spend more time in the planning phase, not less.
  • The Foundation You Build On: Agents glob extra code onto a weak structure—unnecessary models, redundant endpoints, patterns that don't fit. A code base organized with clear conventions, good documentation in your CLAUDE.md or AGENTS.md, and dependable patterns lets the agent discover and extend rather than guess and hope.
  • Specification and Validation Are Bookends: Good requirements translate directly into good tests. Acceptance criteria on one end, changes in the middle, validation on the other end—directly connected. Poor specification sitting on a poor structure guarantees poor execution and poor validation.
  • Reframing Your Objections: Think scope creep is the problem? That's a requirements issue. Think you lack the talent? That's a foundation issue—because the engineer's job now is to cultivate the foundation so generated code enriches it instead of toppling it.

This is not a new problem. We asked it of the internet, of web frameworks, of CSS. Now it's time to apply the same principles to agentic velocity: look at your requirements, your foundation, and your validation. Somewhere in those three is your bottleneck. I guarantee it.

📮 Ask a Question

If you enjoyed this episode and would like me to discuss a question that you have on the show, drop it over at: developertea.com.

📮 Join the Discord

If you want to be a part of a supportive community of engineers (non-engineers welcome!) working to improve their lives and careers, join us on the Developer Tea Discord community today!

🗞️ Subscribe to The Tea Break

We are developing a brand new newsletter called The Tea Break! You can be the first in line to receive it by entering your email directly over at developertea.com.

🧡 Leave a Review

If you're enjoying the show and want to support the content head over to iTunes and leave a review!





Download audio: https://dts.podtrac.com/redirect.mp3/cdn.simplecast.com/audio/c44db111-b60d-436e-ab63-38c7c3402406/episodes/fefacc17-e3d8-4484-b27e-7e3cc672fc72/audio/e1400875-9a2e-4fe1-8657-6cbf86180006/default_tc.mp3?aid=rss_feed&feed=dLRotFGk
Read the whole story
alvinashcraft
56 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

Introducing Corvus.Text.Json V5: Extended Types

1 Share

At endjin, we maintain Corvus.JsonSchema, and in the previous post we looked at JSON Pointer resolution.

JSON has a deliberately simple type system - strings, numbers, booleans, null, objects, and arrays. But the data those types carry is often richer than the JSON grammar suggests. A string might be a URI. A number might have 50 significant digits. A date-time might need proper time zone handling. V5 extends the core type system with first-class support for all of these.

UTF-8 URIs and IRIs

JSON Schema defines four URI-related format keywords: uri, uri-reference, iri, and iri-reference. V5 validates and parses all four with zero-allocation ref struct types that operate directly on the UTF-8 bytes in the document buffer.

Utf8Uri and Utf8Iri

Utf8Uri is a readonly ref struct that parses a URI from a ReadOnlySpan<byte> without allocating. It gives you access to every component - scheme, authority, user, host, port, path, query, and fragment - as ReadOnlySpan<byte> slices into the original buffer:

Utf8Uri uri = Utf8Uri.CreateUri(
    "https://api.example.com:8080/v1/users?active=true#top"u8);

// Each component is a ReadOnlySpan<byte> slice - no allocation
ReadOnlySpan<byte> scheme = uri.Scheme;       // "https"
ReadOnlySpan<byte> host = uri.Host;           // "api.example.com"
ReadOnlySpan<byte> path = uri.Path;           // "/v1/users"
ReadOnlySpan<byte> query = uri.Query;         // "active=true"
ReadOnlySpan<byte> fragment = uri.Fragment;    // "top"
int port = uri.PortValue;                      // 8080

For schema-generated types with "format": "uri", the code generator emits a TryGetValue method and an explicit conversion operator:

// Schema: { "type": "string", "format": "uri" }
// Generated type: MyEndpoint

if (endpoint.TryGetValue(out Utf8UriValue uriValue))
{
    using (uriValue)
    {
        Utf8Uri uri = uriValue.Uri;
        // Access components via uri.Scheme, uri.Host, uri.Path, etc.
    }
}

// Or via explicit cast (throws FormatException if invalid)
using Utf8UriValue uriValue = (Utf8UriValue)endpoint;

Utf8UriValue is a regular (non-ref) struct that owns its backing memory. It implements IDisposable, so always use a using declaration so the backing buffer is returned to the pool.

Canonical and display forms

URIs have two standard string representations. The canonical form percent-encodes reserved characters for safe transmission. The display form decodes those sequences for human readability:

Utf8Uri uri = Utf8Uri.CreateUri(
    "https://example.com/caf%C3%A9?q=hello%20world"u8);

// Display form: decodes percent-encoded sequences for readability
// "https://example.com/café?q=hello world"
string display = uri.ToString();

// Canonical form: percent-encodes reserved characters for safe transmission
Span<byte> buffer = stackalloc byte[256];
if (uri.TryFormatCanonical(buffer, out int written))
{
    // "https://example.com/caf%C3%A9?q=hello%20world"
    ReadOnlySpan<byte> canonical = buffer.Slice(0, written);
}

// Display form as UTF-8 bytes
if (uri.TryFormatDisplay(buffer, out written))
{
    ReadOnlySpan<byte> displayUtf8 = buffer.Slice(0, written);
}

Both methods write directly to a Span<byte> with no allocation. ToString() is the convenience overload that allocates a string for the display form.

Why not System.Uri?

System.Uri merges several distinct RFC concepts into a single type. It handles absolute URIs, relative references, and IRIs all through one class, which can be confusing. A method that accepts System.Uri gives no indication of whether it expects an absolute URI, a relative reference, or an IRI. V5 separates these into distinct types (Utf8Uri, Utf8UriReference, Utf8Iri, Utf8IriReference) so the semantic intent is clear at the API boundary.

Beyond the type-safety question, System.Uri allocates a managed string and normalises the URI, which can change its representation. The Utf8 variants validate and decompose the URI in place, with no allocation and no normalisation surprises. For JSON Schema format validation, this means checking whether a string is a valid uri-reference costs nothing beyond the parse itself.

All four types are derived from the .NET runtime's own System.Uri parser, rewritten to operate on UTF-8 spans rather than managed strings.

Arbitrary-precision numerics

JSON has no precision limit on numbers. The string 99999999999999999999999999999.123456789 is perfectly valid JSON. But double gives you about 15 significant digits, and decimal gives you 28. Anything beyond that is silently truncated.

In practice, you will almost never need arbitrary-precision types. The vast majority of JSON numbers fit comfortably in int, long, double, or decimal. The right approach is to use the format keyword in your schema to bound your numeric types appropriately. Use "format": "int32", "format": "double", "format": "decimal", and so on. The code generator will then select the matching .NET type, and you get compile-time safety for free.

BigNumber and BigInteger exist for the vanishingly small number of scenarios where unbounded precision is genuinely required. That includes cryptographic values, scientific datasets with extreme precision, or financial interop where the source system sends numbers beyond 28 significant digits.

How V5 handles numbers internally

V5 never converts a JSON number to a floating-point type during validation or comparison. Instead, it parses the raw UTF-8 bytes into normalised components:

Component Type Example for 1.200e3
isNegative bool false
integral ReadOnlySpan<byte> "1"
fractional ReadOnlySpan<byte> "2"
exponent int 2

All comparison and validation operates on these components. A 500-digit JSON number is compared with perfect accuracy.

BigNumber and BigInteger

When you do need to materialise an arbitrary-precision value, there are two types. BigNumber handles decimal numbers (with a fractional part or exponent), while BigInteger handles integers of unlimited size:

using Corvus.Numerics;

// Arbitrary-precision decimal
BigNumber decimalValue = element.GetBigNumber();
BigNumber result = decimalValue * 2 + BigNumber.Parse("0.001");

// Arbitrary-precision integer
BigInteger intValue = element.GetBigInteger();

BigNumber stores a BigInteger significand and an int exponent (value = significand × 10^exponent). Both types implement INumber<T> on .NET 9+, so they work with generic math APIs.

Formatting

BigNumber implements IFormattable, ISpanFormattable, and IUtf8SpanFormattable on .NET 9+, and the static formatting methods are available on all targets including netstandard2.0. It works with string interpolation, String.Format, and direct span formatting. All the standard numeric format specifiers are supported:

BigNumber value = BigNumber.Parse("12345678901234567890.123456789");

value.ToString("G", CultureInfo.InvariantCulture);   // General: "12345678901234567890.123456789"
value.ToString("F2", CultureInfo.InvariantCulture);  // Fixed-point: "12345678901234567890.12"
value.ToString("N0", CultureInfo.InvariantCulture);  // Number with grouping: "12,345,678,901,234,567,890"
value.ToString("E3", CultureInfo.InvariantCulture);  // Scientific: "1.235E+019"
value.ToString("C", CultureInfo.GetCultureInfo("en-GB"));  // Currency: "£12,345,678,901,234,567,890.12"

For zero-allocation formatting, write directly to a UTF-8 byte span:

Span<byte> buffer = stackalloc byte[128];
if (value.TryFormat(buffer, out int bytesWritten, "F2", CultureInfo.InvariantCulture))
{
    ReadOnlySpan<byte> utf8Result = buffer.Slice(0, bytesWritten);
    // Use utf8Result directly - no string allocation
}

Extended numeric types in code generation

The code generator reads the JSON Schema format keyword to select the appropriate .NET type:

Format .NET type Notes
"int32" int
"int64" long
"int128" Int128 .NET 9+ only; falls back to long on netstandard2.0
"uint128" UInt128 .NET 9+ only; falls back to ulong on netstandard2.0
"half" Half .NET 9+ only; falls back to double on netstandard2.0
"single" float
"double" double
"decimal" decimal
(none, type: integer) long Default for unformatted integers
(none, type: number) double Default for unformatted numbers

For types that are only available on modern .NET, the code generator emits #if NET guards with appropriate fallbacks.

NodaTime integration

If you work with dates and times in .NET, NodaTime is the de-facto library for rich date and time handling. It helps you think about your data more clearly and express operations on that data more precisely. V5 includes built-in UTF-8 parsers for ISO 8601 formats that produce NodaTime types directly, without going through DateTime or DateTimeOffset as an intermediate step.

JSON Schema format NodaTime type Example value
"date" LocalDate "2026-05-31"
"date-time" OffsetDateTime "2026-05-31T10:30:00+01:00"
"time" OffsetTime "10:30:00+01:00"
"duration" Period "P1Y2M3DT4H5M6S"

When the code generator encounters these format keywords, the generated types automatically include NodaTime-typed accessors alongside the standard .NET ones:

// Generated from a schema with "format": "date-time"
OffsetDateTime when = calendarEvent.When.GetOffsetDateTime();

// The standard .NET accessor is also available
DateTimeOffset whenDto = calendarEvent.When.GetDateTimeOffset();

The parsers operate directly on the UTF-8 bytes in the document buffer. There is no intermediate string allocation. The NodaTimeExtensions namespace includes custom implementations of the Gregorian calendar calculations needed for validation, so there's no runtime dependency on the NodaTime NuGet package. The parsing is self-contained.

The NodaTime parsers handle the full complexity of ISO 8601 duration syntax, including fractional seconds, negative durations, and the distinction between date-based periods (P1Y2M) and time-based durations (PT1H30M). The Period type preserves the original components rather than normalising to a single unit, so P1M and P30D remain distinct.

Next up

In the [ref slug=introducing-corvus-text-json-v5-toon text=next post], we'll look at TOON - a compact text format for JSON-shaped data that reduces token count when working with LLMs.



Read the whole story
alvinashcraft
57 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

Securing Developers with Tanya Janca

1 Share

How can sysadmins help software developers work securely and make more secure applications? While at NDC in Toronto, Richard sat down with Tanya Janca of SheCodesPurple to discuss what admins can do to help address the security challenges software developers face. Tanya talks about securing development environment and pipelines - developers routinely work from high privilege accounts because their tools require it, and as a result, have become the targets of black hats to get access to accounts, keys, and other exploitable resources. There are plenty of tools available to help work through the issues, including the latest AI-powered tools. LLMs can also help generate more secure code in the first place, and Tanya has created a set of prompts you can use to create more secure software. The threat landscape is shifting with these tools, and we need to act quickly to resist the new attacks!

Links

Recorded May 8, 2026





Download audio: https://cdn.simplecast.com/media/audio/transcoded/5379899c-61c5-43c3-aa3f-1128cffd9ef4/c2165e35-09c6-4ae8-b29e-2d26dad5aece/episodes/audio/group/ce7e1136-d556-4f60-9dcc-9c0462a5c4bc/group-item/4ca332c4-8fa2-42ea-a888-e9daa1802988/128_default_tc.mp3?aid=rss_feed&feed=cRTTfxcT
Read the whole story
alvinashcraft
5 hours ago
reply
Pennsylvania, USA
Share this story
Delete

2.9.1

1 Share

Implement a new yaml setting for users to configure the default port …

Read the whole story
alvinashcraft
5 hours ago
reply
Pennsylvania, USA
Share this story
Delete

v2026.6.10

1 Share

Release v2026.6.10

Read the whole story
alvinashcraft
5 hours ago
reply
Pennsylvania, USA
Share this story
Delete

Enable agentic work management with Microsoft Planner MCP Server

1 Share

Microsoft Planner capabilities are now part of the new Work IQ APIs which reached general availability on June 16, 2026.

The Microsoft Planner MCP Server is part of Work IQ MCP and makes it easier to connect Planner capabilities into agent-driven workflows. It allows agents to manage and automate work across plans. These capabilities extend Planner into end-to-end work management workflows, where agents can take action on plans and handle routine coordination. Designed for building custom agents, the Planner MCP Server connects planning, execution, and tracking across tools. As agents take on day-to-day coordination, teams can spend less time managing tasks and more time focusing on higher-impact decisions.

Core capabilities

The Work IQ MCP provides a unified set of tools that allow agents to interact with Microsoft 365 entities through a single endpoint. As part of this interface, the Planner MCP Server enables new ways to help manage and automate work.

Core capabilities include:

  • Manage and automate work across plans: Create new plans and tasks, update existing ones, and understand the state of work. This includes scenarios such as status reporting, workload visibility, and automated updates.
  • Connect work across Microsoft 365: Use shared context from Planner, Microsoft Teams, Outlook, and more to coordinate work across tools and workflows
  • Built for secure, enterprise workflows: All actions are permission-trimmed and policy-enforced under the signed-in user’s Entra ID context.

The Planner MCP Server helps eliminate the complexity of custom integrations and unlocks new possibilities for agentic automation. By unifying Planner with other Microsoft 365 data, organizations can now connect work management into their intelligent workflows across teams and departments.

Example scenarios

Planner can be accessed through a conversational experience for creating, updating, and reviewing plans and tasks, as well as understanding the state of work through natural language queries. Custom agents can then be built to support organizational needs, helping automate common scenarios and repetitive workflows.

Examples of support scenarios include:

  • On-demand status reporting: Summarize progress, risks, and blockers across plans and teams.
  • Cross-plan workload views: Aggregate tasks and assignments for resource management.
  • Automated plan updates: Create, update, or archive plans and tasks based on triggers.
  • Automated follow-ups: Schedule reminders, assign tasks, and track completion across Planner and Teams.

Getting started

To begin leveraging the Planner MCP Server, enable the Work IQ API in your tenant. Leverage Copilot extensibility guidance to enhance your AI solutions with Work IQ APIs and follow these step-by-step instructions to get started.

  • Install and set up GitHub Copilot CLI.
  • Install the Work IQ Plugin: Follow the instructions to install the Work IQ plugin. Once the installation is done, restart GitHub Copilot CLI.
  • Verify that the MCP server and skills are loaded running ‘/mcp’ and ‘/skills’ commands on your console.
  • You are all set. You can now start playing with your Planner data. For example, try prompts such as:
    • Show me my Planner plans.
    • View my tasks that are due this week.
    • Find high-priority overdue tasks from my plans.
    • Based on my latest email thread with Adrian, update my Compliance Audit Plan.

Be sure to follow best practices for delegated permissions, admin consent, and Copilot licensing.

Limitations

The Planner MCP Server currently supports basic plans. Premium capabilities such as dependencies, task history, and custom fields are not yet available. It is available through GitHub Copilot in VS Code and GitHub Copilot CLI. Support for Copilot Studio, Agent 365 SDK/CLI, and Microsoft Foundry is in progress.

Provide feedback

We can’t wait to hear how you use the Planner MCP Server to transform work management. Please share your experience in the comments below. We also encourage you to share any feature requests by adding your ideas to the Planner Feedback Portal.

Thank you for being part of our journey and helping us shape the future of work management with AI!

Learn more

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