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

Article: Beyond CLEAN and MVP: Architecting an Offline-first Reactive Data Layer in Android

1 Share

With the Reactive Data Layer Architecture (RDLA), you establish a clear boundary between public data APIs and private, framework-specific data-source implementations. Your presentation layer operates in a purely reactive manner, observing data changes rather than procedurally querying them. RDLA also simplifies testing by encouraging you to program to interfaces and use clean seeding patterns.

By Mervyn Anthony
Read the whole story
alvinashcraft
just a second ago
reply
Pennsylvania, USA
Share this story
Delete

Visual Studio Code 1.127

1 Share

Learn what's new in Visual Studio Code 1.127 (Insiders)

Read the full article

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

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
2 hours 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
2 hours 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
7 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
7 hours ago
reply
Pennsylvania, USA
Share this story
Delete
Next Page of Stories