Thunderclap, the Newsletter of Rolling Thunder
Volume 4, Number 2 Spring 2002
In this issue:
Feature Article: Design Tips for .NET XML Web Services: Performance, State Management, and Security
New Book: Second Edition of Introducing Microsoft .NET
Blatant Self-Promotion: New Training Class on .NET, on XML for Insurance, Discount for Canadian
Customers, Public Semester Class on .NET at Harvard Extension this fall.
The Internet Chuckle: Musician Jokes
Contest with Prizes: Jeopardy
Results of Last Contest: Laws of the Universe
When I asked Keith Ballinger, the Microsoft project manager who wrote the foreword to this book, what I should tell readers about designing their XML Web services, he said, "Two things—make them chunky, and think carefully about their state." In this newsletter, I’ll discuss what he meant and why he said it, and throw in a few more suggestions for writing robust, high-performance XML Web services quickly.
Make Them Chunky
The simplest sample service that I wrote a few issues ago returns a single string. While an XML Web service method can return only one object as its return value, the object is not limited to being a simple type such as the string shown here. It can be any .NET object that can be serialized into XML. I discussed the requirements for XML serialization in my previous newsletter, but basically it’s any type of object that contains a default constructor (one that requires no parameters), which means almost every type of object in existence. This means that you can return any amount of data from your XML Web service methods. For example, it's fairly common for an XML Web service to return aDataSet object representing the results of a database search.
The overhead of making an XML Web service call is fairly high. A lot of work goes on at low levels even though you don’t have to write the code for it. You want to incur this overhead as seldom as possible, which means bundling as much information as you think you’re going to want into each call. For example, you don’t want to make one call to get a customer’s first name, another to get his last name, and a third to get his middle initial. You’d rather make one call to obtain a customer object containing all this information. Even if the customer object contained some information that you didn’t care about in this particular case, maybe the prefix and suffix fields of the customer’s name, you still make a performance profit by avoiding multiple calls. On the other hand, you probably don’t want to transfer the customer’s entire 25-megabyte buying history record when all you want to do is send him a birthday card. It’s sort of like the shipping charges on Amazon.com, which they’ve cleverly structured to encourage you to buy more than one item per order.
I wondered where the break-even point for chunkiness was, so I wrote a sample program shown below. The XML Web service method accepts an integer parameter and returns an array containing that number of bytes. The user specifies the number of calls to make and the number of bytes to fetch per call. The client program makes that series of calls and reports the elapsed time.
Going from one machine to another machine on a 100-MB/sec Ethernet in my own office with authentication turned off and nothing else happening on the network or on either machine, I measured the results shown in the following table.
Elapsed time per call,
The basic overhead (the cost of Amazon.com shipping an empty box) is about 6 milliseconds per call. Transferring data took about 1.5 milliseconds per kilobyte (the extra shipping cost based on the weight of your package’s contents). This means that it’s worth transmitting about 4000 bytes of unneeded data to avoid one unnecessary call. Your mileage, of course, will vary significantly. Authentication will raise the per-call overhead; network congestion will raise the per-kilobyte transmission cost; faster machines and networks will lower them both. You’ll have to repeat this measurement on your target system to know exactly where the break-even point is for your project. But in general, Keith is right, make them chunky.
Think Carefully About Their State
XML Web service objects in their natural state are stateless (groan). That means that ASP.NET constructs a new instance of the XML Web service object for each incoming call and destroys it at the end of the call. None of the data from one call is available to the next call unless you go out of your way to make it so. This situation is somewhat analogous to just-in-time activation of objects in COM+.
Sometimes this behavior is what you want; sometimes it isn’t. For my time service shown in this chapter, it probably is. Whether a client wants to show the seconds digit has no bearing on what the next client wants or should get. Each function call is sufficient unto itself; there’s no reason to remember anything from one to the next. In the classic sense of an object as a combination of data and the code that operates on the data, you might argue that there’s no object here. The client is simply making a call that has no relation to anything else, and that’s not an object, that’s a function. You can waste your time arguing religious semantics; I’ve got a product to ship.
When this behavior isn’t what you want, an object can maintain state between one call and another. The physical instance of an object is still created and destroyed on demand, but you can maintain the data that it was working on in its previous life, as you might leave instructions in your will for your descendants. There are basically two ways to do this, each with its advantages and disadvantages.
First, we can use the internal state collections in ASP.NET. In Chapter 3 of my .NET book I discuss theSession and Application state collections available to objects used on ASP.NET pages. The same state management options are available to your XML Web service objects, as they run on top of ASP.NET. The base class WebService, from which your XML Web service is derived, contains two collections for holding state, called Session and Application. You put data into them and take data out by accessing the collections through string names of items, just as ASP.NET page code does. However, XML Web service code has one large difference from ASP.NET page code. Because of its overhead, Session state is turned off by default in an XML Web service. If you want to use it, you have to explicitly turn it on by passing a parameter to the WebMethod attribute declaration, as shown in the code fragment here:
Public Function IncrementAndReturnSessionHitCount() As String
You must do this in every method that you want to access session state. If you don’t, the Session object will be Nothing, and accessing it will cause an exception. I’ve written a sample service and client to demonstrate session state and exception handling in the next section. It looks like this:
Sessions sound easy to program with, and within their design constraints, they are. However, excessive use of theSession collection can hinder your service’s scalability for several reasons. First, it takes time on each XML Web service call to create the session object, restore its state, and attach it to the XML Web service context. You also pay a space price to store the state information between calls. So don’t just store session state for the sake of storing it; think carefully about exactly how much you need and why. Make sure to omit it from methods that don’t require it.
The indeterminacy of theSession state lifetime is a subtler problem. Suppose a client calls method A on an XML Web service that stores some state in the Session object. Then suppose that some time later, the client calls method B, whose functionality is dependent on that state. If the session state timeout interval has expired, then the state on which method B depends will no longer exist. You’ll have to program both client and server to handle that eventuality. You could increase the session state timeout interval, but this might mean keeping lots of state information around for XML Web service clients that have finished their business and gone away. Session state is a good idea in pages viewed by casual human users, but this indeterminacy is a bigger problem in functions called by other computer programs. If you go this route, think about having some sort of logout method that the client can call to clean up and dump the session state.
The alternative to storing object state in theSession collection is to maintain all state on the client. The client calls method A on an XML Web service, which returns some sort of object containing internal state information. The client then passes this object back to the service when it calls method B, and the server picks the state information out of the parameter and picks up where it left off. The advantages are obvious: neither party has to worry about session state timing out, and the server doesn’t have to buy space to store state for its thousands of clients. The disadvantages are also obvious: all the required state information has to travel over the network on each call, increasing the bandwidth requirements. Also, the client can examine and potentially change the state information between calls, so you’ll have to encrypt it if it contains any sensitive information. This is how Web Forms controls maintain their view state from one round trip to another, as I described in Chapter 11 of the second edition of my book.
I discussed error handling at great length in Chapter 2, explaining the problems of communicating an error from method to caller and why .NET structured exception handling was such an advance. However, structured exception handling only works in .NET because all programs use the same MSIL implementation internally. The whole point of XML Web services is to receive and service calls from clients that arenot running on the .NET Framework, so how can we signal errors to them?
The one commonality that client and server have in XML Web services is SOAP. The SOAP specification provides for error handling with an element called<Fault>. When the XML Web service throws an exception of any type, ASP.NET catches it. ASP.NET then copies the exception’s message into a SOAP <Fault> element, and passes it back that way.
I’ve written a sample program that causes an exception by accessing a non-existent session property (I didn’t turn on the session state, as I discussed in the previous section). Accessing the state object throws a NULL reference exception, which ASP.NET catches and converts into a SOAP<Fault> element, as shown in the following example:
<faultstring>Server was unable to process request-->
Object reference not set to an instance of an object
Once the SOAP fault returns to the client, it’s entirely up to the client to determine how to proceed with it. Any proxy generator worthy of the name will read the SOAP fault and translate it back into the language of the proxy. For example, a Java proxy will turn the SOAP fault into some kind of Java exception. The .NET proxy generator code turns it into a .NET exception of typeSystem.Web.Services.Protocols.SoapException. Our client program catches it, as it would any other type of exception, and tries to make sense of it. The Message element of the exception contains the original message from the server with the location of the error on the server. All the rest of the fields of the exception refer to where it originated on the client, which doesn’t tell us anything about the error on the server side. So if you want the client to understand the cause of the error, to be able to differentiate, say, a login failure from a file-not-found error, you’ll have to come up with some sort of encoding system to place in this string.
What if the client hasn’t accessed the service with SOAP? Then throwing an exception from your XML Web service code causes ASP.NET to send back an uninformative HTTP error screen. If we want to be able to signal useful error information to HTTP clients, we have to use special-case return values. Deciding whether or not this is cost effective is a marketing call. I’d suggest probably not.
Replacing the Namespace URI
If you've ever written a sample Web service, you’ve probably seen the message on the XML Web service test page that says, "This web service is using http://tempuri.org/ as its default namespace. Recommendation: Change the default namespace before the XML Web service is made public." In the wild and woolly world that is the Web, it’s entirely possible that other vendors will create services with the same object and method name as yours, such asService1.GetTime. We’d like to have some way of definitively marking our service so that callers who care are certain that they have ours and not someone else’s. You can argue that the URL at which a client accesses a service definitively differentiates yours from another’s, but we’d like a way of identifying the service itself regardless of its location. When the secret police kick down your door at 4:00 AM shouting, "KGB! You are Ivan Ivanovich?" you’d very much like to be able to say, "No, he lives down the hall."
You do this by placing anamespace URI on your service by means of a .NET programming attribute. A namespace URI is nothing more nor less than a big, long, fancy string that is the unique family name of your service and all other services that you create. You are promising callers that any service and method name within this namespace is unique, that you are in control of the development of services marked with this namespace URI, and that you’ve made sure that there aren’t any conflicts. A proxy wanting to access an XML Web service can specify the namespace URI in addition to the service and method name to make even more sure that he’s gotten what he asks for. The server will compare the requested URI with the URI that it actually has, and throw an exception if they don’t match. The namespace URI can be anything at all that you’d like it to be, as long as it’s unique in the universe—for example "PoliticalFamilyFromHyannisportMassWithMoreMoneyThanBrains." It often begins with your Web address because you own it and no one else should be using it, just as you often use your e-mail address as a login ID. Nothing prevents someone else from using yours just to mess you up, but they probably won’t. No one accesses the namespace URI as a Web address during an XML Web service call, so there doesn’t have to be anything there and often isn’t. However, some vendors do indeed put product or service information at the Web address used as a namespace URI so that potential customers who see the code know where to contact them.
Once you’ve chosen a namespace URI, you place it on your XML Web service by using the attributeNamespace on your XML Web service class declaration, as shown in the code fragment below. The namespace URI appears in your XML Web service’s WSDL file, and thus makes its way into generated proxy clients.
Public Class Service1
Note that specifying the namespace of the service the server is accessing does not provide any real check on the identity of the server. It only specifies who the server must claim to be, without requiring proof that the server really is that particular one—sort of like a limousine driver carrying a sign with the expected passenger’s name on it, but not asking for a picture ID. If your client really cares about authenticating the server (as opposed to the other way around, which is much more common), then you’ll have to work with some sort of trust provider certificate program.
XML Web Service Security
XML Web services have many of the same security considerations as ASP.NET pages. Some XML Web services are public and anonymous, such as an airline schedule browsing program. Others are private, such as a mutual fund company’s financial trading program. Most will require at least some degree of authentication and authorization, more in the case of medical or financial records, less in the case of $20/year magazine subscriptions. There is no One True security design for XML Web services. Even more than for ASP.NET pages, you have to look carefully at the specific details of every case.
We’ve seen that XML Web services are layered on top of ASP.NET. Since we get services such as state management from the underlying layer, it would only make sense that we would get our security from it as well. While this happens in some situations, it doesn’t always solve our problems. XML Web services differ from Web Forms because the client of an XML Web service may not be a human being; it could easily be another computer program. In ASP.NET forms authentication, we redirected an unauthenticated user to a Web form and required her to type in her authentication credentials. That won’t work for XML Web services, so we have to come up with some automatic way for the user to present authentication credentials. Passport authentication suffers from the same problem; it is likewise human-centric.
Our XML Web services can get automatic authentication from ASP.NET today if we select Windows authentication. But as I explained in Chapter 3, this approach works well only in Windows-only environments. That’s fine for a corporate intranet, where you have control over all the software and machines. Security-conscious designers can do things such as restrict the IP addresses from which clients are allowed to access the service, require the clients to present X509 certificates issued by their own certificate authority or by a third party, or install encrypted network cards in all their computers. But to my mind, that’s exactly what XML Web services arenot about. The whole point of XML Web services is to be open to everyone on the amorphous Internet, from anybody, to anybody. We need a security mechanism that works no matter what hardware and software environments the clients are using. We don’t currently have a point-and-click mechanism in .NET, but as I’ll show you, we can use .NET utility objects to roll our own without too much trouble.
I wrote a sample XML Web service that simulates a magazine publisher that allows paying clients to request the text of articles from its server. Users must purchase a subscription, which is done outside the XML Web service, perhaps on the human-readable Web site with a credit card. The user is then given a user ID and password, which is good for unlimited requests over some period of time.
The XML Web service has two methods, one calledLogin and one called RequestArticle. The first takes a user ID and password and returns an admission ticket, a "session key" if you will. The second requires this admission ticket and an article number, and returns the article. Here's the workflow in this system.
I really don’t want anyone to steal a user ID and password. Anyone who did that could use my system for free for the duration of that user’s account. They couldn’t hurt anything with it, as I don’t store any sensitive information about my users, but I don’t want the bad publicity that a theft would bring. So my client would use Secure Sockets Layer (SSL) for making the call to my service’sLogin method. That’s easy for the client to do, simply change HTTP to HTTPS in the request URL. This causes the client to request the server’s public key, use that to negotiate a session key, and use the session key to encrypt the call transmitting the user ID and password. That way no one with a packet sniffer can record the user ID and password to break in later. The drawback is that the handshaking and encryption require a significant amount of extra time and CPU cycles. (Note: the sample program supplied with this book uses HTTP instead of HTTPS, because most readers will find it difficult to set up their servers to handle HTTPS.)
TheLogin method on the server checks to see if the user is in my subscriber database and if the subscription is current. My sample program simulates that with hardwired names. If the user ID or password is not valid, the server throws an exception and it’s up to the client to deal with it. The server then creates an admission ticket, using the forms authentication utility functions that I demonstrated in Chapter 3. The ticket contains the user’s name, the roles to which the user belongs, and an expiration time. I encrypt the ticket using other system utility functions and return it to the client in the form of a string. The client retains this string for use in future calls.
When the client wants to request an article, he calls theRequestArticle method, passing the encrypted admission ticket and the desired article number. The server decrypts the admission ticket using the system utility functions. My server now knows who the user is, what roles he belongs to, and whether the ticket’s expiration time has passed. If a client presents an expired ticket in an article request, the server throws an exception and the client has to send the user ID and password again (over SSL). My sample program does this internally without bothering the user.
I didn’t use SSL for requesting an article because I didn’t want to take the performance hit. Anyone who might be sniffing packets could read the clear text of the articles being retrieved, though since the user’s name is encrypted in the ticket, they won’t know who is reading which article. If the data were sensitive in itself, such as medical or financial records, I’d probably use SSL throughout regardless of time cost, but in this case, it’s just an article that the snooper didn’t select. If a snooper records the ticket string and plays it back in a spoof request, he’d be able to request articles without paying for a subscription, but only for the duration of the ticket’s validity, which I set to half an hour when I create it.
This security architecture was relatively easy for me to implement. Without spending too much developer time and money, I’ve made the cost of cracking my system significantly higher than the cost of buying a subscription, and limited the damage done in the case someone does crack it. If some sexually frustrated geek has nothing better to do on a lonely Saturday night than sniff my packets to hijack free article retrieval for an average time of 15 minutes per break-in, welcome to it, say I.
You can see the design decisions that I’ve made, trading off development time against security against runtime efficiency. I hope you see why I think I have about the right balance for this particular system. Different systems will require different trade-offs. Pick the one that makes you the most money, as I’ve done.
As you can see, the switch to .NET, while well worth the effort, requires a significant learning curve. I’d be happy to come to your company and teach you all about .NET. You can read about my .NET training classes here on my Web site .
We'll continue our discussion of .NET in the next issue. Enjoy playing with your new attribute program. Give me a call if I can help you out. Until next time, as Red Green would say, "Keep your stick on the ice."
New Book: Introducing Microsoft .NET, Second Edition
Updated to V1.0 Release, with 5 New Chapters Added
Sample code now in both VB and C#
by David S. Platt, President of Rolling Thunder Computing
Published by Microsoft Press
Microsoft finally shipped .NET, so I decided to update my book on it, and add lots of new material that I didn't have time to cover before. I've brought all the material up to date with the final RTM version of the product. I've added enhanced material so several of the previous chapters, a sample of which forms this newsletter's feature article. And I've added 6 new chapters, as you can see in the table of contents below. And as many readers asked for, I've written all the sample code in both VB and C#. If you liked the first edition, you'll probably like the second one as well. I tried really hard to make it worth making your boss pay for again. We'll see what the sales numbers are.
If you haven't seen it before, this book is meant to be accessible to managers, while still providing enough information to be useful to programmers, both lightweight VB types and heavyweight C++ types. The book is meant to be non-threatening, using a lot of pictures and diagrams, a small amount of simple VB code, and no C++ at all, which would scare off 80% of the potential readers. I write each chapter in a pyramidal structure, so managers can read the first three sections (Introduction, Problem Background, Solution Architecture). Ambitious managers and VB programmers can continue through the next section (Simplest Example). Heavier-duty developers can read the ends of the chapters, where I discuss more advanced elements of the topic. The book's Web site is, naturally, www.introducingmicrosoft.net. There you will find a free chapter on .NET My Services, as well as the code for all the book's samples, and a list of errata as soon as we find some. You'll also find a glowing review of the first edition here (well, what other kind did you expect me to link to? I may be crazy, but I'm not stupid.) written by Manohar Kamath. Here's the table of contents:
1. Introduction (updated to RTM)
|7. XML Serialization and Support (new chapter)|
2. .NET Objects (updated to RTM)
|8. Events and Delegates (new chapter)|
3. ASP.NET (updated to RTM)
|9. Threading (new chapter)|
|4. .NET XML Web Services (updated to RTM, new material added, see this newsletter's feature article)||10. Windows Forms Controls (new chapter)|
|5. Windows Forms (updated to RTM, new material added)||11. Web Forms Controls (new chapter)|
|6. ADO.NET (updated to RTM, new material added, printed in book this time)||12. Epilogue and Benediction (hasn't changed much)|
The second edition should be on shelves by May 10. You can pre-order it through amazon.com by following this link . It should be popular. The first edition was, and this one's better. On June 11, 2001, the first edition's sales rank on amazon.com was #762, thumping Tom Clancy's latest hardcover and paperback, hopelessly mired around #1400. That tells you something about Amazon's customers -- they're all geeks.
Blatant Self Promotion
5-day In-House Training Class on .NET or .NET for Insurance
.NET is here, and it's hot. It changes everything in the software business, and you can't afford to be without it. Rolling Thunder Computing now offers in-house training classes on .NET, using my book as the text. See the syllabus at www.rollthunder.com/dotnetfiveday.htm
4-Day In-House Training Class on XML or XML for Insurance
You've probably been hearing an enormous amount of hype these days about XML. It sounds like a great idea, and it is indeed a beautiful solution to certain classes of problems. This four- or five-day training class covers the guts of XML, such as well-formed documents, validating documents with DTDs or schemas, programming XML using DOM and SAX, and transforming XML with XSL stylesheets. Most important, we talk about how to use XML in real-life applications: the advantages and drawbacks, the places where it fits well, and the places where you'd be trying to bang a square peg into a round hole. Get your reservation in quickly, because this one looks hot. You can read all about the new XML class on Rolling Thunder Computing's web site, http://www.rollthunder.com.
Public Class on Programming .NET at Harvard Extension this fall
I'll be back teaching at Harvard Extension this fall, after a two-year hiatus. The course will be a broad survey of all of .NET, with lots of programming homework. I don't know which night it will be, and I haven't worked out the full syllabus yet. Announcements will be made on my web site, http://www.rollthunder.com. Anyone interested in being a teaching assistant for the class, send me e-mail at firstname.lastname@example.org.
20% Discount for Canadian Customers
The Canadian dollar, known as the "loonie" for the picture of a loon that it carries, has had the snot kicked out of it in the last few years. It's now trading at about 62 cents on the US dollar. Even leaving aside the cost of developing Web sites in both French and English, they're having a tough time. I stopped at Tim Horton's (the Canadian equivalent of Dunkin' Donuts, only better) on my way to a gig, ordered two Canadian maple donuts and a cup of coffee, and paid with a US $10 bill. The counter guy gave me back 11 Canadian dollars, and said, "You gave me 10 bucks, and I'm giving you back MORE than ten bucks, PLUS THE DONUTS. Something's wrong here." Perhaps it's the socialist government that's given up trying to share the wealth and now spends its time distributing the poverty. To help out our good neighbors (they fed and housed hundreds of stranded airline passengers on 9/11 and the tough days afterwards) in their own tough times, and to get some business in places I'd like to bring my family, I'm offering 20% off the training fees on in-house training classes held in Canada through August 31, 2002. Give me a call and we'll talk about it.
Internet Chuckle: Musician Jokes
I've always liked jokes that slander entire categories of people. It saves an awful lot of time compared to slandering each one individually. You can't tell ethnic or religious jokes in polite company any more. But for some reason, jokes about musicians, targeted at the instruments they play, are still socially acceptable (featured on PBS radio, for example) and often quite funny (despite being featured on PBS radio). While some of them are recycled from one instrument to another many are unique to the particular instrument. Players of the viola (a slightly overgrown violin, hard to play well and not considered as cool as either violin or cello) seem to attract a lot of them. I really love seeing someone get less respect than us computer geeks. For example:
You can see the whole collection at
If you'd like an explanation of why violist are the butt of all
these jokes, you can see the paper
Laughing Matter: The Viola Joke as Musician's Folklore,
presented by Carl Rahkonen at the National Meeting of the American Folklore Society and the Society for Ethnomusicology on October 21, 1994.
Drummers also attract the attention of humorists. They're considered the dumbest of all musicians. For example:
A drummer, tired from being ridiculed by his peers, decides to learn how to play some "real" musical instruments. He goes to a music store, walks in, approaches the store clerk, and says "I'll take that red trumpet over there and that silver accordion." The store clerk looks at him a bit funny, and replies "OK, you can have the fire extinguisher but the radiator's got to stay".
Q: What's the difference between a drum machine and a drummer?
A: You only have to punch the information into the drum machine once!
You can read more drummer jokes at http://www.cse.ogi.edu/Drum/jokes.html. I don't have an explanation of why they exist (the jokes, not the drummers), although the Wall Street Journal of July 31, 2001 ran an article entitled "Why Nobody Wants To Be the Drummer in a Rock 'n Roll Band," which read, in part:
"There is a serious drummer shortage afflicting the new and struggling acts
that hope to be next year's R.E.M. or U2. Unreported by labor economists, the
crunch follows decades of neglect and mistreatment of the men and women who
pound skins and bang cymbals. Hidden at the back of the stage while guitarists
and singers shine out front, rarely consulted on matters of musical direction
and treated like sidemen instead of bandmates, resentful drummers are
withholding their services, making unsettling demands and, in some cases, simply
putting down their sticks.
"Dave Grohl, who was the drummer for the legendary grunge band Nirvana, is now a big hit as the singer for Foo Fighters. But the drummer-hurt lingers. 'Look at the songs they let Ringo sing,' he says. 'Yellow Submarine. If he could have sung Let It Be, then all drummers could have been respected.' "
Disclaimer: This section will often refer to commercial vendors of products or services. Rolling Thunder Computing receives no benefit from these vendors in any way, shape, form, or manner; and would decline any if offered. The sole criterion for mention in this section is that I laughed at it, and that I think people as sick as I am would do so as well. If you know a good one, send me the URL at email@example.com. If I use it, I'll send you a Rolling Thunder Computing coffee mug, which makes an excellent specimen container when your company implements mandatory drug testing.
Contest with Prizes: Jeopardy
Lord, send a man like Robbie Burns to sing the Song o' Steam!
-- Rudyard Kipling, "McAndrew's Hymn", 1894
I'd settle for Dr Seuss, except he's dead.
-- David S. Platt, "Plattski's Musings", 2000
I got very few responses to the last contest, only the three that you see here. I might have had a problem with the distribution mailing, as the hit count was lower than usual, but maybe I made the contest too hard. This is the contest that I ran in the my first ever edition of ThunderClap, almost 4 years ago. It's pretty simple. As with the game show Jeopardy, I want you to write an answer and then give the question. For example:
A. Eisenhower, Kennedy, and Johnson
Q. Name two dead presidents and a floor wax.
The first time I ran it, the winner was Greg Manto, whose winning entry was:
Q. What do you get from having sex with Zeus?
Send them here to me, and I'll publish your wisdom for all to see. As always, your entries must be computer-related and original. This isn't a family newsletter, so they don't have to be clean or politically correct. Submitting more than one increases your chances of winning. First prize is $100, second prize $50. Winners will be announced in the next issue of Thunderclap, and the judge's decision (mine) is final. In the event of duplicate or similar entries, the earliest wins. All entries become property of Rolling Thunder Computing. All authors will be identified by first and last names, but not by company. Authors names will be withheld if you so request. Submit your entries via e-mail to firstname.lastname@example.org.
Results of Last Contest: Law of the Universe
First Prize, $100 to Stuart Celarier
Data is one
hundred times harder to debug than code.
Second Prize, $50, to Jack Cloninger
Jack's First Law of Time Compression: No matter how much time has been
allocated to a software development project, 99% of the actual hours spent
on the project will be spent in the last two weeks prior to the deadline.
Corollary to the First Law of Time Compression: The last two weeks prior to
a software project deadline consist of days that must contain an infinite
(plus or minus a few) number of virtual hours, but which in fact contain a
number of actual hours that approach zero asymptotically as the date
approaches the deadline.
And honorable mention to:
Two can live as cheaply as one for half as long.
-- Perry Riposte
This is the eleventh issue of my (more or less) quarterly newsletters. Each will bring you a technical article on current topics in software development, which I hope you will find useful. Each will also bring you a contest, allowing you to show off your intelligence and creativity to win prizes, which I hope you will find funny (although some readers have reported the reverse.) In between you will find my own blatant self promotional material, telling you about the latest ways I've come up with to separate you from your money. (I could have said "carefully selected products and services that we feel might interest you", or other mealy-mouthed horsepuckey. You want the truth or you want me to waste my time and yours dressing it up?)
I'd like to hear what you think about this newsletter and what types of articles you'd like to see in the future. Send your e-mail comments to email@example.com.
This newsletter may be freely redistributed, provided that it is sent in its entirety. If you enjoyed it, can I ask you to please forward it to a friend who might also enjoy it? The subscription roll has grown to over 5500 for this issue.
Thunderclap is free, and is distributed via e-mail only. We never rent, sell or give away our mailing list. Subscription and unsubscription are handled by a human operator reading e-mail messages. To subscribe or unsubscribe, jump to the Rolling Thunder Web site and fill in the subscription form.
Thunderclap does not accept advertising; nor do we sell, rent, or give away our subscriber list. We will make every effort to keep the names of subscribers private; however, if served with a court order, we will sing like a whole flock of canaries. If this bothers you, don't subscribe.
Source code and binaries supplied via this newsletter are provided "as-is", with no warranty of functionality, reliability or suitability for any purpose.
This newsletter is Copyright © 2002 by Rolling Thunder Computing, Inc., Ipswich MA. It may be freely redistributed provided that it is redistributed in its entirety, and that absolutely no changes are made in any way, including the removal of these legal notices.
Thunderclap is a registered trademark ® of Rolling Thunder Computing, Inc., Ipswich MA. All other trademarks are owned by their respective companies.