Introducing Code Generation - Tying It Together: Implementation and Testing
(Page 9 of 13 )
The code you produce is totally “normal” .NET code. Because it’s code, you can do all the things you’d do to any other code, including source control. The code is “normal,” is compiled to MSIL, and runs with the full speed of .NET at runtime.
Your users pay absolutely no performance penalty because of your use of code generation, and in some cases you can even improve performance.
Everything from here on in your development will follow your current practices, except possibly testing. Development tends to be a zero-sum game, meaning that extra resources required on one task are taken from another. Too often, resources used by development problems or new features are taken from testing. I hope that as code generation speeds your development, you’ll have more time for testing. But I’m not optimistic about it.
You can manipulate generated code in familiar ways. It appears in the Class View just like any other code. You’ll put generated code under source control, and it can interact with other components of your application. Placing hundreds of similar classes derived from the same template under source control may appear to be a waste of resources. However, this allows you to back up and distribute generated code to your developers in the same manner as any other code. It also lets you to know exactly what edits were made if someone inadvertently edits generated code.
*********************
TIP Hold generated code to the same high-quality standards you use for the rest of your code, and treat it with the same care— including source control and testing.
Understanding the Five Principles for Code Generation To obtain the benefits of code generation, the five steps are closely linked to five principles. These principles are my non-negotiables for code generation:
- Principle #1: You have control of the templates that generate your code and can change or replace them as required.
- Principle #2: You collect metadata as a separate, distinct step with usable output that can independently evolve.
- Principle #3: You, or someone unfamiliar with project, can regenerate your code precisely as a one-click process—now or at any point in the future.
- Principle #4: You embrace handcrafted code by isolating and protecting it. Code generation is a supporting player to human programming and doesn’t overwrite files unless they were generated and haven’t been edited.
- Principle #5: The code-generated application is a high-quality application. It allows more effective testing, has equal or better performance, and is more easily maintained than a similar fully handcrafted application.
These may seem like challenging goals, especially because many previous attempts at code generation fell short. But with the tools .NET provides and the current level of understanding of architecture and development processes, you can fulfill all these principles and allow code generation to revolutionize your application development. Throughout the rest of this book, you’ll learn how to put these principles into practice.
You Have Control Over the Template
You’re in control of what you output. You or your organization remains the boss. Your decision to use my templates, your own, or someone else’s is independent of the decision to use code generation. So many nuances determine the “best” way to architect a particular project in .NET, and they change over time, that no one can tell you what architecture is best for your application without being familiar with your unique requirements.
*********************
NOTE I hope that we move to a world where there are a number of interchangeable templates available to you, and I talk about that in Chapter 11. But it’s important that (even if you’re using templates created by someone else) you can easily modify them or replace them if someone builds a better mousetrap. Interchanging templates from different vendors will require a level of standardization that isn’t yet available.
Control also means you’re in charge of quality and performance. The importance of performance differs phenomenally in different types of applications and even at different points in the deployment life cycle. You can focus your decisions on what’s best for the application, including quality and runtime performance where appropriate.
Metadata Is a Distinct Step
The metadata that defines your application details and the templates that define how your application runs are two distinct pieces. There are many advantages to separating them: Different people can work on each. You can separately debug them. You don’t have to re-create metadata (generally a slower process) when you’re testing templates. You can use dummy metadata or dummy templates to avoid delays early in your development cycle. But most important, you can move the templates (and your template-generating mechanisms) forward as a way to kick-start your next application. A portfolio of templates can evolve within your organization to reflect your current best practices.
Although metadata and templates are highly symbiotic, they evolve in response to separate pressures. The initial source of metadata is a project’s requirements, and the initial source of templates is the application’s architecture. If you work in a formalized development environment, different people are likely to develop these pieces—experts in understanding domains vs. the underlying technologies such as .NET. Because different pressures lead to changes, metadata and templates evolve on separate timelines. Metadata generally expands in response to new feature requests regarding business logic. Templates evolve when there’s an architectural change, generally because of changes in the underlying platform, language, security issues, best practices, or other technology issues. The metadata is unlikely to change just because you’re ready to write a new version of your Windows application for the Web, but the templates will certainly change. Conversely, templates won’t change when you add a new field, but metadata certainly will.
Implement One-Click Regeneration
Code generation isn’t a one-time thing. Most of your generated code will need to reflect evolving metadata and evolving templates. It can only reflect changes in these underlying sources if you can regenerate your code at any time.
Although one-click generation is convenient, the reason for committing to one-click generation isn’t convenience. When you use code generation, you should commit to it for the life of your project. Someone will need to regenerate your application to add a new field or some other feature. That may happen two or three years down the road, and you may have moved on to other projects. Even if you’re still around, will you remember exactly what you did during code generation in three years? The only way to ensure that anyone can exactly reproduce your code generation is to run it as a script initiated by a generation harness.
That’s one-click regeneration. And the benefits are a significant decrease in effort required for simple changes and an increase in application stability throughout your project lifetime.
Embrace Handcrafted Code
Handcrafted code is the code you still write line by line in an editor. This is the important stuff representing the unique aspects of your application. It cradles little aspects such as validation and the core algorithms that make up your application. To protect your handcrafted code during regeneration, you need to isolate it from autogenerated code. This isolation will generally occur by placing handcrafted code and autogenerated code in different files and incorporating the handcrafted logic through specific techniques such as inheritance.
Visual Studio doesn’t understand that the next regeneration will destroy any edits made in autogenerated files. Those edits are immediately doomed. Whether it’s three minutes or three years from now, unless you take special steps the next code generation will overwrite these edits. Don’t panic or run to the nearest bar because of images of some junior programmer editing a file three weeks into maintenance and not finding the problem until you regenerate six months later. You could make rules such as, “Thou shall not edit any file marked for generation and teach this to all thy children.” I don’t know whether that’s good enough to keep you sane, but it isn’t enough to let me sleep at night. I don’t want the long-term viability of projects dependent on whether everyone follows a set of rules. Chapter 3 shows how to insert a hash code into the file’s header so you can later check whether the file has been edited. If the file has changed, you can force the programmer to solve the problem before trashing the edits. You can even run this check across your generated code base periodically if you’re having nightmares about some fool editing your autogenerated code.
You have control over the implementation of this principle. Due to the intricacies of code generation, different rules are appropriate for different templates, and you’ll be able to tune overwriting behavior as part of the one-click script presented in Chapter 3.
Generated Code Has Great Quality, Performance, and Maintainability
I want to have my cake and eat it, too! I want all the benefits of code generation, and I don’t want it to affect my users. I want them to have an application with the fastest reasonable performance, highest quality, and long-term maintainability.
Code generation is a design-time feature and will have no negative impact on runtime performance. In certain cases, you might do something a little better with a code-generated application. For example, you can easily generate enums for your database column positions or base validation on database information such as providing a maximum length or database constraints. Although there will be variations because of different design decisions, the performance is generally identical between code-generated and handcrafted applications because the runtime code is nearly identical.
Software quality is often an elusive goal. You know that it means your software will do what is intended well without unexpected quirks (bugs). But getting to that goal sometimes seems to require effort that’s beyond the available resources. Code generation contributes to software quality, but only by amplifying other good practices. You can break down the process of achieving software quality into four basic areas: planning, implementing, testing/deploying, and maintaining. Code generation aids in the planning stage because it encourages iterative prototype-based requirements gathering. The code generation proto type is special because it’s capable of smoothly evolving into a robust permanent project element (unless it brings a design problem to light). During implementation, code generation significantly improves consistency of the code.
Code generation affects testing in two ways. First, because code-generated parts of your application are repetitively created, if you’re in triage mode, you can limit testing to a few examples of each pattern, rather than testing every class. I’m not suggesting this is a good idea, but triage is always a matter of picking the best of bad choices. The other way code generation affects testing is to automate unit and regression testing. You’ll learn in this book how to generate code and run complex scripts. In addition to combining these features for creating application code, you can combine them for testing. You won’t be able to do all of your testing, but you can cover the repetitive tests that are the hardest do manually. The key to this is building a specialized set of metadata geared to testing.
Code generation offers automatic benefits in maintainability. Many things that contribute to agility also contribute to maintainability. You can make common types of changes easily and propagate them throughout the application. But maintainability goes beyond this. Maintainable code is well organized and easily read by a human being. It has appropriate scope to help later programmers understand how things should be used. The application code is tracked in source control. Classes have meaningful extensible interfaces. Code generation doesn’t get you off the hook on any of these aspects of creating a quality application. It allows you to do it more easily because you can propagate quality, but careless use of code generation propagates sloppy code.
This chapter is from Code Generation in Microsoft .NET by Kathleen Dollard (Apress, 2004, ISBN: 1590591372). Check it out at your favorite bookstore today.
Buy this book now. |
Next: The Strongly Typed Dataset >>
More .NET Articles
More By Apress Publishing