Skip to main content
SEMastery
Fundamentalsbeginner

6 Steps for Setting Up a New .NET Project the Right Way

A friendly, step-by-step guide to starting a clean .NET 10 project with the right folder layout, central packages, analyzers, EditorConfig, and CI.

12 min readUpdated September 22, 2025

A small story before we start

Imagine your mother is setting up a new kitchen. Before she cooks even one meal, she does some quiet, boring work first. She decides where the spices go. She labels the jars. She keeps the rice on one shelf and the dal on another. She puts a dustbin in the corner.

None of this is cooking. But because she spent ten minutes organising, every meal after that is faster and calmer. She never hunts for the salt. Guests can find a glass of water by themselves.

Setting up a new .NET project is exactly the same. The first thirty minutes feel boring. You are not building the real feature yet. But if you set up the "kitchen" properly, every day after that is smoother. Your code stays tidy. Your teammates know where things live. Mistakes get caught early.

This guide gives you 6 simple steps to set up a new .NET project the right way. We will use .NET 10, which is the current LTS (Long Term Support) version in 2026, with C# 14. You can follow along even if you are new.

The six setup steps, start to finish

Why bother doing this at all?

You might think: "I just want to write code. Why all this setup?"

Here is the honest answer. A messy start does not hurt you on day one. It hurts you on day sixty, when you have ten projects, three teammates, and nobody remembers which version of a library to use. The setup work is cheap now and very expensive later.

Here is a quick comparison of the two paths.

Without setupWith setup
Versions differ across projectsOne version, listed once
Every .csproj repeats the same settingsSettings live in one shared file
Bugs found late, in productionBugs caught at build time
New teammate is confused for daysNew teammate runs one command
"Works on my machine" problemsSame rules for everyone

Let us walk through the steps one by one.

Step 1 — Create the solution and your first project

A solution is a box that holds one or more projects. Think of the solution as a school bag and each project as a notebook inside it.

Open a terminal in an empty folder and run these commands. Each one does a small, clear job.

// These are shell commands, shown here so they are easy to copy.
// dotnet new sln -n MyShop
// dotnet new web -n MyShop.Api -o src/MyShop.Api
// dotnet sln add src/MyShop.Api/MyShop.Api.csproj

The first command makes the solution file. In .NET 10 with Visual Studio 2026 you can use the newer .slnx format, which is a much cleaner XML file than the old, bloated .sln. To get it, add --format slnx when you create the solution.

The second command makes a small web project inside a src folder. The third command adds that project to the solution so they know about each other.

From empty folder to first run

Solution
Project
Add to sln
Run

Steps

1

Solution

dotnet new sln

2

Project

dotnet new web

3

Add to sln

dotnet sln add

4

Run

dotnet run

What each command gives you

After this, type dotnet run --project src/MyShop.Api and you should see your app start. That is your "hello world" moment. Now we make it tidy.

Step 2 — Pick a clear folder layout

Where you put files matters more than people think. A good rule, recommended in the Microsoft docs and used by many real teams, is to split your code into two top-level folders:

  • src/ holds your real application code.
  • tests/ holds your test projects.

This separation is simple but powerful. Anyone who opens your repository instantly knows where the product is and where the tests are. Build scripts can target one folder. New people are not lost.

A clean repository layout

A common beginner mistake is to dump every project in the root folder. It works for one project. It becomes a mess at ten. So set up src and tests from day one, even if each has only one project for now. It is the labelled spice jar idea again.

Here is a small table of what each project type usually means, so the names are not scary.

ProjectWhat it holdsExample name
Api / WebThe entry point users talk toMyShop.Api
DomainYour core business rulesMyShop.Domain
InfrastructureDatabase, email, outside systemsMyShop.Infrastructure
TestsCode that checks your codeMyShop.UnitTests

You do not need all of these on day one. Start small. But knowing the pattern helps you grow without chaos.

Step 3 — Use Central Package Management

This is the step that surprises people the most, because it solves a pain you may not have noticed yet.

Normally, each project lists its own NuGet packages with version numbers inside the .csproj file. With three projects, you might accidentally use version 9.0.1 of a library in one and 9.0.3 in another. These tiny differences cause real, confusing bugs.

Central Package Management (CPM) fixes this. You list every version once, in a single file at the repository root called Directory.Packages.props. Then your project files only say which package they want, not which version.

Create Directory.Packages.props at the root:

<Project>
  <PropertyGroup>
    <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
  </PropertyGroup>
  <ItemGroup>
    <PackageVersion Include="Serilog.AspNetCore" Version="9.0.0" />
    <PackageVersion Include="xunit" Version="2.9.2" />
  </ItemGroup>
</Project>

Now in each project's .csproj, you reference the package without a version:

<ItemGroup>
  <PackageReference Include="Serilog.AspNetCore" />
</ItemGroup>

The version is gone from the project file. There is exactly one place to change a version, and every project follows it. This feature is built into NuGet (6.2 and later) and is the standard way to manage versions in modern .NET.

How CPM resolves a version

Project asks for package
Look in Directory.Packages.props
Apply the listed version
Restore

Steps

1

Project asks

No version in csproj

2

Central file

Find PackageVersion

3

Apply

Use that version

4

Restore

Same for all projects

One source of truth for versions

One tip for safety: a couple of well-known libraries like MediatR and MassTransit are now under a commercial license for many uses. They are still good tools, but check the license before adding them to a paid product. With CPM you at least have one clear place to see and review every dependency you pull in.

Step 4 — Share build rules with Directory.Build.props

You have centralised versions. Now centralise settings.

Every .csproj usually repeats the same lines: which .NET version to target, whether to turn on nullable warnings, and so on. Copying these everywhere is tiring and easy to get wrong.

Directory.Build.props is a special MSBuild file. When you build, MSBuild automatically looks up your folders, finds this file, and applies it to every project below it. You write the settings once, and all projects inherit them, just like house rules everyone in the family follows.

<Project>
  <PropertyGroup>
    <TargetFramework>net10.0</TargetFramework>
    <LangVersion>14.0</LangVersion>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
  </PropertyGroup>
</Project>

Let me explain the important ones in plain words.

  • Nullable enable turns on warnings when a value might be null. This catches one of the most common crashes in C# before it ever runs.
  • TreatWarningsAsErrors makes the build fail on warnings. This sounds harsh, but it keeps small problems from piling up. A warning ignored today becomes a bug next month.
  • ImplicitUsings removes the need to write the most common using lines in every file. Less noise, cleaner code.
How MSBuild finds and applies Directory.Build.props

Now adding a new project is almost free. It picks up all the rules automatically. No copy-paste, no drift.

Step 5 — Add analyzers and an EditorConfig

So far we have set up structure. Now we add a helper that reads your code and gently corrects you, like a kind teacher checking homework.

Analyzers are small tools that run during the build and spot problems: unused variables, risky patterns, things that look like bugs. Popular free ones include Meziantou.Analyzer, Roslynator, and SonarAnalyzer.CSharp. You add them like any package (and with CPM, you list the version once).

<ItemGroup>
  <PackageReference Include="Meziantou.Analyzer">
    <PrivateAssets>all</PrivateAssets>
  </PackageReference>
  <PackageReference Include="Roslynator.Analyzers">
    <PrivateAssets>all</PrivateAssets>
  </PackageReference>
</ItemGroup>

The PrivateAssets>all part just means "use this while building, but do not ship it to people who use my library."

The second helper is .editorconfig. This is a plain text file that describes your code style: how many spaces to indent, whether to use var, how to name things. The wonderful part is that almost every editor (Visual Studio, VS Code, Rider) reads it automatically. So everyone on the team writes code the same way, even if they have different settings on their own machine.

A tiny example:

// .editorconfig (shown as text)
// root = true
//
// [*.cs]
// indent_style = space
// indent_size = 4
// dotnet_diagnostic.CA1822.severity = warning

That last line shows something powerful: you can set how serious each analyzer rule is. You can make some rules just suggestions and others hard errors. You tune the strictness to fit your team.

What happens when you build with analyzers

You write code
Build runs
Analyzers check
EditorConfig style check
You get hints or errors

Steps

1

Write

Save your file

2

Build

dotnet build

3

Analyzers

Find risky code

4

Style

Apply editorconfig

5

Feedback

Fix before runtime

Feedback before the code ever runs

The goal here is simple: catch mistakes at build time, not in front of a user. The earlier you catch a problem, the cheaper it is to fix.

Step 6 — Set up Git and a simple CI pipeline

Your project is now clean and consistent. The last step is to protect it.

First, Git. Run git init and add a .gitignore file so you do not accidentally commit junk like the bin/ and obj/ build folders. The easy way is dotnet new gitignore, which creates a ready-made file tuned for .NET.

Second, CI (Continuous Integration). CI means a robot on the internet that builds and tests your code every time you push. If something is broken, you find out in minutes, not when a user complains. GitHub Actions is a popular, free choice.

Here is the idea of a basic pipeline in plain steps:

StageWhat it does
RestoreDownloads the packages
BuildCompiles with all your strict rules
TestRuns everything in tests/
PublishOptionally packages the app
The CI pipeline on every push

Because of the earlier steps, this pipeline is strict and trustworthy. Your TreatWarningsAsErrors setting means a sloppy warning fails CI. Your analyzers run in CI too. Your central versions mean the CI machine builds exactly what you build. Everything connects.

A small but important habit: commit your Directory.Build.props, Directory.Packages.props, and .editorconfig to Git. They are the rules of the kitchen, and everyone who clones the repo should get them automatically.

Putting it all together

Let us zoom out and see how the six pieces support each other. Each step makes the next one stronger.

How the six steps reinforce each other

None of these steps is hard on its own. Together they turn a random pile of files into a project that is pleasant to work in for years. A new teammate can clone it, run dotnet build, and trust that the rules are already in place.

Remember the kitchen. Ten quiet minutes of labelling jars makes every future meal easier. Thirty quiet minutes of project setup makes every future feature easier. That is the whole idea.

Quick recap

  • Step 1: Make a solution and your first project. Use .slnx in .NET 10 if you can.
  • Step 2: Split code into src/ and tests/. Decide the layout early.
  • Step 3: Use Central Package Management so every version lives in one file.
  • Step 4: Put shared settings in Directory.Build.props (nullable, target framework, warnings as errors).
  • Step 5: Add analyzers and an .editorconfig to catch bugs and keep one style.
  • Step 6: Set up Git with a .gitignore, and a simple CI pipeline to build and test on every push.
  • Target .NET 10 (LTS) with C# 14 for new projects in 2026.
  • Watch licenses: tools like MediatR and MassTransit are now commercially licensed for many uses.
  • The setup is cheap now and saves a lot of pain later. Treat it like labelling your kitchen jars.

References and further reading

Related Posts