I was skeptical. One simple instruction to upgrade .net version, five minutes, and zero errors. If you’ve been on the fence about AI-assisted development, this might be the nudge you need.
Before you start
- VS Code with the GitHub Copilot extension installed. Agent mode is available in stable VS Code, no Insiders build needed.
- A paid Copilot subscription: agent mode consumes premium requests and the free tier runs out mid-task. See plans.
- .NET x SDK: SDK installed locally so builds and tests run. Project committed and clean so you can diff and roll back.
- A working test suite: without tests, Copilot can’t verify its own changes. This is what makes the whole flow trustworthy.
The setup
I’ve been meaning to upgrade a project to a newer .NET version for a while. It’s not complicated work, but it’s the kind of task that’s easy to keep pushing down the backlog: touch a few .csproj files, check the config, update the pipeline, re-run tests, fix whatever breaks. It’s not hard.
So I thought: let’s see what GitHub Copilot does with it. My entire instruction was:
“upgrade project to .net 9”
That’s it! Only 1 sentense.
What Copilot Actually Did?
It ran in agent mode, used Plan mode first to scope the work, then executed across the full project. Here’s what it touched:
- .csproj files: Updated the target framework across the main project and all relevant test projects.
- Config files: Adjusted configuration references and runtime settings that changed between versions.
- Bicep & deployment pipeline: Updated the Azure Bicep templates and CI/CD pipeline YAML to target .NET This one I really didn’t expect it to pick up on.
- Removed legacy packages: Cleaned up System.Net.Http and System.Data.SqlClient, and updated test helpers to match. More on this below.
- Ran the test suite: It didn’t just make changes and stop. It built and ran tests to verify nothing was broken before wrapping up.
The Interesting Part: Cleanup
The upgrade itself was expected. What stood out was that Copilot proactively removed two packages I hadn’t asked it to touch, with solid reasoning for each.
System.Net.Http On modern .NET, HttpClient is part of the shared framework. The explicit NuGet package reference is not just unnecessary, it’s a potential source of transitive dependency noise. Copilot spotted it was only listed in one .csproj and wasn’t backed by any actual source usage. Gone.
System.Data.SqlClient This one is more nuanced. The project was already using Microsoft.Data.SqlClient (the actively maintained, cross-platform client) in DatabaseService.cs. Having both on the dependency graph creates ambiguity: you can end up with subtle type-mismatch issues where SqlException from one namespace isn’t the same type as SqlException from the other.
Pitfall: After removing System.Data.SqlClient, one test helper was still hardcoding System.Data.SqlClient.SqlException. Copilot caught this, updated it to Microsoft.Data.SqlClient.SqlException in FailureFromDatabaseService.cs, and aligned the expected exception messages in StoredProcedureExecutionResultsInFailureTests.cs. Tests passed after the fix. This is exactly the kind of small thing that slips through in manual upgrades.
Plan Mode: Worth Using
Before making any changes, Copilot laid out what it intended to do. This is the part I’d recommend not skipping, especially in a team setting. It gives you a chance to sanity-check the scope before anything gets written to disk.
Observation: Plan mode is particularly useful when you’re not 100% sure what “upgrade” means in context. Copilot’s plan surfaced that it would touch the Bicep files and pipeline, which I might have forgotten to include myself. Seeing that upfront builds confidence before you let it run.

On bypassing approval prompts
I enabled “bypass approvals” because the default flow, since approve every individual file change creates a lot of friction for a task like this. Once you’ve reviewed the plan and trust the scope, approving file-by-file adds little value and breaks the flow.

Time and effort
The whole thing took about 5-10 minutes and I took a break. Could I have done it manually faster? Possibly, on a small project. But the value isn’t purely speed. It’s that Copilot didn’t just change the version number and call it done. It reasoned about the full project graph: tests, infra, pipeline, and legacy dependencies. Doing that manually with the same level of thoroughness would have taken longer and would have been more error-prone especially for a project I wasn’t familiar with.
Wait, What about the .NET upgrade assistant?
If you’ve used the .NET Upgrade Assistant before, you might wonder why it’s not mentioned here. The short answer: it no longer exists as a standalone tool. Microsoft officially deprecated it in late 2025 and now points developers directly to GitHub Copilot instead.
The Upgrade Assistant was free, deterministic, and rule-based. It followed a fixed sequence of steps and worked well for straightforward .NET version bumps. What it couldn’t do: touch your pipeline, clean up your infra files, reason about your test helpers, or proactively remove legacy packages. Copilot does all of that because it understands the full project context, not just the .csproj file.
The tradeoff is cost. The Upgrade Assistant was free. GitHub Copilot requires a subscription. If your team already has Copilot Business or Enterprise, you get this for free as part of it. If not, it’s a factor worth considering before you commit.
What does this mean for the future of development?
Mark Zuckerberg is shipping diffs again after 20 years away from code. Garry Tan is writing code again after 15. People who stepped away from hands-on engineering are stepping back in because AI agents removed the friction that made it impractical. The same thing is happening at the codebase level: you no longer need to own a service to safely work on it.
The floor for developer work is rising
Version upgrades, config changes, dependency updates, feature requests where previously sprint tickets. Now they’re prompts. Human value changes to “context providers”, architecture and code approvals.
Speed without tests is a trap
This worked because the scope was clear and the test suite verified the output. Remove either and the outcome will change.
Conclusion
Genuinely surprised by this one. It picked up Bicep and pipeline changes I didn’t ask for, cleaned up legacy tech debt, caught its own breaking change in the tests, and verified everything passed before wrapping up.
The caveat: the clearer the task, the better the result. Use Plan mode and make sure your tests are in place. But don’t let that stop you, the bar to get started is low.
Just try it: Stop procrastinating and hand it to Copilot. Review the plan, enable bypass, go get a coffee. You’ll come back to green tests and a clean build.
