ASP.NET Core: Migrating from Startup.cs to Program.cs

ASP.NET Core has evolved significantly since its inception, with one of the most impactful changes arriving in .NET 6: the introduction of the minimal hosting model. This new approach simplifies application bootstrapping by moving away from the traditional Startup.cs file and consolidating the app configuration into a single, streamlined Program.cs. While it may seem like a minor syntactic change at first, the shift reflects a deeper evolution in the ASP.NET Core ecosystem, improving developer productivity, reducing boilerplate, and aligning better with modern development practices.

In this article, we will walk through the motivation behind the minimal hosting model, compare the old and new approaches, and provide a practical guide for migrating an existing ASP.NET Core application from Startup.cs to the new Program.cs style. We will also explore best practices, common pitfalls, and when it might make sense to stick with the traditional model.

Why the Change?

Before diving into migration details, it’s worth asking: why did Microsoft introduce the minimal hosting model in the first place?

1. Simplification

The traditional ASP.NET Core startup process had multiple steps:

  • CreateHostBuilder() in Program.cs
  • ConfigureServices() in Startup.cs
  • Configure() in Startup.cs

For newcomers, this could be overwhelming. The minimal hosting model flattens this process, making it easier to understand and faster to build.

2. Better for Small & Microservices

The minimal hosting model is especially suited to lightweight apps such as microservices or APIs. It reduces the boilerplate and supports concise syntax — ideal for fast development and prototyping.

3. Alignment with Minimal APIs

Minimal hosting aligns with the newer minimal API approach in .NET 6 and beyond. Both share the goal of writing less code to achieve the same functionality.

Traditional Startup.cs Model (Pre-.NET 6)

Here’s a basic example of how a traditional ASP.NET Core app is structured:

Program.cs

public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
}

Startup.cs

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseRouting();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }
}

This separation is clean and clear, but for small projects or developers new to ASP.NET Core, it introduces some complexity.

Minimal Hosting Model (Since .NET 6)

In .NET 6+, the app structure is drastically simplified:

Program.cs

var builder = WebApplication.CreateBuilder(args);

// Register services
builder.Services.AddControllers();

var app = builder.Build();

// Configure the HTTP request pipeline
if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}

app.UseRouting();

app.MapControllers();

app.Run();

As you can see, everything is defined in a single file. It’s more compact and easier to reason about.

Migrating from Startup.cs to Program.cs

Step 1: Remove Startup.cs

You can start by deleting or renaming Startup.cs. We’ll move the contents into Program.cs.

Step 2: Move ConfigureServices()

Find the code in Startup.ConfigureServices() and place it after builder.Services.

var builder = WebApplication.CreateBuilder(args);

// From Startup.ConfigureServices
builder.Services.AddControllers();
builder.Services.AddDbContext<AppDbContext>();
builder.Services.AddScoped<IMyService, MyService>();

Step 3: Move Configure()

Place this code after var app = builder.Build(); and before app.Run();

var app = builder.Build();

// From Startup.Configure
if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}
else
{
    app.UseExceptionHandler("/Home/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthorization();

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

app.Run();

Now your whole application is configured in one place.

Tips and Best Practices

1. Use Partial Classes to Organize Large Program.cs Files

If your Program.cs starts getting large, you can split it into multiple files using extension methods or partial classes.

// Extension method example
public static class ServiceExtensions
{
    public static void AddMyServices(this IServiceCollection services)
    {
        services.AddScoped<IMyService, MyService>();
    }
}

// Usage
builder.Services.AddMyServices();

2. Enable Strong Typing with IConfiguration

You can still bind config settings to strongly typed classes:

builder.Services.Configure<MySettings>(
    builder.Configuration.GetSection("MySettings"));

3. Continue Using Dependency Injection as Usual

All DI patterns (Scoped, Singleton, Transient) work the same.

4. Middleware Order Still Matters

The order in which you call middleware methods (like UseRouting, UseAuthorization, etc.) still affects the request pipeline.

Should You Always Use Minimal Hosting?

While minimal hosting is powerful and clean, it’s not mandatory. You might want to stick with the traditional Startup.cs in certain cases:

  • You prefer separation of concerns
  • You have an older codebase with many contributors
  • You want to gradually adopt minimal APIs but not fully migrate

The good news is, you can still use Startup.cs in .NET 6 and later — it’s fully supported.

Conclusion

Migrating to the minimal hosting model in ASP.NET Core is a smart move for many projects. It reduces boilerplate, speeds up development, and aligns with modern best practices. While it may feel unfamiliar at first, especially for developers used to the Startup.cs pattern, the benefits become clear as you work with it more.

Whether you’re starting fresh or modernizing an existing app, embracing this newer style can make your codebase more approachable and maintainable. And remember: you’re not losing power — you’re just writing less to do more.

Happy coding!

Related Posts

Leave a Reply

Your email address will not be published. Required fields are marked *