Woozle Wuzzle
Shocking revelation

I was talking with another developer the other day and he revealed an interesting piece of information: he believed that comments and code style were a matter of personal choice. To me this was like believing that the world was flat and then having someone say that it's round. All of that time I spent perplexed wondering why I couldn't see things from long distances over a "flat" plane finally become crystal clear. Learning that developers may believe comments and code style are a matter of personal choice has allowed me to understand and put into perspective a number of other conversations that I have had with developers.

Hypothesis 1.1

Code comments and style are a function of quality.

This is currently my running hypothesis that I am attempting to prove through empirical evidence. My non-scientific research has shown it to be true. The difficulty in firmly establishing quantitative evidence for this hypothesis stems from the fact that, for example, diligently and effectively commenting code intrinsically changes ones approach to coding. In other words, you cannot separate out the processing of adding and maintaining comments without changing the nature of how one programs.

Self commenting code

In an attempt to dispel the "I don't need to comment my code since if the code is written clearly enough it should describe itself" theory, I present the following:

Definition 1.1

The purpose of code comments is to present intent.

Definition 1.2

A software defect is a deviation from intent. This definition does not make a distinction between implicit (i.e. expected but not defined in a requirement) and explicit (i.e. defined in a requirement) intent.

Theorem 1.1

Code is incapable of sufficiently presenting desired intent.

Proof   I will provide an indirect proof of Theorem 1.1 by assuming "code is capable of sufficiently presenting desired intent" and obtaining a contradition. Choose a section of code that contains defects. By Definition 1.2 this section of code does not correctly describe the intent. QED

Notice that Theorem 1.1 contains the word desired. This is necessary to distinguish between the intent that a section of code with defects presents and the intent that is required. Also notice that Theorem 1.1 contains the word sufficiently. Later entries will expound on this in more depth but for now it will suffice to say that code utilizing crafty programming may obfuscate intent.

I do acknowledge that for those who use the "I don't need to comment my code since if the code is written clearly enough it should describe itself" to mean "I'm too cool / talented / whatever to comment" or to cover for "I'm too lazy to comment" that my argument will have fallen on deaf ears. I'm getting to you next!

Implies

I simply cannot remember which is normalized and which is denormalized. I cannot do coordinate transforms -- I consider myself "spatially impared". And I can never remember the truth table for implies.

"p implies q" or "p only if q" has the following truth table:

     p | q | p -> q
    ---+---+--------
     T | T |   T
     T | F |   F
     F | T |   T
     F | F |   T

where p and q are propositions.

It is logically equivalent to say:

  • p implies q
  • q is a necessary condition for p
  • p is a sufficient condition of q
  • in order that p be true it is necessary that q be true
  • if p is true then q is true

For example:

    If a person is a father then a person is male.

This statement is of the form p -> q where:

  • p: A person is a father
  • q: A person is male

It is necessary for a person to be male to be a father. Being a father is a sufficient condition for being male. If a person is not a father, nothing can be said about if they are male. Whereas if a person is not male, they may not be a father. This last statement is the contrapositive of the proposition.

     p | q | -p | -q | p -> q | -q -> -p
    ---+---+----+----+--------+----------
     T | T |  F |  F |   T    |    T
     T | F |  F |  T |   F    |    F
     F | T |  T |  F |   T    |    T
     F | F |  T |  T |   T    |    T

where - represents negation. Since both p -> q and -q -> -p have identical truth tables they are said to be logically equivalent.

Food cooking instructions

The instructions for my frozen lunch state: pierce film 3 - 4 times. "3 - 4"? Can you pierce something 3.3 times? What's a 0.3 of a pierce? Since pierce isn't defined (is it one inch long by 0.001 inches wide?), couldn't you renormalize to just 3?

And why not just "3" or "4". Why a range? Is there some legal precedent that caused this to occur? U.S v. Hungry Jack: Exactly n piercings leads to multiple deaths. And what happens if I go outside of the pierce operating range of my frozen lunch? Will death result?

Perhaps when they were putting the food through the QA cycle, the average number of piercings that caused a "perfectly cooked meal" was 3.6. Knowing that the average american is too dumb to know what a 0.6 of a piercing is (cuz I sure as heck don't!), they made it a range (Can you imagine the number of calls asking: what's a 0.6 of a pierce? The phone company would be brought to its knees!) This is just a theory at this point, but in order to finally get some sleep at night, I'm going to have to believe something.

The answer is out there!

Those wacky mathematicians

While doing some research for a greedy algorithm that I'm working on, I stubled across the McNugget(tm) Number. Who says that mathematicians can't be fun?

Millennium Park

I had the opportunity to visit Chicago's new Millennium Park over the weekend. To quote Cindy Pritzker, "Isn't this the spiffiest thing you ever saw?". Yes Cindy, yes it is and thank you for all of your contributions that made it possible.

Chicago threw one helluva party in a way that only Chicago could do. The number of activities and events that were available for all ages was simply staggering -- and they were free. From rap to jazz to classical all forms of music were available. There was dancing. There was drumming. There was juggling. I spent most of Friday evening, Saturday and Sunday in the park and I don't think that I saw everything that there was to see!

(I will be updating this entry with pictures and comments on the new park. Check back for updates!)

Pipe selector problems

As if the previous java.nio.channels.Pipe inconsistencies weren't enough, on both Linux and Windows it appears that you have to drain a pipe's source before you can write to the sink again. Again, this is a case that falls under the system-dependentness mentioned in Pipe's javadoc. Poopy I say!

Update

After I determined that the write selector was lying to me on Linux, and after pouring over Stevens' Advanced Programming in the UNIX Environment to refresh my memory on pipe() (which Linux uses), it turns out that the pipe does not require drain-then-fill. Again, the write selector is lying (which appears to be a known "issue" with Linux's select() on a pipe()). Windows remains drain-then-fill.

Update II

It seems that the results of drain-then-fill are dependent on how the sink is filled. If the sink is filled one byte at a time, then neither Linux nor Windows is drain-then-fill but Linux will still have an inaccurate write selector. If the sink is filled in 8k chunks, then Windows will exhibit a drain-then-fill requirement; Linux is never drain-then-fill.

Closing thoughts

Given that Linux's write selector is not accurate and always returns "none available" when there is data in the pipe (but will always return "go ahead" when the pipe is empty), it is nearly impossible to generically replace a file or network channel with a Pipe.SinkChannel. Rob angry good!

Link-back to main entry: NIO and SSL.

More Pipe inconsistencies

The number of bytes written to a java.nio.channels.Pipe's sink at a time will determine the apparent size of the pipe (i.e. the number of bytes that can be written before the pipe blocks). If one byte is written at a time, the size of the pipe is 33012 for Windows or 1 (yes, 1) for Linux. If two bytes are written at a time the sizes are 33012 and 2. Four bytes gives 33012 and 4. Seeing a trend?

Windows will eventually give you the known value of 32768. With Linux, you need to specify more bytes than the known buffer size (4096) to get the buffer size.

The code used is as follows:

// create a Pipe and retrieve its sink and source
// NOTE:  the sink and source are SelectableChannels
final Pipe pipe = Pipe.open();
final WritableByteChannel sink = pipe.sink();
final ReadableByteChannel source = pipe.source();

// set the sink to non-blocking and create and register a write 
// Selector on it.  The Selector is used to determine when the sink 
// is "full".
// NOTE:  the cast is required since there is no common super-type
//        for selectable + readable / writable
((SelectableChannel)sink).configureBlocking(false/*non-blocking*/);
final Selector writeSelector = Selector.open();
((SelectableChannel)sink).register(writeSelector, SelectionKey.OP_WRITE);

// continue to write to the sink until it is "full"
// NOTE:  the sanity upper-bound is used to ensure that, in a remote
//        case, a sink is not infinite
final ByteBuffer writeBuffer = ByteBuffer.allocate(BUFFER_SIZE);
for(int i=0; i<BUFFER_SIZE; i++)
    writeBuffer.put((byte)(i & 0xFF)); // arbitrary
writeBuffer.flip();
boolean isInfinite = true;  // set to false if limit found on write
int numberOfBytesWritten = 0;
for(int i=0; i<UPPER_BOUND/*sanity*/; i++)
{
    // ensure that data can be written
    // NOTE:  selectNow() is used so that it does not block
    if(writeSelector.selectNow() > 0)
    {
        // clear the selected keys (required)
        writeSelector.selectedKeys().clear();

        // write the data 
        // NOTE:  the actual data written is arbitrary
        numberOfBytesWritten += sink.write(writeBuffer);
        writeBuffer.rewind();
    } else
    {
        // the sink is full.  Flag that a limit was found and break 
        // out of loop.
        isInfinite = false;
        break;
    }
}

UPPER_BOUND is some large number (say 10000) and BUFFER_SIZE has values as described above.

And, no, changing selectNow() to something like select(1000L/*1s*/) doesn't matter (for those worried about concurrency problems).

I should mention that Pipes javadoc does state platform inconsistencies and that this case falls under that unbrella. It's one thing to read a javadoc. It's another to actually see those inconsistencies first hand.

An interesting Linux tidbit: if the following code is added after the code listed above with a BUFFER_SIZE less than 4096 then numberOfBytesWritten will be non-zero.

// attempt to write more to the sink even though we shouldn't be 
// able to
writeBuffer.limit(1/*writes one byte*/);
numberOfBytesWritten = sink.write(writeBuffer);
writeBuffer.rewind();

It seems that the Linux write selector is lying to us. This is not the case on Windows.

Link-back to main entry: NIO and SSL.

Pipe inconsistencies

Here's some java.nio.channels.Pipe triva questions for you:

Q:What happens when you close the sink of a pipe?

For all those that answered "The source returns -1 on read indicating an end-of-stream", you get a gold star! Now on to a toughie.

Q:What happens when you close the source of a pipe?

Belt it out! "Writing to the sink will throw java.io.IOException." Well, you're half right. And what does half right mean? Yup. You're half wrong. The correct answer is "It depends on the platform."

  • Linux: any write will cause .java.io.IOException: Broken pipe to be thrown.
  • Windows: any number of writes can be performed before java.io.IOException: An established connection was aborted by the software in your host machine is receveied (WinSock error 10053).
  • Other: as of yet untested

The concern is the windows case where a write (or multiple writes) can be performed successfully.

Why isn't there a close() on Pipe itself? And why doesn't closing either the sink or the source automatically and synchronously close the other? I just don't know. Part of Sun's grand scheme to render its developers into blubbering masses of goo? That's just anger talking again. I'll just chalk it up to lack of foresight that has caused such things as the SSL + NIO problem.

Expect to see a defect report / RFE in the bug parade on this topic.

A big hearty thanks goes out to Igor for doing the Linux testing!

Link-back to main entry: NIO and SSL.

Momentary elation: Channels

If you've been following my lamenting over NIO and SSL then you can probably guess that I've made it to step 5 (acceptance). I had a moment of elation this morning when I found another one of those obscure NIO classes: Channels. Channels does various conversions between traditional IO streams and NIO channels. In theory I could take SSLSocket's getInputStream() and get a ReadableByteChannel. One problem: ReadableByteChannel is not selectable. Oh well, back to the drawing board.

First grieveing and now momentary elation followed by good swift kick in the gut. Doesn't Sun care about the unstable mental state all of this has left me in?!?

Side note: isn't it annoying that there's no interface that describes a selectable, readable / writeable channel? In other words, there's no common way to describe a Pipe.SourceChannel and SocketChannel. Ppfth!

NIO and SSL

I have worked with NIO quite a bit in the past. It has a high activation energy but once you're over that initial hump, it's pretty smooth sailing. I find it difficult not to write non-blocking IO these days.

I recently wrapped up a client / server prototype and I am just beginning to get it ready for a "real world" test. The first thing that I thought of was SSL. So like all good programmers, I brought up Google and typed "NIO SSL". Much to my chagrin I find that it is not possible to combine NIO, Selectors and SSL. My first thought was "This must be from the initial 1.4 release. There's no way that in three years Sun would let NIO rot without SSL.", so I continued my search.

To make a painful story short, there is no information regarding SSL ever being a possibility with NIO in 1.4. 1.5 will introduce an SSLEngine to solve the problem, but again, nothing is said if this will be made available for 1.4 users.

For those in the same boat as I am, there are solutions for using Selectors with SSL such as wrapping a standard stream with a Pipe. The problem with any wrapped solution is that the connection (which is done with a standard socket) is blocking. Non-blocking connections are one quarter of the problem that you're typically trying to solve with NIO (the other three being read, write and accept).

I'll spare you the Sun rant but let's just say that I'm less that impressed with their decisions to not provide SSL with NIO and to, for all intents and purposes, cover it up. When you read the 1.4 datasheet about NIO and then about JSSE, you get the impression that all is just sunshine, rainbows and lollipops. How can one think that it's acceptible to provide developers with the ability to "write ultra-scalable, high-performance server applications" without parity with existing sockets? And then, in 3 years, not make up for the discrepancy?

If you're into conspiracy theories, what do you think about the missing RFE for SSL + NIO? My tin foil hat has been firmly placed on my head!

Follow up:

I've been doing a lot of poking around to see if there are freeware implementations of JSSE that support NIO. There aren't. I did find this interesting link. Given all of my ramblings about features vs. quality, if Sun didn't ship SSL with NIO due to quality risks then I can buy that. If Sun hasn't shipped an updated JSSE for NIO due to pervasive changes required then I can buy that too. The length of time between releases is just hard to swallow.

As you may be able to tell, I have moved onto phase three of the Kubler-Ross 5 stages of grief. The initial entry was written while firmly in phase two. I fully expect to be at phase five by mid-day tomorrow and I will begin to find an acceptable solution to my current problems.

Related Entries

Classic Tradeoff

As mentioned in another entry the classic tradeoff is: features vs. quality. A further refinement of this is: do you release a high priority feature even if it is not complete?

It is common for a project bend to the pressures of client expectation and release a feature that may not be complete or is known to have defects. The thought that leads to this is that it is better to meet the expectation of the feature and deal with the quality risks later than it is to miss expectation. In my experience, the reality is the exact opposite. Releasing a feature with defects lowers the overall expectation of the product to a level much lower than that of a single missing feature.

Let's take a look at this in more detail.

Releasing a product with a missing feature defines a single point of contention (i.e. the missing feature). The client will have a focused attack on the omission that tends not to spread to other features (unless of course there are other missing features). I have no theories as to why an omission does not spread -- I only have experience that says that it does not impact the preception of the rest of the product.

Releasing a product with a defect heavy feature tends to lower the overall perception of the product. I believe that this follows a "If this feature has this many problems, then how bad is the rest of it?" progression. On top of this is the onslaught of defect reports (most of which are already known internally to the developers) that accompanies the feature. The amount of man power required to deflect or stop the barrage is sometimes greater than that required to simply fix the bugs. (Developers may be thinking "Just fix the bugs and ignore the client's bug reports. Once the fixed version comes out all of the bug reports can be cleaned up en masse." Unfortunately, it just doesn't work this way unless the fix comes out a very short time thereafter. In most situations, the client hand-holding and expectation resetting combined with handling of the defect reports consumes needed resources.) I have seen projects where this barrage eventually stalls all progress and the entire team ends up in defect triage, changing focus from strategic to immediate tactical.

My recommendation in this situation is to take the omission hit and follow it up with a well planned and executed (and, if possible, ahead of expectated delivery) patch / release that includes the missing feature. If you botch the followup release, well, let's just say that that tends to be worse than had the defective feature been released in the first place.

The List

I am a big fan of movies -- especially independent and foreign. Nary a week will go by that I wont see a previously unseen movie. I always send a quick email or IM to friends about the movie and if they should see it or not. On more occasions than I can count, people have asked me to post a list of these movies. I've been avoiding doing this out of sheer laziness for quite some time, but after unintentionally seeing the same movie twice, the final shoe has dropped.

What would a movie list be without some ordering or structure? I'm firmly against the starring system used in the industry. It has a tendancy to downplay or misrepresent wonderful movies. For example, how can you use the same "quality" rating system to compare:

  • Shrek 2 A recent feature that has everything that one could ask for in animated movie: you laugh, you cry and you spend a good deal of time thinking about what movie is being spoofed.
  • The Deer Hunter A chilling tale about life after being a prisoner of war.
  • National Lampoon's Vacation A poignant depiction of a family's cross country roadtrip.

Clearly, putting Vacation and Deer Hunter into the same quality category would be a crime against nature. Or even attempting to compare the comedic aspects of Shrek 2 and Vacation would leave one or the other standing in the mud. But when all is said and done, all three movies are a must see.

I will adopt the following structure:

  • Must See: Not all of these movies will be "good" movies in the traditional sense (the 60's did some downright painful things in the wardrobe, set and music deparements) but they all have qualities that make them required viewing material.
  • Should See: These movies didn't make the cut into the must see list but are still movies with exceptional traits.
  • If You Have the Time: If it's a Tuesday night and you have nothing better to do, watch these movies. They are by no means bad.

Movies that I just don't care for will not be present in the list. So how does one distinguish between a movie that has not yet been added and one that is just not on the list (*cough* Lord of the Rings)? You don't. It's just one of those gambles in life.

And so, without further ado, The List.

Must See

Should See

If You Have the Time

Still need to be categorized

This list is by no means static. I will add to it as time permits. Also, it's possible for movies to change categories.

Minor ZipFile gotcha

java.util.zip.ZipFile and its subclass java.util.jar.JarFile has a minor gotcha when using getEntry(String); leading slashes are not ignored. For example:

    final JarFile jarFile = new JarFile("rt.jar");
    System.out.println(jarFile.getEntry("/java/lang/Object.class"));
    System.out.println(jarFile.getEntry("java/lang/Object.class"));

will return:

    null
    java/lang/Object.class

In general, this is not a big deal. But when manually parsing URLs (don't ask) such as:

    jar:file://rt.jar!/java/lang/Object.class

it can bite you in the butt.

Classic tradeoff leads to classic mistake

Let me begin by saying that I have a lot of respect for the Eclipse team. They have maintained tight milestones on a massive project. They have remained responsive to bug reports and feature requests. I could go on for quite a while. Much can be learned from that project.

Unfortunately, they stumbled on the classic tradeoff and that has lead to a classic mistake.

The classic tradeoff is: features vs. quailty. Do you spend more time fixing defects or creating new features? The classic mistake is: releasing a feature with a high defect rate.

Classic mistake

Notice that I said defect rate and not defect count. It may be that a feature has zero defects when it is shipped but in the recent past the number of defects found and resolved was high. Understanding the difference goes back to a firm understanding of QA and a little math. The defect rate is the number of defects found per unit time. The unit of time is commonly determined by the frequency and duration of test / fix cycles. A large defect rate means that there were many defects found in a cycle whereas a small defect rate means that there were few defects found. The defect rate for a complex feature typically increases then decreases. This trend follows the common sense rule that a single test cycle will not reveal all defects. In fact, in early test cycles, defects will block other defects from being seen. As fixes are put in place, the number of defects will eventually decrease.

For those more savvy, the change rate of the defect rate (a defect acceleration of sorts) is also useful. A positive acceleration means that there are more defects found in a cycle than previously. A negative acceleration means that less defects were found than previously. And a zero acceleration does not mean that zero defects were found -- it means that the same number of defects were found and were found previously.

Releasing a feature while the defect acceleration is positive (or zero with large defect count) or while the defect rate is still large will typically result in a feature with a high defect rate. In effect what you're doing is replacing a testing cycle with your client. This is the classic mistake.

Classic tradeoff

If there is one thing that clients are exceptionally bad at it would be understanding that development takes a finite amount of time (and typically longer than they expect). After the process of requesting and specifying a feature has finished and while development is occurring, the client is already thinking about the next set of features. By the time that development releases the features, the client already has expectations beyond that of the release. This results in development always trailing or missing expectations. (If you're a developer reading this, you've probably thought Well, duh!. Trust me, clients really don't understand the basis of these skewed expectations.)

What I've just described always occurs. It's the background noise that developers and project managers have to deal with. Deciding between missing features or having defects while being affected by the background noise is the classic tradeoff.

Eclipse tradeoff and mistake

Late in development of Eclipse 3.0, code folding was introduced. There was a high defect rate (and a positive defect acceleration) associated with the feature. It interacted with a significant number of other features and is highly complex (this adds up to a high quality risk). Lo and behold, code folding is significantly buggy.

Should code folding, a much asked for feature, have been included in the release? Check back for a follow up entry on this subject.

Testing and code coverage

More times than I can count, I have heard someone say "We're trying for XX percent code coverage with our unit tests". If XX is 100% are you guaranteed to have no defects?

The first thing that comes to my mind when people start rambing on about unit tests is: What about integration and system testing? There are a number of blog entries out there that talk about the difference between the types of testing. It suffices to say that pure unit testing, even at 100% code coverage, is only going to reveal a small percentage of an application's defects.

So what are you actually trying to do?

Test for a reason -- don't just test to test. You can get into a cycle where you're writing unit test apon unit test and still releasing applications that are defect riddled. First you need to define a quality metric (a way to measure the quality of the application). "As few bugs as possible" isn't it! Ask yourself questions such as: What's a bug or defect? and How is the priority and severity of a defect determined? (e.g. Are spelling errors bugs? They may not be to a developer, but they may be show stoppers to a client.) Everyone wants to jump in and test up the wazoo but the problem is, if you test the wrong things and you don't know what a bug or defect is then your testing is potentially wasting time.

Let's put this another way: let's say you have 80% coverage with all of your tests. What's preventing that 20% that you're not testing from containing 90% of the defects and 100% of the P1 show-stoppers?

Your goal is to identify the cross product of the areas of highest quality risks and the areas that are most important to the user of the application. These areas must have enough testing to ensure that the desired level of quality is met. For example, what if the 20% that you did not test just so happens to be the login page? The client cannot log into the application! So what's the point of testing or even writing the rest of the app? This is an obvious overtrivialization as the login page is an obvious element to test. But in reality, the show stoppers commonly fall into a trivial category. A number of projects that I've performed triage in the past suffered from this problem to an alarming degree. Months of work and testing when into components of the product that, at the end of the day, were not high on the clients list and were overshadowed by trivial defects.

Plan ahead. Identify what determines quality in your application. Test effectively.

Creative Commons License Unless otherwise expressly stated, all original material of whatever nature created by Rob Grzywinski and included in this weblog and any related pages, including the weblog's archives, is licensed under a Creative Commons License.