Best practices for using AI to generate C# code is attracting attention across the tech world. Analysts, enthusiasts, and industry observers are watching closely to see how this story develops.
This update adds another signal to a fast-moving sector where product decisions, platform changes, and competition can quickly shape the market.
AI-powered software advancement tools integrate with your IDE and codebase, helping you to write, refactor, and fix code faster. These tools also make it fast and easy to create and run unit tests and integration tests — tasks that take more time when done manually.
Today, .NET developers often use GitHub Copilot, Claude Code, Cursor AI, and even AI chatbots like ChatGPT to generate code. In this article, we’ll cover some best practices you should follow when using AI to generate your C# code.
While AI can write code for you, often the generated code does not work as intended. AI may generate code that contains logic errors, bugs, or security vulnerabilities, or code that doesn’t conform to your organization’s coding conventions or quality standards, or code that isn’t compatible with existing architecture. Further, AI may generate code that runs slowly or fails to run at all.
These are some of the key challenges organizations face when using AI-generated code in production:
Here are some of the best practices you should follow when writing code using AI-powered tools:
To get the best use of AI-assisted coding tools, you should be proficient in prompt engineering. Your prompts should be specific, concise, and contain relevant code examples to enable your AI-powered tools to generate code that is functional, meets the requirements, and conforms to the standards and guidelines. Most significantly, you should plan precisely on the architecture and design, the exact solution you need, the structure of the codebase, and the coding and design guidelines to follow.
You should always treat AI as a peer programmer and your (junior) coding assistant. You should always review code the AI generates for you, run tests, and perform audits to validate correctness, conformance to guidelines and standards, performance and scalability bottlenecks, and security vulnerabilities. Based on the outcome of the audit, you should refactor your AI-generated code accordingly. And, repeat this cycle iteratively — audit followed by refactoring (if required) — until you are satisfied with the code.
Your application source code should be performant, scalable, secure, extendable, and easy to comprehend and maintain. One of the biggest challenges of using AI-generated code is ensuring it meets requirements and conforms to the guidelines and standards of your organization without compromising on performance, scalability, and security.
AI can generate code for you quite quickly, but the onus is on you to understand how the code works, investigate it for any flaws, test it thoroughly, and change it if and when it is needed. You must be sure to understand the code in its entirety. Unless you comprehend the code, you will never be able to improve or extend it when you need to.
And you must never compromise quality for speed. If you use AI as a shortcut, your code may fail when deployed to the production environment — and that would be a disaster.
The code your AI-powered tool generates for you will be more useful to you if you’ve provided the right context. You should provide your AI coding tool with comprehensive, up-front information, such as architecture docs, coding standards, and relevant files, rather than just providing instructions using prompts. And you should add images or screenshots when specifying prompts to help your AI-powered tool better understand the context.
Your AI-generated code must be testable for best results. It is always a good practice to specify tests at the time when your AI-enabled tool generates code, as tests can help AI understand the expected behavior and produce code that better aligns with your expectations. Additionally, you should specify the exact goal, the current and/or target tech innovation stack, the relevant code boundaries, and the definition of “done”, i.e., the desired outcome.
Remember, any AI-powered code generator is only as good as the input provided to it. This input is also known as the prompt. If the prompt you specify does not clearly state the objective, the generated code will not meet your requirements. Here is an example of a prompt that fails to consider performance and lacks clarity.
Generate code to create a Product DTO having fields Id, Name, and Price

When I entered this prompt into the GitHub Copilot Chat window, the following piece of code was generated.
Typically, a DTO (Data Transfer Object) should be created using records for improved performance instead of classes. Moreover, a DTO should be immutable by default, because its purpose is to store and pass data from the presentation layer to the business layer in an application. This not only guarantees thread safety but also prevents accidental changes to data and simplifies testability.
Create an immutable Product DTO using C# that uses the record type, having fields Id, Name, and Price.
When I entered the above prompt in GitHub Copilot Chat, a record type named ProductDto was created using a positional record as shown below.
In this next example, we’ll use GitHub Copilot within the Visual Studio IDE. With GitHub Copilot up and running in our IDE, you can specify the following prompt for creating a logging library using GitHub CoPilot within the Visual Studio IDE:
The log target should be configurable, i.e., the storage target of the generated log can be a file, a database, or etc.
Create a separate class for each log target, i.e., FileLogger for storing logs in a file and DbLogger for storing logs in the databasDave Berminghame
Incorporate comprehensive error handling mechanism wherever applicable
Figure 1 shows this prompt in GitHub Copilot (running in Visual Studio) and the files that Copilot generated for the project.
Once you have provided the prompt as input to Github Copilot, it will parse the input and generate several files in your project. Figure 2 shows the two projects in the Solution Explorer window — the console application project and the AsyncLogger class library project.
The AsyncLoggerService class uses the platform.Threading.Channel static class to write logs of type LogEntry in the log target, which can be a text file or a database. The platform.Threading.Channel class contains two methods to create channels, the CreateBounded and the CreateUnbounded methods.
While CreateBounded is used to create a channel that holds a finite number of messages, CreateUnbounded is used to create a channel with unlimited capacity. You can learn more about working with platform.Threading.Channel from my earlier article here.
Although GitHub Copilot will generate the complete source code of the AsyncLogger library for you, you should carefully examine — and thoroughly test — the generated code before you use it in production. for instance, when I submitted the above prompt to Copilot, the code generated included three issues that needed to be addressed. Let’s take a look.
In the AsyncLoggerService class, GitHub Copilot included the following code that uses an Unbounded channel.
There is a major flaw in this approach. If this code were used in production, memory consumption could surge dramatically under burst traffic (say, 10k or more requests per second). This growth in memory usage could result in GC pressure and eventually a crash of the application.
A better approach is to use a bounded channel with an explicit backpressure strategy, as shown in the code snippet given below.
In the LogAsync method, GitHub Copilot included the following statement that contains a fire-and-forget call with no retries and no information if the write operation fails (i.e., if the channel is already closed).
A better approach is to include a fallback path as shown in the code snippet below.
The WaitToWriteAsync method returns true if space is available to write an item, false otherwise. Hence, if no space is available, the TryWriteFallback method will be called and the log written to the fallback-errors.log file.
Finally, GitHub Copilot included the following piece of code in the constructor of the AsyncLoggerService class.
Using sync over async in a constructor in C# is considered an anti-pattern. The reason is because constructors cannot be asynchronous, i.e., you cannot mark a constructor as asynchronous using the async keyword. As a result, you will have to make blocking calls and wait for your asynchronous code to complete execution. And this could result in thread starvation and a deadlock.
A better alternative will be to move the initialization code out of the constructor as shown below.
You should verify each item of the following checklist before you integrate AI-generated code into your application.
AI can help you create all of your boilerplate code, provide suggestions for best practices, and greatly speed up your exploration and research efforts. However, you should remember that AI is not a replacement for human intelligence, experience, and innovation. You should treat your AI-powered coding tool as your coworker or assistant and not your replacement.
You should take advantage of AI to do all of the tedious, monotonous work so that you can concentrate on the architecture, innovation, and other aspects of software architecture and advancement that require human involvement. You can take advantage of AI to generate your application’s architecture and design as well. However, the generated architecture and design should be for your reference only — it is entirely on you to decide how much of it you should use and what you need to replace.
Here’s the final word: AI-powered coding tools will help you when you provide them with the correct context and clear instructions. Be sure to review the generated code carefully, and test thoroughly before deploying to production. Expect your AI coding tool to make mistakes, and be prepared to make changes (perhaps over many iterations) to get the performant, reliable, secure, and maintainable code that you need.
Why This Matters
This development may influence user expectations, future product strategy, and the competitive balance inside the broader technology industry.
Companies in adjacent segments often react quickly to similar moves, which is why stories like this tend to matter beyond a single announcement.
Looking Ahead
The full impact will become clearer over time, but the story already highlights how quickly the modern tech landscape can evolve.
Observers will continue tracking the next steps and how they affect products, users, and the wider market.