A Bug’s Life

Thomas Hobbes famously said that in humankind's natural state, a man's life tends be "nasty, brutish, and short". I suppose the natural corollary of this is that in technologically advanced societies, life is comparatively "pleasant, peaceful, and long". Of those three things, it is the last one that can be measured most objectively; we see, for example, that the major country with the highest life expectancy at birth is Japan, 85 years currently -- 31 years longer than in Nigeria, say, where it is 54 years.

Recently, I was thinking along these lines about the life expectancy of a bug in various software development projects. Of course, here, the situation is the opposite: the better run a software development project, the shorter the life expectancy of a bug. By the same token, bugs surviving for a very long period of time -- years... decades... -- would be a very strong indication that a project is quite unhealthy. (Or maybe just dead.)

Of course, I was thinking about all this recently because, not long ago, I nailed some bugs in the JavaCC codebase that were surely present for well over 20 years! This one, for example. Of course, as I point out in that article, once a bug reaches a certain age, it graduates to being a known issue -- something that people have tacitly decided that they will just live with indefinitely.

I suppose most people understand why bugs would have a much lower life expectancy in a well run project with a healthy development culture. Here are the main reasons that occur to me off the top of my head:

  1. A well run project tends to have a much cleaner codebase, so there are just far fewer messy corners in which bugs can breed and live.

  2. In a project with a healthy culture, people are honest and can accept criticism, so if a problem is reported, it leads to a pro-active result, i.e. a bug is hunted down and squashed. In a development culture afflicted with morbid nothingburger-ism this just doesn't happen.

  3. A well run project will typically have some tests in place and some concern is taken to have good test coverage. If a bug shows up and is squashed, one typically adds a "regression test" to make sure that the bug cannot come back.

My sense is that, of the three factors above, it is the first two that are really more important. Actually, those two points interact. Yes, there is the will to hunt down the bug, but also bugs just don't have many places to hide in a clean codebase.

A few months ago, I became aware of a bug. I came across it looking through the javacc-users google group but it had already been reported as an "issue", issue 86 to be precise, here. I saw there that the issue was originally reported on 30 March 2019. I looked at the bug report and pretty quickly saw that what was described was indeed a bug. What is quite extraordinary about the issue is that the initial bug report message already contains the exact code snippet where the bug occurs. The reporter, one Hongze Zhang (zhztheplayer on Github) provides the code snippet:

for (int i = 0; min > 1 && i < e_nrw.getChoices().size(); i++) { 
    nested_e = (Expansion)(e_nrw.getChoices().get(i)); 
    int min1 = minimumSize(nested_e, min); 
    if (min > min1) min = min1; 

That code, by the way, as I write these lines 19 months later, is still there unchanged.

I suppose few readers will immediately see this, but most should have the sense to believe me when I say this: the entirety of the bug is that the min > 1 in the first line of that code snippet should be min > 0. I won't take any space here explaining in detail why that is so, but it is. In fact, anybody who was really active in the code would see the issue quite quickly. Besides, any seasoned programmer is familiar with this type of bug, a one-off boundary case in a loop. (Changing the min > 1 to min >= 1 would be fine too.)

Well, when I saw this, obviously my first reaction was to investigate whether this bug was present in my code. Surely, at some point, either in the old FreeCC days over a decade ago, or during my more recent run at resuscitating the project, I had run across this issue and nailed it, no?

Well... no. The bug was still there in my code! I checked the same test case that was reported there, and saw, to my chagrin, that it also failed against my current version of JavaCC21. Well, I immediately fixed it, of course. It also involved simply changing the min > 1 condition to min > 0, except in my version, the bug was now in a FreeMarker template.

Now, I just silently fixed this bug. I'm not even sure whether I even mentioned it in the git commit where I did. Probably I did, but I was working on various things, and the next commit to the repository contained that bug-fix and various other things, so... but understand that, to me, something like this is no big deal. The bug came to my attention and I squashed it and that was that. It so happens that the bug-fix is about the most minimal fix imaginable. It just involves changing a single character! That's all it required in my codebase, and in the legacy project's codebase as well!

Now, getting back to my initial remarks in this post, obviously a bug like this should not survive for over twenty years. But regardless of that, once identified, in a healthy project, the bug is squashed and that is the end of the story. However, the legacy JavaCC project is not a healthy project. (To say the least!) So the story does not end there.

It is absolutely fascinating (albeit in a rather grotesque manner) to read over the discussion under that bug report. Five days after the initial reporting of the bug, a long-time JavaCC user, one Julian Hyde, pipes in that the Calcite project he is involved in has also run into this very same bug.

A day after that, Sreeni Viswanadha, the self-appointed dictator for life of JavaCC, finally responds to this. (His handle there is "new-javacc".) Sreeni's position is that the bug is not a bug. In a way, I suppose that is not so surprising. However, his attempts to explain why the bug is not a bug are really quite surreal. Well, he says things that just make no sense at all. After all, the bug is a bug. The code generation logic does not generate the full code for a lookahead in certain cases, because of this classic boundary condition mistake, that the min > 1 should be min > 0.

Nearly two full months after the initial bug report, on 26 May 2019, Sreeni writes a bizarre message in the thread:

I thought I mentioned above - it's not a bug. An empty action is always taken because the lookahead is trivially true. Again like I said:

[ x ]

is not the same as

{} | x

As best I can tell, [ x ] is in fact the exact same thing as: (x | {}). (In the opposite order, the x is simply unreachable...) But what on earth is Sreeni even saying that has any bearing on this bug report?

Four days after that, on 30 May 2019, so about 2 months after opening the issue, the original bug reporter makes a really exhaustive best effort to try to explain to Sreeni why the bug is a bug. No such luck. Sreeni adamantly insists that the bug is not a bug.

I later noticed that Hongze Zhang later submitted a pull request which sparked more discussion in which Sreeni adamantly maintains that the bug is not a bug. That discussion is, if anything, more surreal than the discussion under the original bug report. Sreeni continues to insist that the bug is not a bug, but then,at some point, also asks the bug reporter to submit a regression test. (Huh?)

But if he does not think this is even a bug, why does he ask for a regression test?

Suddenly, this reminded me of my own interaction with Sreeni back in 2008. Somebody commented to me in private that Sreeni frequently did not seem to even remember what he was arguing and would make arguments that blatantly contradicted something he said in a previous message. By the way, I had thought that the whole discussion was lost down the memory hole, because all of that was hosted on java.net and nobody had taken care to transfer to the mailing list data. Now, I see that it is all available on a site called markmail which is searchable, so anybody can find my rather Quixotic discussions with Sreeni and the rest of these people quite easily.

What I think is quite noteworthy about this more recent incident of the non-bug and my previous encounter with Sreeni is the man's lack of intellectual engagement. In this latest conversation about the bug (which has a one-character fix) there is no sign that he makes any real effort to understand what his interlocutor is telling him. In my own much earlier encounter with this person, he would argue that JavaCC cannot use a template engine like FreeMarker for code generation because one of the features that people like so much in JavaCC is that there is no extra dependency besides the JVM.

But dude, they like the fact that the generated code has no extra dependencies! In JavaCC21 (and in FreeCC before it) FreeMarker is not a run-time dependency for the generated code!

He also argued that it was important not to use any of the newer features in Java (at the time, this was mainly generics introduced in 2004, and this conversation was in 2008) because some people were still deploying on older JVM's. However, that was only an issue for generated code, NOT the code of the JavaCC tool itself! He would continually make very basic mistakes in terms of understanding this distinction -- the code of the tool itself, as opposed to the code that the tool generates. In this more recent discussion, he adamantly insists that something is not a bug but then requests that the person, in addition to his patch to the code, also submit a regression test. But you only need a regression test if this actually is a bug!

Well, 'nuff said, I suppose. I don't know about you, dear reader, but I find all of this extraordinary. (I suppose that, after all the water that has gone under the bridge, I shouldn't, but I still do!) In this specific case, we have this sheer amount of verbiage devoted to this issue that can be resolved with a one character edit to a file! But beyond that, there is Sreeni's stubbornness that something that is clearly a bug is not a bug. Then there is the dogged persistence of the original bug reporter, who, somehow, throughout several months of this, remains polite with Sreeni. (I would not be capable of that...) Not that it does him any good. (Admittedly my getting angry, which I surely would in the same spot, would not do any good either... The result would be the same.) At the time of this writing (late October 2020) the bug is still there.

The issue has been resolved as follows: The bug is not a bug!

Addendum: Philip Helger and his ParserGeneratorCC project

When I decided to resuscitate my work on JavaCC towards the very end of 2019, one of the first people I corresponded with was an Austrian guy, Philip Helger, who had created his own JavaCC fork because (in his words) he was concerned about the state of the existing JavaCC project and its poor code quality. At some point in my dialogue with him, the conversation became surreal (at least from my point of view) because, when I talked about a roadmap for new features, he openly told me that he had no interest in implementing any new features. I mentioned the value of having an INCLUDE directive at the very least, and, as I recall, he replied that that was nice, but he would never use it, because it was important for him to remain completely compatible with the original JavaCC project.

But if he did not want to implement any new features, why did he fork the project?

He mentions poor code quality as a reason, but why would anybody engage in any real code cleanup if not with the goal of facilitating the development of new features?

About three months ago (as of the time of this writing) I figured I would get back in touch with Helger. I considered it possible that he now actually wanted to do some real software development work after all. I figured I wouldn't show up empty handed. Perhaps needless to say, the aforementioned bug was also present in his codebase. I wrote him and told him that he could fix this long-standing bug by changing min > 1 to min > 0 on the appropriate line. Given that I was giving him something of some value, I figured that, at the very least, he would write me a curt thank you note. I received no response to that initial message and wrote him again a couple of weeks later, asking him whether he did receive it. Now, to be clear, I did not have any real intention of harassing the man. And it's not like there was much likely value for me in continuing a conversation with him. But I honestly thought that my first message may have ended up in his SPAM folder, for example.) I also had had enough previous interaction with Helger to have developed the impression that he was fairly polite, so he would be the sort of person to at least respond.

He wrote a response finally saying that it was now August and he was on holiday (which was sacred to him) but that JavaCC was not a priority for him now anyway. He made no mention of the one-character bug fix I had sent him in the previous note.

I just checked back and looked at the appropriate place in Helger's forked codebase. He never applied the one-character bug fix that I sent him and I have to assume that he never will. I suppose that fixing that bug would cause his fork to be incompatible with the original tool, so...

Thus, I close this bug's tale. Earlier this year, I wrote an essay (or an initial draft of one) on this concept of nothingburger-ism. I guess this "bug's tale" could be thought of as another installment in the ongoing nothingburger series. One of the things one sees in nothingburger projects is the way these people will always talk themselves out of doing anything -- or of even trying to do anything. Now, of course, people can do what they want, so, for example, if Mr. Helger wants to create a fork of one nothingburger project to have another basically identical nothingburger project, albeit with different branding, why should I complain?

Well, it still is unfair to me, since somebody actually looking for an actively developed version of JavaCC may find his project instead of mine. At the very least, all of this nothingburgerism reduces the signal to noise ratio out there. But beyond that, the problem with these sorts of people -- I have come to see this, unfortunately -- is that they pretty much invariably develop this seething resentment... OF ME! Though, properly understood, it's not really about me specifically. They would develop the same resentment of anybody who actually tried to do something meaningful in the application space because...

There are a whole bunch of these people who maintain the view that, by picking up this JavaCC thing and trying to do some meaningful work on it, I have wronged them in some specific way -- or I have wronged this Sreeni or other people around him, or something... Well, I guess, properly understood, it is the classic psychological phenomenon of projection. They accuse me of treating them unfairly when the exact opposite is the case. What is one to make of people who, month after month, year after year, and eventually decade after decade, will just pretend that the real work taking place in a space simply does not exist!

It is a very difficult situation because when I (or anybody in this position really) gets openly angry at these people it is taken as further proof of what a terrible person I am. Well, I am busy with the tasks I have set myself and should not spend too much energy feeling resentment of these people. Besides, I don't really hate these people particularly. I mostly feel sorry for them.

That said, I have to admit that I would prefer for them not to exist.