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

Addressing GitHub’s recent availability issues

1 Share

Over the past several weeks, GitHub has experienced significant availability and performance issues affecting multiple services. Three of the most significant incidents happened on February 2, February 9, and March 5.

First and foremost, we take responsibility. We have not met our own availability standards, and we know that reliability is foundational to the work you do every day. We understand the impact these outages have had on your teams, your workflows, and your confidence in our platform.

Here, we’ll unpack what’s been causing these incidents and what we’re doing to make our systems more resilient moving forward.

What happened

These incidents have occurred during a period of extremely rapid usage growth across our platform, exposing scaling limitations in parts of our current architecture. Specifically, we’ve found that recent platform instability was primarily driven by rapid load growth, architectural coupling that allowed localized issues to cascade across critical services, and inability of the system to adequately shed load from misbehaving clients.

Before we cover what we are doing to prevent these issues going forward, it is worth diving into the details of the most impactful incidents.

February 9 incident

On Monday, February 9, we experienced a high‑impact incident due to a core database cluster that supports authentication and user management becoming overloaded. The mistakes that led to the problem were made days and weeks earlier.

In early February, two very popular client-side applications that make a significant amount of API calls against our servers were released, with unintentional changes driving a more-than-tenfold increase in read traffic they generated. Because these applications end up being updated by the users over time, the increase in usage doesn’t become evident right away; it appears as enough users upgrade.

On Saturday, February 7, we deployed a new model. While trying to get it to customers as quickly as possible, we changed a refresh TTL on a cache storing user settings from 12 to 2 hours. The model was released to a narrower set of customers due to limited capacity, which made the change necessary. At this point, everything was operating normally because the weekend load is significantly lower, and we didn’t have sufficiently granular alarms to detect the looming issue.

Three things then compounded on February 9: our regular peak load, many customers updating to the new version of the client apps as they were starting their week, and another new model release. At this point, the write volume due to the increased TTL and the read volume from the client apps combined to overwhelm the database cluster. While the TTL change was quickly identified as a culprit, it took much longer to understand why the read load kept increasing, which prolonged the incident. Further, due to the interaction between different services after the database cluster became overwhelmed, we needed to block the extra load further up the stack, and we didn’t have sufficiently granular switches to identify which traffic we needed to block at that level.

The investigation for the February 9 incident raised a lot of important questions about why the user settings were stored in this particular database cluster and in this particular way. The architecture was originally selected for simplicity at a time when there were very few models and very few governance controls and policies related to those models. But over time, something that was a few bytes per user grew into kilobytes. We didn’t catch how dangerous that was because the load was visible only during new model or policy rollouts and was masked by the TTL. Since this database cluster houses data for authentication and user management, any services that depend on these were impacted.

GitHub Actions incidents on February 2 and March 5

We also had two significant instances where our failover solution was either insufficient or didn’t function correctly:

  • Actions hosted runners had a significant outage on February 2. Most cloud infrastructure issues in this area typically do not cause impact as they occur in a limited number of regions, and we automatically shift traffic to healthy regions. However, in this case, there was a cascading set of events triggered by a telemetry gap that caused existing security policies to be applied to key internal storage accounts affecting all regions. This blocked access to VM metadata on VM creates and halted hosted runner lifecycle operations.
  • Another impactful incident for Actions occurred on March 5. Automated failover has been progressively rolling out across our Redis infrastructure, and on this day, a failover occurred for a Redis cluster used by Actions job orchestration. The failover performed as expected, but a latent configuration issue meant the failover left the cluster in a state with no writable primary. With writes failing and failover not available as a mitigation, we had to correct the state manually to mitigate. This was not an aggressive rollout or missing resiliency mechanism, but rather latent configuration that was only exposed by an event in production infrastructure.

For both of these incidents, the investigations brought up unexpected single points of failure that we needed to protect and needed to dry run failover procedures in the production more rigorously.

Across these incidents, contributing factors expanded the scope of impact to be much broader or longer than necessary, including:

  • Insufficient isolation between critical path components in our architecture
  • Inadequate safeguards for load shedding and throttling
  • Gaps in end-to-end validation, monitoring for attention on earlier signals, and partner coordination during incident response

What we are doing now

Our engineering teams are fully engaged in both near-term mitigations and durable longer-term architecture and process investments. We are addressing two common themes: managing rapidly increasing load by focusing on resilience and isolation of critical paths and preventing localized failures from ever causing broad service degradation.

In the near term, we are prioritizing stabilization work to reduce the likelihood and impact of incidents. This includes:

  1. Redesigning our user cache system, which hosts model policies and more, to accommodate significantly higher volume in a segmented database cluster.
  2. Expediting capacity planning and completing a full audit of fundamental health for critical data and compute infrastructure to address urgent growth.
  3. Further isolate key dependencies so that critical systems like GitHub Actions and Git will not be impacted by any shared infrastructure issues, reducing cascade risk. This is being done through a combination of removing or handling dependency failures where possible or isolating dependencies.
  4. Protecting downstream components during spikes to prevent cascading failures while prioritizing critical traffic loads.

In parallel, we are accelerating deeper platform investments to deliver on GitHub’s commitment to supporting sustained, high-rate growth with high availability. These include:

  1. Migrating our infrastructure to Azure to accommodate rapid growth, enabling both vertical scaling within regions and horizontal scaling across regions. In the short term, this provides a hybrid approach for infrastructure resiliency. As of today, 12.5% of all GitHub traffic is served from our Azure Central US region, and we are on track to serving 50% of all GitHub traffic by July. Longer term, this enables simplification of our infrastructure architecture and more global resiliency by adopting managed services.
  2. Breaking apart the monolith into more isolated services and data domains as appropriate, so we can scale independently, enable more isolated change management, and implement localized decisions about shedding traffic when needed.

We are also continuing tactical repair work from every incident.

Our commitment to transparency

We recognize that it’s important to provide you with clear communication and transparency when something goes wrong. We publish summaries of all incidents that result in degraded performance of GitHub services on our status page and in our monthly availability reports. The February report will publish later today with a detailed explanation of incidents that occurred last month, and our March report will publish in April.

Given the scope of recent incidents, we felt it was important to address them with the community today. We know GitHub is critical digital infrastructure, and we are taking urgent action to ensure our platform is available when and where you need it. Thank you for your patience as we strengthen the stability and resilience of the GitHub platform.

The post Addressing GitHub’s recent availability issues appeared first on The GitHub Blog.

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

Learn how to fine-tune LLMs in 12 hours

1 Share

The goal isn't just to train a model; it's to build a system that understands your specific data as well as you do.

We just posted a massive, 12-hour course on the freeCodeCamp.org YouTube channel designed to turn you from an AI consumer into an LLM architect.

While massive models like Llama 3, Gemini, and GPT-4 are impressive out of the box, their true power is unlocked when they are tailored to specific domains. This course is a deep dive into the modern LLM ecosystem, teaching you how to take these giants and make them work for your specific needs.

The course is structured into four major sections:

  • The Foundations of PEFT: Learn why full fine-tuning is often overkill. You will learn about Parameter-Efficient Fine-Tuning (PEFT), and master techniques like LoRA and QLoRA to train models on consumer hardware.

  • Advanced Alignment: You’ll learn about Reinforcement Learning from Human Feedback (RLHF) and the increasingly popular Direct Preference Optimization (DPO) to align models with human intent.

  • High-Performance Tooling: Get hands-on with the fastest tools in the industry. The course covers Unsloth (for 2x faster training), Axolotl, and the Llama Factory project for streamlined workflows.

  • Enterprise & Multimodal AI: Beyond text, the course explores Vision Transformers (ViT), multimodal architectures (Image, Video, Audio), and how to leverage enterprise APIs from OpenAI and Google Cloud Vertex AI.

Watch the full course on the freeCodeCamp.org YouTube channel (12-hour watch).



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

Stack Overflow Repositioning

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

How to Organize Minimal APIs

1 Share

Minimal APIs arrived with .NET 6 and now, in Version 10, are already part of the daily routine for many developers. In this post, we'll explore best practices for organizing your Minimal APIs and see how Carter can help make them even cleaner, more modular and more elegant.

In contrast to traditional Controller classes, minimal APIs offer a more compact way to create web APIs. The downside of this approach is that the Program class can quickly become bloated. To overcome this problem, we can use some approaches and libraries that help organize the mess and bring order to things.

In this post, we’ll create a Minimal API with a disorganized Program class and then refactor it into a clean and elegant version. We’ll also explore a very versatile approach using the Carter library.

One Class to Rule Them All

.NET 6 introduced a new format for creating web APIs, eliminating the need for the Startup class and even the Controller classes, responsible for HTTP input and output methods.

In the Minimal APIs model, the Program class, previously responsible only for bootstrapping the application, now also concentrates everything that was previously in Startup and Controller: service configuration, middleware definition and endpoint mapping.

This unification considerably reduced the number of classes in the project, but also opened the door to a common problem: the tendency to transform Program.cs into an inflated, confusing and difficult-to-maintain file.

Without a structured approach to organizing these responsibilities, what should be minimalist can quickly become something far from it. Therefore, adopting strategies and tools that help modularize routes, configurations and business rules is not only a good practice but also essential for maintaining the scalability and clarity of the project.

Organization Is the Key ️

When working with Minimal APIs, the tendency is to put everything in the Program class, but the truth is that this class wasn’t designed to be a repository, but rather to orchestrate the application. Keeping this class clean, small and focused on composition is the secret to preserving readability and facilitating code evolution.

And this is only possible when we use consistent organization: separating modules, extracting configurations, delegating routes and preventing implementation details from leaking to the highest level of the application.

In this section, we’ll first create a messy example and then transform Program.cs into a truly minimalist entry point, taking in only what it should take in, while moving the rest to the most appropriate places.

Creating the Project

The complete application code is available in this GitHub repository: Customer Admin source code.

To create the base application, you can use the command below:

dotnet new web -n CustomerAdmin

Then, open the application and add the following dependencies to the .csproj file:

 <ItemGroup>
    <PackageReference Include="Microsoft. EntityFrameworkCore" Version="10.0.1" />
    <PackageReference Include="Microsoft. EntityFrameworkCore .SqlServer" Version="10.0.1" />
    <PackageReference Include="Microsoft. EntityFrameworkCore .Design" Version="10.0.1">
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
      <PrivateAssets>all</PrivateAssets>
    </PackageReference>
  </ItemGroup>

Now let’s move on to the grand finale, the monstrous Program.cs class. Replace the code in the Program class with the code below:

using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));

builder.Services.AddLogging(logging =>
{
    logging.ClearProviders();
    logging.AddConsole();
});

builder.Services.AddHttpClient();
builder.Services.AddEndpointsApiExplorer();

builder.Services.AddCors(options =>
{
    options.AddPolicy("Default", policy =>
    {
        policy.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod();
    });
});

builder.Services.AddSingleton<IEmailSender, SmtpEmailSender>();

var app = builder.Build();

app.UseCors("Default");

app.Use(async (context, next) =>
{
    var logger = context.RequestServices.GetRequiredService<ILoggerFactory>().CreateLogger("RequestLogger");
    logger.LogInformation("Request: {method} {url}", context.Request.Method, context.Request.Path);
    await next();
});

app.MapPost("/customers", async (CustomerDto dto, AppDbContext db, IEmailSender email) =>
{
    if (string.IsNullOrWhiteSpace(dto.Name))
        return Results.BadRequest("Name is required");

    if (!new EmailAddressAttribute().IsValid(dto.Email))
        return Results.BadRequest("Invalid email");

    var entity = new Customer
    {
        Name = dto.Name,
        Email = dto.Email,
        CreatedAt = DateTime.UtcNow
    };

    db.Customers.Add(entity);
    await db.SaveChangesAsync();

    await email.SendAsync(dto.Email, "Welcome!", "Thanks for registering!");

    return Results.Created($"/customers/{entity.Id}", entity);
});

app.MapGet("/customers", async (AppDbContext db) =>
{
    var items = await db.Customers.ToListAsync();
    return Results.Ok(items);
});

app.MapGet("/customers/{id:int}", async (int id, AppDbContext db) =>
{
    var customer = await db.Customers.FindAsync(id);
    return customer is null ? Results.NotFound() : Results.Ok(customer);
});

app.MapPost("/customers/{id:int}/activate", async (int id, AppDbContext db) =>
{
    var customer = await db.Customers.FindAsync(id);
    if (customer is null)
        return Results.NotFound();

    if (customer.IsActive)
        return Results.BadRequest("Customer already active");

    customer.IsActive = true;
    await db.SaveChangesAsync();

    return Results.Ok(customer);
});

app.Run();

public class Customer
{
    public int Id { get; set; }
    public string Name { get; set; } = default!;
    public string Email { get; set; } = default!;
    public bool IsActive { get; set; }
    public DateTime CreatedAt { get; set; }
}

public class CustomerDto
{
    public string Name { get; set; } = default!;
    public string Email { get; set; } = default!;
}

public interface IEmailSender
{
    Task SendAsync(string to, string subject, string body);
}

public class SmtpEmailSender : IEmailSender
{
    public Task SendAsync(string to, string subject, string body)
    {
        Console.WriteLine($"Sending email to {to}: {subject}");
        return Task.CompletedTask;
    }
}

public class AppDbContext : DbContext
{
    public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }

    public DbSet<Customer> Customers => Set<Customer>();
}

The code above demonstrates the type of architecture that starts simple but quickly becomes a serious problem as the application grows. The Program class should only be the compose root of the application, where services are registered, middlewares are configured and high-level endpoints are mapped. Instead, the code above:

  • Validates data
  • Accesses the database
  • Implements business rules
  • Sends emails
  • Defines DTOs, entities, services and DbContext
  • Registers all services manually
  • Writes middleware logic directly in it
  • Maps detailed and complex endpoints

The result is a single file that performs the work of about 10 different files, resulting in something that grows uncontrollably and prevents the healthy evolution of the application. Each new feature exacerbates the problem, leaving the class disorganized and prone to errors. Furthermore, it hinders teamwork, generates merge conflicts and makes the integration of new developers much slower.

Putting Things in Order

Now that we’ve seen a bad example of a bloated and messy Program class, let’s put things in order, separating each part into its proper place.

The image below shows what the complete application structure will look like at the end of the post:

Project structure

So, let’s start with the entities, for which we can create separate classes. In this case, create a new folder called “Model” and inside it create the classes below:

namespace CustomerAdmin.Models;

public class Customer
{
    public int Id { get; set; }
    public string Name { get; set; } = default!;
    public string Email { get; set; } = default!;
    public bool IsActive { get; set; }
    public DateTime CreatedAt { get; set; }
}
namespace CustomerAdmin.Models;

public class CustomerDto
{
    public string Name { get; set; } = default!;
    public string Email { get; set; } = default!;
}

Now let’s create all the other things related to the Infrastructure layer, which refers to everything that communicates with external services such as databases and web APIs. Create a new folder called “Infrastructure” and inside it create a new folder called “Data” and add the following class to it:

using CustomerAdmin.Models;
using Microsoft.EntityFrameworkCore;

namespace CustomerAdmin.Infrastructure.Data;

public class AppDbContext : DbContext
{
    public AppDbContext(DbContextOptions<AppDbContext> options)
        : base(options) { }

    public DbSet<Customer> Customers => Set<Customer>();
}

All subsequent folders should be created inside the Infrastructure folder. So, add another folder called “Email” and, inside it, add the following interface and class:

namespace CustomerAdmin.Infrastructure.Email;

public interface IEmailSender
{
    Task SendAsync(string to, string subject, string body);
}
namespace CustomerAdmin.Infrastructure.Email;

public class SmtpEmailSender : IEmailSender
{
    public Task SendAsync(string to, string subject, string body)
    {
        Console.WriteLine($"Sending email to {to}: {subject}");
        return Task.CompletedTask;
    }
}

Here we create an interface and a class for sending emails, which is used only for demonstration purposes and is therefore very simple, but in real applications it can become large, so separating it from the Program class is essential.

The next step is to create the extension methods for the controllers, endpoints and policies. Create a new folder called “Extensions” and, inside it, create the classes below:

using System.ComponentModel.DataAnnotations;
using CustomerAdmin.Infrastructure.Data;
using CustomerAdmin.Infrastructure.Email;
using CustomerAdmin.Models;
using Microsoft.EntityFrameworkCore;

namespace CustomerAdmin.Infrastructure.Extensions;

public static class CustomerEndpoints
{
    public static async Task<IResult> CreateCustomer(
        CustomerDto dto,
        AppDbContext db,
        IEmailSender email
    )
    {
        if (string.IsNullOrWhiteSpace(dto.Name))
            return Results.BadRequest("Name is required");

        if (!new EmailAddressAttribute().IsValid(dto.Email))
            return Results.BadRequest("Invalid email");

        var entity = new Customer
        {
            Name = dto.Name,
            Email = dto.Email,
            CreatedAt = DateTime.UtcNow,
        };

        db.Customers.Add(entity);
        await db.SaveChangesAsync();

        await email.SendAsync(dto.Email, "Welcome!", "Thanks for registering!");

        return Results.Created($"/customers/{entity.Id}", entity);
    }

    public static async Task<IResult> GetAll(AppDbContext db)
    {
        return Results.Ok(await db.Customers.ToListAsync());
    }

    public static async Task<IResult> GetById(int id, AppDbContext db)
    {
        var customer = await db.Customers.FindAsync(id);
        return customer is null ? Results.NotFound() : Results.Ok(customer);
    }

    public static async Task<IResult> Activate(int id, AppDbContext db)
    {
        var customer = await db.Customers.FindAsync(id);

        if (customer is null)
            return Results.NotFound();
        if (customer.IsActive)
            return Results.BadRequest("Customer already active");

        customer.IsActive = true;
        await db.SaveChangesAsync();

        return Results.Ok(customer);
    }
}

Here we add all the endpoints for the new customer registration API. Note that the methods are static so they can be used by the endpoint mapper that we will create next.

So now create a new class called CustomersModule and add the following code to it:

namespace CustomerAdmin.Infrastructure.Extensions;

public static class CustomersModule
{
    public static IEndpointRouteBuilder MapCustomerEndpoints(this IEndpointRouteBuilder app)
    {
        var group = app.MapGroup("/customers");

        group.MapPost("/", CustomerEndpoints.CreateCustomer);
        group.MapGet("/", CustomerEndpoints.GetAll);
        group.MapGet("/{id:int}", CustomerEndpoints.GetById);
        group.MapPost("/{id:int}/activate", CustomerEndpoints.Activate);

        return app;
    }
}

In the code above, we define an endpoint module where we use an extension method to encapsulate the mapping of routes related to customers. All routes are grouped with the prefix /customers, which allows for scalable application growth and facilitates API organization.

Now let’s create another extension class to define the registration of application services in the ASP.NET Core dependency injection container. This class will centralize the configuration of application dependencies such as infrastructure, HTTP communication, API documentation and CORS policies. So, still in the “Extensions” folder, add the class below:

using CustomerAdmin.Infrastructure.Data;
using CustomerAdmin.Infrastructure.Email;
using Microsoft.EntityFrameworkCore;

namespace CustomerAdmin.Infrastructure.Extensions;

public static class ServiceCollectionExtensions
{
    public static IServiceCollection AddApplicationServices(this IServiceCollection services)
    {
        services.AddScoped<IEmailSender, SmtpEmailSender>();
        return services;
    }

    public static IServiceCollection AddInfrastructure(
        this IServiceCollection services,
        IConfiguration config
    )
    {
        services.AddDbContext<AppDbContext>(options =>
            options.UseSqlServer(config.GetConnectionString("DefaultConnection"))
        );

        services.AddHttpClient();
        return services;
    }

    public static IServiceCollection AddApiDocumentation(this IServiceCollection services)
    {
        services.AddEndpointsApiExplorer();
        return services;
    }

    public static IServiceCollection AddCorsPolicies(this IServiceCollection services)
    {
        services.AddCors(options =>
        {
            options.AddPolicy(
                "Default",
                policy => policy.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod()
            );
        });
        return services;
    }
}

Note that here we have several configurations grouped into a separate file, leaving the Program class free for what it is actually responsible for.

Now let’s create the class responsible for implementing the configurations that belong to the WebApplication class. So, create a new class called WebApplicationExtensions and add the code below to it:

using CustomerAdmin.Infrastructure.Middlewares;

namespace CustomerAdmin.Infrastructure.Extensions;

public static class WebApplicationExtensions
{
    public static IApplicationBuilder UseApiDocumentation(this WebApplication app)
    {
        return app;
    }

    public static IApplicationBuilder UseCorsPolicies(this WebApplication app)
    {
        app.UseCors("Default");
        return app;
    }

    public static WebApplication UseRequestLogging(this WebApplication app)
    {
        app.UseMiddleware<RequestLoggingMiddleware>();
        return app;
    }
}

The last configuration class will be used to implement logging middleware. So, create a new class called RequestLoggingMiddleware and add the code below to it:

namespace CustomerAdmin.Infrastructure.Middlewares;

public class RequestLoggingMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<RequestLoggingMiddleware> _logger;

    public RequestLoggingMiddleware(RequestDelegate next, ILogger<RequestLoggingMiddleware> logger)
    {
        _next = next;
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        _logger.LogInformation("Request: {method} {url}", context.Request.Method, context.Request.Path);
        await _next(context);
    }
}

With all the implementations organized, we can finally implement the method calls in the Program class. So, replace the existing code there with the code below:

using CustomerAdmin.Infrastructure.Extensions;

var builder = WebApplication.CreateBuilder(args);

builder
    .Services.AddApplicationServices()
    .AddInfrastructure(builder.Configuration)
    .AddApiDocumentation()
    .AddCorsPolicies();

var app = builder.Build();

app.UseRequestLogging();
app.UseApiDocumentation();
app.UseCorsPolicies();

app.MapCustomerEndpoints();

app.Run();

See how the Program class looks now. Instead of spreading configurations across multiple files, it centralizes the entire application initialization process in a fluid way.

Starting with the creation of the WebApplicationBuilder, responsible for preparing the environment, the configuration of services is done in a chained manner, which has greatly improved readability compared to the first version.

Each extension method represents a well-defined block of responsibility, such as registering application services, configuring the infrastructure (database, external integrations, etc.), API documentation and CORS policies.

Finally, the HTTP request pipeline, middleware and endpoint mapping are initiated. Thus, this refactored approach makes the Program class leaner and more expressive, serving as a high-level view of how the application is composed and initialized.

Taking a Shortcut with Carter

Carter is an open-source NuGet package for defining HTTP routes and request handlers using a simple and declarative syntax.

Below, we’ll use Carter in the refactored version and see how it can be a great option for organizing minimal APIs.

To download Carter to the project, simply run the command below:

dotnet add package Carter --version 10.0.0

Next, create a new folder in the project called “Modules,” inside it another folder called “Customers” and, inside that folder, create the class below:

namespace CustomerAdmin.Modules.Customers;

using System.ComponentModel.DataAnnotations;

public class CustomersModule : ICarterModule
{
    public void AddRoutes(IEndpointRouteBuilder app)
    {
        var group = app.MapGroup("/customers");

        group.MapPost("/", Create);
        group.MapGet("/", GetAll);
        group.MapGet("/{id:int}", GetById);
        group.MapPost("/{id:int}/activate", Activate);
    }

    private static async Task<IResult> Create(CustomerDto dto, AppDbContext db, IEmailSender email)
    {
        if (string.IsNullOrWhiteSpace(dto.Name))
            return Results.BadRequest("Name is required");

        if (!new EmailAddressAttribute().IsValid(dto.Email))
            return Results.BadRequest("Invalid email");

        var customer = new Customer
        {
            Name = dto.Name,
            Email = dto.Email,
            CreatedAt = DateTime.UtcNow,
        };

        db.Customers.Add(customer);
        await db.SaveChangesAsync();

        await email.SendAsync(dto.Email, "Welcome!", "Thanks for registering!");

        return Results.Created($"/customers/{customer.Id}", customer);
    }

    private static async Task<IResult> GetAll(AppDbContext db)
    {
        return Results.Ok(await db.Customers.ToListAsync());
    }

    private static async Task<IResult> GetById(int id, AppDbContext db)
    {
        var customer = await db.Customers.FindAsync(id);
        return customer is null ? Results.NotFound() : Results.Ok(customer);
    }

    private static async Task<IResult> Activate(int id, AppDbContext db)
    {
        var customer = await db.Customers.FindAsync(id);
        if (customer is null)
            return Results.NotFound();
        if (customer.IsActive)
            return Results.BadRequest("Customer already active");

        customer.IsActive = true;
        await db.SaveChangesAsync();

        return Results.Ok(customer);
    }
}

Note that the class we created inherits from the Carter interface: ICarterModule, which allows grouping related endpoints without coupling them to Program.cs. This isolates route definitions, validations and integrations into separate modules, making the code scalable and aligned with the true purpose of Minimal APIs: simplicity with organization.

To configure the Program class with Carter, replace the existing code with the code below:

using Carter;
using CustomerAdmin.Infrastructure.Extensions;

var builder = WebApplication.CreateBuilder(args);

builder
    .Services.AddApplicationServices()
    .AddInfrastructure(builder.Configuration)
    .AddApiDocumentation()
    .AddCorsPolicies();

builder.Services.AddCarter();

var app = builder.Build();

app.UseRequestLogging();
app.UseApiDocumentation();
app.UseCorsPolicies();

app.MapCarter();

app.Run();

Note that the Program class remains simple and clean using Carter. Another important point to highlight is that we have no coupling to the Program class. Instead, we pass all responsibility for the modules to Carter through dependency injection (DI), implemented by the app.MapCarter(); method.

Conclusion

Minimal APIs streamline development by offering a simple approach to creating compact endpoints. However, as the application grows, it’s common for the Program class to become extensive and disorganized if project structure isn’t carefully considered.

To minimize the chances of having an inflated Program class, we can adopt some organizational strategies. In this post, we saw an approach that clearly separates the responsibilities of each class and, to shorten the process, we used the Carter library, leaving the Program class responsible only for the configurations that truly belong to it.

I hope this post helps you create more organized APIs that are ready to evolve!

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

DirectStorage 1.4 release adds support for Zstandard

1 Share

Today we’re releasing the public preview of DirectStorage 1.4 and the initial public preview of the Game Asset Conditioning Library. Together, they introduce Zstandard (Zstd) compression as an option for game assets on Windows. This new support meets the needs of the gaming ecosystem, bringing an open standard that improves compression ratios, enables faster load times, and provides smoother asset streaming for content-rich games.

We shared this availability at GDC in DirectX State of the Union: DirectStorage and Beyond, along with how our GPU hardware and software vendor partnerships will help this work reach ecosystem scale. Read on to understand all the details!

DirectStorage 1.4 public preview is now available

DirectStorage and Zstd

DirectStorage 1.4 brings Zstd codec support to the runtime. Zstd is a popular and open compression standard that meets our key criteria for the next great compression codec for game development.

We evaluated codecs across the following key criteria: compression ratio and decompression performance, hardware and software availability, and existing adoption. Zstd stands out by delivering competitive compression ratios and decompression performance, broad availability on hardware and software across operating systems, and widespread adoption in OS, cloud, and web scenarios.

In this release, Zstd is added to our multi-tier decompression framework with support for CPU and GPU decompression. This lets developers pick the best execution option for their workload today, while our GPU partners work towards future hardware specific optimizations for Zstd.

We’re also open sourcing Microsoft’s Zstd GPU decompression compute shader on the DirectStorage GitHub as an early, working baseline that all GPU implementations can reference. The shader is in development and is initially optimized for content chunked to 256KB or smaller, consistent with modern game packaging patterns for streaming workloads. We plan to expand capabilities and continue improving performance of the shader over the coming months as these compression investments scale across the PC ecosystem.

Improved developer orchestration and control

DirectStorage 1.3 introduced EnqueueRequests, giving developers more control over how data requests are issued and synchronized with graphics work. DirectStorage 1.4 continues this investment by adding global D3D12 CreatorID support. Developers specifying a CreatorID via DStorageSetConfiguration2 associates a D3D12 CreatorID with internal D3D12 command queues that DirectStorage manages on a per-device basis. This enables D3D12 command queue grouping to properly account for DirectStorage workloads facilitating improved predictability and GPU execution scheduling.

Introducing the Game Asset Conditioning Library

As we invested in Zstd, we saw opportunity to further improve the compression ratio resulting in the Game Asset Conditioning Library (GACL). GACL is designed to work in your existing content pipeline delivering up to a 50% improvement in Zstd compression ratios for your assets, while keeping runtime decompression cost low when used with DirectStorage.

This initial public preview contains lossless and lossy conditioning techniques including:

  • Shuffling: Transforms BCn bit streams to promote additional and lower cost matches for Zstd.
  • Block-Level Entropy Reduction (BLER) and Component-Level Entropy Reduction (CLER): Early investments in using perceptual quality to guide entropy reduction techniques at the block and component level. CLER utilizes machine learning to improve outcomes.

DirectStorage and GACL are designed to work together. After a Zstd stream is decompressed at runtime, any shuffle transforms applied during content conditioning are seamlessly reversed by DirectStorage. DirectStorage 1.4 supports this post processing step for BC1, BC3, BC4 and BC5 textures. BC7 support and additional post processing performance improvements will arrive in a future DirectStorage update.

Building for the PC ecosystem with our GPU hardware vendor partners

We are co-engineering closely with GPU hardware vendors to ensure Zstd decompression performs well across the breadth of gaming hardware. We’re excited about the optimizations each IHV will deliver in driver updates later this year that will light up even better performance when using Zstd through DirectStorage.

AMD: “Aligning the industry on an open compression standard builds a foundation for future game titles to deliver immersive experiences with even larger worlds than realistically possible today. We plan to make optimizations for AMD GPUs available in a public driver during the second half of 2026 and look forward to seeing how developers use these investments to enhance the player experience.” – Daniel Staheli, CVP Software Development, AMD

Intel: “We’re co-engineering with Microsoft to tune Zstandard decompression through DirectStorage across our GPU architectures. We look forward to sharing our performance improvements in the months ahead.” – Lisa Pearce, Corporate Vice President, Software Group, Intel

NVIDIA: “NVIDIA is excited to bring Zstd support to game developers, with decompression optimizations tailored for NVIDIA GeForce RTX GPUs arriving in the second half of this year.” – Patrick Neill, Distinguished Engineer, NVIDIA

Qualcomm: “Before the end of the year, we’re excited to bring tuned driver updates that reflect our investments in Zstd decompression on our platforms. We look forward to these investments ensuring reliable, high performance asset streaming across Windows games.” – Nagendra Kumar, Senior Director of Engineering, Qualcomm

Get started with Zstd

Get started with Zstd support in DirectStorage 1.4 and Game Asset Conditioning Library 1.0 using the steps below. This public preview release is meant to let you start evaluating Zstd with minimal disruption in your existing content and build pipeline.

  1. Download and try the DirectStorage 1.4 public preview to see Zstd support in action.
  2. Download and integrate the Game Asset Conditioning Library 1.0 public preview into your existing content pipeline.
  3. Run the new GameAssetConditioningDemo sample to see the recommended API usage and integration patterns. The sample starts with the asset content pipeline, uses GACL to shuffle and compress BCn textures at build time, and then loads and renders them at runtime through DirectStorage and D3D12.
  4. Run the updated GpuDecompressionBenchmark sample, which now supports Zstd, to compare throughput and CPU overhead across compression formats on your target hardware.
  5. Check out the Zstd GPU decompression compute shader to see how it integrates with DirectStorage and contribute improvements that scale across the breadth of GPUs.

We want your feedback as you explore these previews, especially around real-world pipeline integration, asset streaming behavior in production content, and what you need next from DirectStorage and Game Asset Conditioning Library. Feel free to open issues on the DirectStorage or Game Asset Conditioning Library GitHub, reach out over email at askwindstorage@microsoft.com, or use the #directstorage channel on the DirectX Discord.

The post DirectStorage 1.4 release adds support for Zstandard appeared first on DirectX Developer Blog.

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

Quadratic Regression with SGD Training Using JavaScript

1 Share
Dr. James McCaffrey presents a complete end-to-end demonstration of quadratic regression, with SGD training, implemented from scratch, using JavaScript. Compared to standard linear regression, quadratic regression is better able to handle data with a non-linear structure, and data with interactions between predictor variables.
Read the whole story
alvinashcraft
10 minutes ago
reply
Pennsylvania, USA
Share this story
Delete
Next Page of Stories