|
|
||
Tom Ball's BlogOctober 2004 ArchivesThe New Golden HammerPosted by tball on October 19, 2004 at 11:48 AM | Permalink | Comments (7)I think all Java developers have at one time or another fallen in love with a new technology and wanted to use it to solve all problems; after all, when you have a "golden hammer", every problem starts looking like a nail. Remember when Java Reflection was all the rage when it was first introduced? All of Java's perceived ills could be solved with it, provided one ignored how slow and unreadable the solution using it was. Now the "first love glow" has subsided, and reflection is now an important but specialized technology in our toolkits. With the release of Tiger, annotations are rightly getting developers excited, but its companion tool, apt, is starting to look a little too golden in my view. Last week, it was suggested that apt should replace javac, and while I applaud that sort of "out of the box" thinking, others are taking a cool thought experiment too literally. Users have already complained on the NetBeans forums about how its upcoming release doesn't support this "feature", even though the apt Mirror API doesn't support code types that a compiler needs. I think what bothers me about wild apt scenerios is that folks are forgetting (or ignoring) the problem annotations are trying to address: that much of the specification for a class is done outside of its source code in other files, or is done in repetitive boilerplate code that has little to do with the class's problem domain. Information about a class is called metadata, and annotations allow developers to define metadata without external files (other than new annotation type declarations), special naming patterns or other error-prone hacks. Java developers have been creating ad-hoc solutions to this problem for years. If you look at the Swing sources, for example, you'll find undocumented "@beaninfo" Javadoc tags, which a custom doclet in its build uses to create its BeanInfo classes (XDoclet is an excellent tool that takes the grunt work out of such metadata file-generation doclets). Another tool that adds pre and post-condition checking via custom doclet tags is JContract. The problem with these approaches is that, like all comments, metadata stored in comments can drift away from the source if not carefully tracked by its developers. It's also fragile, in that this metadata isn't checked for typos; for example, try misspelling "@deprecated" in a doc-comment and notice how the compiler just ignores it as comment text, instead of either recognizing it or reporting it as an error. The Java 1.5 annotation support doesn't fix all metadata problems, but it helps in several ways. First, annotations are true Java types whose definition and usage are verified by the compiler. This makes it much harder to break metadata with a typo, but more importantly it means that if you enhance the definition of an annotation type and forget to update a class that uses it, the compiler will catch that error. I'm very partial to letting the compiler do as much project validation as possible (followed by my unit test suite). It also allows metadata to be discoverable in a standard way in classfiles and at runtime, so that tools like deployment tools or introspectors don't have to use external files and classes. What makes apt and annotations so exciting to me is that anyone can write an annotation processor as easily as an XDoclet module, writing metadata using a verifiable Java language facility instead of comment text. So instead of trying to see how apt can replace existing tools, look at all the metadata your project has and see how it can be better handled using annotations. If your classes take more than one file to define (such as a factory or "info" class), has boilerplate code in it or doc-comment tags that aren't documentation-specific, consider defining it using annotations. If you need a compiler, stick with javac or jikes. New Tricks for Old DogsPosted by tball on October 12, 2004 at 12:04 PM | Permalink | Comments (14)"You can't teach an old dog new tricks." Like many senior developers (old dogs), I've been quite happy and productive with Emacs or vi. I could use an IDE if absolutely necessary, but since my management pays me to be productive that always meant going back to my core tool set when the pressure was on. One reason Emacs (and, to a lesser extend, vi) remained such a productive tool is because I could always configure it to support my work environment. It didn't matter what the project language, source control system, or build tool was -- it just worked. It also didn't impose any of its own abstractions on the project, such as proprietary project file types. "Wait, Tom, your biography says you are working on NetBeans!" That's true, but a couple of years ago I couldn't use it in my daily work, nor could the rest of my project team at the time. We had a short list of reasons why: it was too slow, the GUI was counter-intuitive, the binary project type couldn't be shared, and the code-completion database wasn't useful since we always ran a pre-release JDK. But the NetBeans team made huge strides with performance in 3.5 (and the work continues), 3.6 saw a very slick windowing system update, and 4.0 has an Ant-based open project system I really like. Still, I did my NetBeans development using Emacs until a few months ago. What changed things was a very small feature you probably won't find in the marketing blurbs: when editing Java source, you can right-click on any identifier (such as getToken in this example) and go to its source, its declaration, or search for its usages in other source files:
![]() "Wait, Emacs can do that!" the diehards might shout, and to a certain extent that's true using its tags facility. But there are two reasons why I have never used tags for day-to-day source editing: tags are not syntax-aware, and the tags file needs to be regenerated to stay in sync with the source. You can regenerate tags files in your build, but I never figured out how the Emacs compile command can be tweaked to notify the tags facility that its database has been updated. I'm sure it's just a small amount of ELisp code, but at the end of the day my managers want working Java code, not ELisp. NetBeans 4.0 addresses both of these issues. First, it leverages javac to provide a very accurate syntactic and semantic map of source files -- it knows what type of identifier my mouse is clicking on and where the source is that resolves it. Second, every time I start up NetBeans, it rescans all my source and class files to rebuild its repository, so that map never gets out-of-date. Code completion is another IDE feature I never found useful because the database the IDE used for lookup was always out-of-date. But an accurate, complete mapping becomes very addictive to this old dog, causing me to bring the list up even for methods whose parameter list I am pretty sure of: it takes less than a second to get confirmation I remember it correctly, rather than risk a build error later. Oh, but the NetBeans editor uses javac for its background error checking, so all I have to do is stop typing for half a second and any errors will be underlined in red. What's the problem? Just move the mouse over the underlined area, and the javac error text is displayed as a tooltip: ![]() These two features alone are responsible for cutting the number of my edit/build cycles down significantly. I understand that other Java IDEs have similar capabilities, so I am not claiming that NetBeans is the only answer here. But the features now starting to appear in these tools are leaping past what Emacs and other older editors could ever offer. Pretty GUI tricks may not interest old developer dogs, but simple productivity gains like these make us sit up and notice. The Problem with Unit TestingPosted by tball on October 08, 2004 at 12:47 PM | Permalink | Comments (3)"Writing tests is QA's job." I've heard that line ever since I started being paid to program over twenty-five years ago. It wasn't just from other developers looking to cut their workloads, but also from my management, QA engineers and their management. Since QA is responsible for product testing, unit tests fall within their jurisdiction. Go back to coding, Tom, and leave the testing to the professionals. When Kent Beck started promoting a test-first best practice as a part of eXtreme Programming, lots of testing experts weighed in on the importance of full-coverage and other metrics in unit tests, invasive strategies for testing private methods, and other heavyweight procedures to ensure test "compliance". My reaction, like most developers, was summed up elegantly by King Arthur in Monty Python and the Holy Grail: "It's the legendary black beast of Aaaaauuuugghhhh! Run away! Run AWAY!!!" Remember how at the circus there are the trapeze artists that use a net, and the "daring" ones that don't? As a kid I noticed that the non-daring ones actually did much cooler, more difficult tricks, because they had the net! Without the net, the artist has to be more careful and circumspect in his work, or make a big (possibly final) splat in front of his fellow artists. Like most developers, I hate making a big splat (breaking the build, introducing showstopper bugs, etc.) in front of my peers. It's a good aversion to have when working on a team, but it can lead to paralysis if you lack confidence that your changes are correct. As a development engineer, all I want from unit testing is a safety net, not a huge, QA-approved test monster (for awhile, JavaSoft only had the JCK to do automated testing, which required a very large disk partition and took hours to run). So over the years, I've built several safety nets, keeping them hidden and avoiding names with words like "test", "quality", "verification", etc. The problem with a single tool as a safety net is that like all software it grows over time and becomes its own maintenance headache (to my horror, management learned of one of my test tools and shipped it!). The advantage of a safety net approach is that it allows me to focus on those areas where I'm most likely to "fall": difficult algorithms, past regressions, murky designs. Shining a unit test light on the latter has proven especially important, as writing tests for some feature I haven't given enough thought to tends to highlight the bad aspects of my design. It also shows testability problems which seem to me to be directly related the cost of maintaining that code. Testable code is maintainable code, and like most developers I would rather write new code than maintain old. But a safety net approach differs from full testing in that it is much more lightweight practice, which takes less time for me (as a developer) to implement and enhance. Unlike traditional testing approaches which can hurt my individual productivity, having and enhancing my safety net improves my productivity, because like a trapeze artist, I am able to implement more impressive changes to my code without fear of breaking it. These days, I'm a happy JUnit user who finds JUnit's green bars to be a great mood and productivity enhancer. My safety net of unit tests is very incomplete from a QA perspective, but it continues to improve along with my current project.
So what's the problem with unit testing? Unit tests are not tests, but rather a developer's safety net. QA doesn't own unit testing, developers do, and it's an important tool to be shared with your fellow developers on any project. Rename this engineering best practice, and it will be perfect.
| ||
|
|