sskiles devblog

code generation ii

Okay, so we have a code generator. Let’s brag about it and tell people that this code wasn’t written by mortals. It was given to us by the machine. What could we say?

// This code was generated by a tool.

That doesn’t really say much. It just leaves people asking who the tool is. It is a nice warning to other developers about who to blame if they need to look at the code. There is a chance that if they are looking at the code, they are trying to fix a bug or add a feature, and they are not happy about it. But they can’t change the code because it was “given to us by the machine”.

// Do not edit this file manually.

Of course, that just makes us want to edit it manually, right? Telling someone not to do something is just asking them to do it. Let them know why they shouldn’t edit it. Something like:

// Changes to this file will be lost if the code is regenerated.

or this:

// Changes to this file may cause incorrect behavior 
// and will be lost if the code is regenerated.

I have to say, I really dislike these. It’s the uncertainty of “if” that bothers me. It implies that there is a chance that the code might not be regenerated, and that the changes might not be lost. That’s not very reassuring and it leads us to at least two questions:

Knowing these answers would help us understand the situation better. If the code is generated by a tool we know or we are already using, we have a head start. If we at least know that the code is generated by a specific tool, we can look up that tool and see how it works.

Was the code generated once at the beginning of the project X years ago and never again? Or is it generated every time the project is built? Only with a specific command? On Tuesdays?

Let’s give them a little more information. Let’s add in:

//     Generator: {generatorName}");
//     Version:   {generatorVersion}");
//     Timestamp: {generationTime}");

This gives us a lot more information about the code generation process. I jumped the gun a little by adding in the version, but it doesn’t hurt to be specific. The timestamp lets us know when the code was generated, so we at least have an idea of what triggers the code generation.

That’s quite a bit of information to give to the developer.

Now, let’s give our tools a little more information about what’s going on under the hood.

// <auto-generated />

This is a standard attribute that many code analysis tools recognize, and it helps them ignore issues when looking at generated code.

Let’s make sure to include this one last attribute in all of our generated code files.

[System.CodeDom.Compiler.GeneratedCodeAttribute("{generatorName}", "{generatorVersion}")]

This attribute is a little more specific than the auto-generated comment. This provides the same information as the user comments, but in a way that tools can use it programmatically. Static analysis and code coverage tools can utilize this attribute to ignore the generated code when analyzing the project. Some IDEs and code editors will also use this attribute to prevent manual edits to the file.

I hadn’t planned on this being so long, but here we are. So, let’s put it all together. Here’s what our code generator might look like:

var generatorName = GetType().FullName;
var generatorVersion = typeof(MyGenerator).Assembly.GetName().Version.ToString();
var generationTime = DateTime.Now.ToString("o");

var sb = new StringBuilder();
sb.AppendLine(@"// ---------------------------------------------------------------------------");
sb.AppendLine(@"// <auto-generated />");
sb.AppendLine(@"// This code was generated by a tool.");
sb.AppendLine(@"//");
sb.AppendLine(@"// Changes to this file may cause incorrect behavior and will be lost if");
sb.AppendLine(@"// the code is regenerated.");
sb.AppendLine(@"//");
sb.AppendLine($@"// Generator: {generatorName}");
sb.AppendLine($@"// Version:   {generatorVersion}");
sb.AppendLine($@"// Timestamp: {generationTime}");
sb.AppendLine(@"// ---------------------------------------------------------------------------");
sb.AppendLine();
sb.AppendLine(@"#nullable enable");
sb.AppendLine();
sb.AppendLine(@"namespace MyConsoleApp");
sb.AppendLine(@"{");
sb.AppendLine($@"    [System.CodeDom.Compiler.GeneratedCodeAttribute(""{generatorName}"", ""{generatorVersion}"")]");
sb.AppendLine(@"    public static class GeneratedCode");
sb.AppendLine(@"    {");
sb.AppendLine(@"        public static string GetMessage() => ""Hello from generated code!"";");
sb.AppendLine(@"    }");
sb.AppendLine(@"}");

And our generated code file might look like this:

// ---------------------------------------------------------------------------
// <auto-generated />
// This code was generated by a tool.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
//
// Generator: MyNamespace.MyGeneratorClass
// Version:   1.0.0.0
// Timestamp: 2025-08-01T20:29:37.1169220-05:00
// ---------------------------------------------------------------------------

#nullable enable

namespace MyConsoleApp
{
    [System.CodeDom.Compiler.GeneratedCodeAttribute("MyGenerator", "1.0.0.0")]
    public static class GeneratedCode
    {
        public static string GetMessage() => "Hello from generated code!";
    }
}

This gives us, and our tools, some nice information about the generated code.

Keep in mind that even though we are suppressing analysis and warnings in the generated code, we still need to make sure that we are analyzing the code without the suppressions on our own. I probably should have mentioned that earlier.

This is important because we want to make sure that the generated code is as clean as possible. This is code that is going to be used but not seen or maintained by the end developer. Any issues in the generated code may lead to bugs that are extremely hard to track down. We want to make sure that the generated code is as clean and maintainable as possible.

Don’t skimp on the comments and documentation. Throw in #pragma warning disable/enable around a statement that you know will generate warnings. Don’t just slap a #pragma warning disable at the top of the file and call it a day. Look at the lowered code to see what it’s actually doing and make sure that it’s doing it efficiently. Remember, we’re generating this code so that we never have to look at it again. If we have to look at it again, then we probably did something wrong.

I may revisit code generation in the future, but for now, I think this is a good start.

© 2026 Shane Skiles