π§π· Prefer reading in Portuguese? Read the Brazilian Portuguese version.
Today, Visual Studio is staying closed.
On Day 01 of my 30 Days of .NET challenge, we explored what happens behind a dotnet run and got to know the .NET CLI. On Day 02, we opened up a .NET project to better understand Program.cs, .csproj, bin, obj, and some of the configurations that make our project work.
So today I decided to put those two things together and take on a little challenge that reminded me of the days before AI: create, build, run, test, and publish a .NET application using only the terminal.
No play button, no right-clicking the Solution, no Build menu, and no Visual Studio doing things for me.
Just me, the terminal, and the .NET CLI. Just like Neo from The Matrix.
Before we start, what can we create?
On Day 01, we learned that the .NET CLI (Command-Line Interface) is a tool that allows us to interact with .NET through written commands and that it comes with the .NET SDK.
One of the first commands we encountered was dotnet new. But before creating anything, we can find out which templates are available on our machine using the dotnet new list command.
The result shows a HUGE list of templates we can use to create different types of .NET projects and files. Among them, we can find Console applications, Class Libraries, test projects, web applications, and many others. We'll explore the differences between them in the not-too-distant future.
So when we type dotnet new console, console isn't just some random word the CLI understands. It's the short name of a template that's installed and available to dotnet new.
For our challenge, we're going to create a Console application called Dia03 using the familiar command dotnet new console -n Dia03.
The -n option is short for --name and defines the name of what we're creating. After running the command, we can navigate into the folder. And that's it, our project was created without opening an IDE.
dotnet build β let's see if this compiles
Now we have code, but we need to transform it into something .NET can execute. That's where our dotnet build from Day 01 comes in.
When we execute this command, .NET uses MSBuild to build the project and its dependencies. The C# code goes through the compilation process we saw on Day 01, and the resulting files start appearing in that
binandobjstructure we explored on Day 02.
By default, the build uses the Debug configuration, so we can find our files in a structure similar to this:
But we can also choose another configuration by using Release: dotnet build -c Release.
The -c option is short for --configuration, and in this case, we're requesting a build using the Release configuration.
In simple terms, Debug is the configuration designed for application development and debugging, while Release is the configuration used when we want a build optimized for distribution. Look how it adds folders to obj and bin.
So dotnet build isn't running our application. It's building our project and checking whether the code can be successfully compiled. If there's a lovely compilation error somewhere along the way, it'll let us know.
dotnet run β we already know this one
After three days of talking about .NET, dotnet run is practically becoming part of the family and joining us for Sunday lunch.
This command runs our application and, when necessary, performs the build process beforehand. If you want to know more about this build process, you can also read Day 01.
dotnet test β now let's make QA happy
We have an application that we can create, build, and run, but we're still missing something important: 90% test coverage tests.
If we try to explore dotnet test in our Console project, we don't have any tests to execute. So let's use the CLI itself to create a test project.
Going back to the folder where our Dia03 project is located, we can execute dotnet new xunit -n Dia03.Tests to create this project. I won't go into the details of this command today.
Now we have two projects:
The xunit template creates a test project using xUnit, a testing framework for .NET. It provides the tools and conventions we use to write automated tests in C#, especially unit tests.
Since we want our tests to be able to access the project we're testing, we add a reference between them:
dotnet reference add ../Dia03/Dia03.csproj --project Dia03.Tests
The reference is registered in the test project's .csproj through a ProjectReference, very similar to what we saw yesterday when we added a NuGet package.
Now we can run our tests and see the result.
The command builds the necessary projects and executes the tests found in the test project. At the end, we get the results directly in the terminal, showing how many tests passed, failed, or were skipped.
I'm not going to dive into xUnit and test structure right now because that deserves its own day. What's important here is that we managed to create the test project and run it without opening Visual Studio's Test Explorer.
dotnet publish β we're done, now we need to deliver
So far, we've created our project, built it, run it, and executed the tests. Now we need to prepare our application to leave our machine using dotnet publish.
One question I've had before is: if dotnet build already compiled my application, why does dotnet publish exist? Both commands do go through the build process, but they serve different purposes.
dotnet build focuses on building the project, generating the binaries needed for development and code validation. On the other hand, dotnet publish prepares the application and its dependencies so it can be deployed to another environment.
We can publish our application in Release using the command dotnet publish -c Release and see a folder similar to this:
The
publishfolder contains the files that are part of our application's published output. So a simple way to think about it is thatbuildanswers something like "Can I build this project?", whilepublishanswers "Which files do I need to prepare to distribute this application?"
It sounds like a lot and it is, but the CLI itself can help us when we're desperate.
Don't memorize everything, ask the CLI itself
So far, we've used several command options that look like random letters: -n, -c. And the list gets much longer as we start exploring the CLI. Eventually, we end up typing things without even knowing why. That's not what we're aiming for anymore, right? The good news is that we don't need to memorize all these commands and options.
We can ask for help directly in the terminal using dotnet --help or, even better, ask for help with a specific command:
dotnet new --help
dotnet build --help
dotnet run --help
dotnet test --help
dotnet publish --help
This shows the command's description, its arguments, and the available options. For me, knowing that an option exists and knowing where to find it is much more useful than trying to memorize fifty parameters I might use twice a year.
From zero to publish without Visual Studio
We started the day without opening Visual Studio and went through practically the entire basic lifecycle of our application using only the .NET CLI. That doesn't mean I'm going to uninstall Visual Studio tomorrow and start developing everything through CMD (God forbid). The IDE exists precisely to make our lives easier and automate many of these tasks. Thank you, Microsoft.
But there's a huge difference between letting a tool do something for us and not knowing what it's doing for us. We could spend hours philosophizing about this and the use of AI, but let's save that conversation for a coffee.
When we click Build, run tests through Test Explorer, or publish an application using the Visual Studio interface, there are .NET tools and commands working behind all those conveniences. Knowing the CLI helps us better understand this process and also makes a big difference when we leave our own machines and encounter environments where there isn't a pretty button to click, such as CI/CD pipelines, containers, and servers.
Visual Studio got some rest today, but it can open again tomorrow.
References
- .NET CLI overview β Microsoft Learn and its command-specific subpages












Top comments (0)