Does Generated Code Lead to Poor Design?
I was using a code generator to create my data layer recently and I wanted to change how the data layer was working. Each entity inherited from a base class that provided some plumbing-type functionality and the change that I wanted to make was in the base class. The problem was that I didn’t want to change the base class, because that would mean changing a bunch of other classes that also used the base class, as it would have changed a public interface, so it would have been messy. Even though the change that I wanted to make might be beneficial in the future, and it certainly wouldn’t be detrimental, I still avoided making the change out of laziness. What I did instead was changed my generated class template file to modify the functionality in each generated class, thereby introducing a ton of duplicate code, but since it was generated, I didn’t really care. I mean, who cares how bloated your code is, if its no harder to maintain than a single template file, right? Right?
This makes me wonder if code generation leads to poorly designed libraries. My first foray into code generation was at my previous job where we made a code generator for our custom o/r mapping library. Because we had built the o/r mapping functionality without thinking of code generation, our generated classes were very lean, because we used to hand code them and that gave us the incentive to put as much functionality as possible into the base class to save our hands from extra typing. By the time we got the code generator up and running, our templates were pretty trivial, the generator didn’t have to do much, it was basically just writing out getters and setters for some properties.
If we hadn’t had the incentive to offload as much functionality as possible into the base class, our resulting classes would have been much more cumbersome and potentially harder to debug. As it was, our base class was very well tested because it was put through the motions many, many times by each business object, it was nice to know that we had a solid base class to derive our simple business objects.
Anonymous said,
Wrote on December 13, 2005 @ 4:33 am
Code generation is a tool and like any other tool it can be abused.
However, judging from the many occasions I’ve seen code generation abused, I guess you do have a point there. The fact that you can easily duplicate functionality creates, ever too often, classes that are hardly “designed” and bloated. When this fact is pointed out the reply is ‘hey, it is generated anyway so why do you care”.
Recently, I’ve spent more than a half a day sitting with developers trying to refactor dependecies out of generated code when the development team suddenly realized that they would want to use the generated code in (several) other areas - and still, during that session I got that reaction…
Anonymous said,
Wrote on December 13, 2005 @ 5:04 am
Generated code should look like code that you would have typed in otherwise. This actually increases the work to be done in both templates and compiled base classes (if you use base classes, but it’s generally a good idea to do that).
In the past 2 years I’ve generally just moved code from generated code to the entity base class and added a lot of methods to the base class so people could tap into the various states the object as a whole will go through. I learned that if you look at a template, and the code in there isn’t relying on stuff only available in the generated code assembly (like a generated factory class for example) you should move that code to the base class and see if it needs extra methods so developers can extend it (On… and On..Completed stuff).
Though it’s often hard to move code to the base class, because it’s for example very specific to the actual class. To make such code ‘generic’ you have to store data in the base class and run that through general code. A good example is PK-FK synchronization code (save Customer, PK of customer is synced with the related Order entities). That is, if you want to use your entities in a disconnected manner of course. The downside of storing data to be used in generic code is that it eats up memory when you load a lot of the entities into memory. Code is shared among all instances, it’s therefore ‘cheap’, compared to expensive memory footprints. Furthermore, storing data is slow, even if you store it in hashtables. Running a special piece of code is much faster.
The last point you should consider is that moving code to the base class can make the code look obscure if you’re not careful. Say, you have entities which have a Save method of their own. To be able to perform the save, they need a DAO class. Now, you can add the Save method (or an override) to the generated class, create teh DAO there and call into it, or you can move the method to the base class and simply add an abstract method CreateDAO() to the base class, which is overriden in the generated code. This is a simple example with a short path from generated assembly to generated code, but it makes the code a bit more complex, because the complete code ran at runtime is not in 1 assembly but in 2: the generated code and the compiled base class.
Still it can be a great way to reduce generated code and therefore keep the overall assembly pretty small, but at the same time, keep in mind that the more code you move to the base class, the more tricks you have to pull to get access to classes which are generated from the base class, because you don’t have access to that code in the base class.
I always first write the code which has to be generated by hand, then move it into a template. This already reduces the amount of code to generate and it saves a lot of time, because developing software with a template is cumbersome: every typo, every mistake you make, it takes relatively long to see the result and change it, left alone debug it.
Anonymous said,
Wrote on December 13, 2005 @ 8:35 am
dhayden said,
Wrote on December 13, 2005 @ 9:54 am
I know you don’t believe that
You hit the problem at the beginning: “The problem was that I didn’t want to change the base class…” This sounds like the right solution. Don’t change your templates to introduce code smells. Spend the time now to create well-written code and you can generate it on every project.
You have run into the age old concern about Interfaces. They are tough to version / change. Be careful using them as they may cause you a lot of work later.
Your refactoring tool should be able to help you with a lot of these changes. There are various refactorings that could probably automate most of the changes.
I know you have played with Codus. Codus uses abstract base classes with templates to generate concrete classes that is all very well refactored. No code bloat there that I could see.
Good luck.
breichelt said,
Wrote on December 13, 2005 @ 10:59 am
Frans, thanks for the comments, you bring up some good points about making the base class more complex to test and debug.
David, I know, I dont believe it
Code generation wont lead to bad design if people arent lazy like me.