Wednesday, September 30, 2009

There is some code where I work that defines a constant with the value of System.getProperty("line.separator") and uses it for the line separator. This would be the right thing to do if the code were talking to the local system, but it was using it for internet protocols, which just as wrong as using the system default character encoding for internet protocols. I guess the person who wrote the code was blindly following some rule, meant apply to for coding local applications, that said System.getProperty("line.separator") should be used instead of "\n" (or "\r\n" or whatever) when writing portable code. For internet protocols, it should use whatever the protocol specifies, usually "\r\n", as the line separator, rather than the platform-specific line separator.

Monday, September 28, 2009

There was this weird error in a testing environment in which the connection on the client end got reset most of the time, but not all of the time, for one case, and no others. The server end was not seeing anything. I don't know how long it was being investigated before someone came to me with it. I figured the load balancer was misconfigured.

Why was it happening for this one particular case and no others? The one case was the only one using HTTP DELETE. Everything else was HTTP GET or HTTP POST. So I poked around a little with telnet, and, sure enough, HTTP DELETE caused the connection to be reset.

Why did it sometimes succeed without the connection being reset? If the HTTP DELETE was issued on a reused connection that was originally used for an HTTP GET or HTTP POST and was still around due to keep-alive, the load balancer wouldn't recognize it and kick out the connection.

Friday, September 25, 2009

The second computer language I learned was 6502 machine code. Eventually, I got an assembler.

The assembler would make two passes. It would determine the values of all the labels in the first pass, and then generate the code on the second pass. Sometimes, the assembler would fail with a "phase error". That meant that the value of a label determined in the first pass didn't match what it was supposed to be in the second pass. I think that was because operations on memory from 0000-00ff, the "zero page", were 2 bytes, while operations on the rest of memory took 3 bytes.

So, "LDA LABEL" would assemble to different instructions, depending on whether LABEL was in zero page. If LABEL was 00C0, it would assemble to A5 C0, but if LABEL was 04C0, it would assemble to AD C0 04. I think the phase error was caused when, during the first pass, if LABEL had yet to be determined, would decide that an instruction like "LDA LABEL" would take 3 bytes, but then, if LABEL turned out to be in the zero page, so the second pass would assemble those instruction into 2 bytes, causing subsequent labels to be off by one. One simple fix would be to always assemble such instructions into the 3 byte operation.

Wednesday, September 23, 2009

One particularly stupid bug that was discovered in production code involved code that was made overly complicated. This occurred a number of years ago, so I don't remember the exact details.

What was happening was that some code was taking an id, which was a long, converting it into a String, then doing a new Integer() on that String. Just some code doing stupid pointless things.

It got discovered in production once the ids got too big to fit in the 32-bit int.

After that, the QA systems were set up to start all the ids at 10 billion or something, to see if anything else would get shaken out. I don't think anything else was found, though.

Monday, September 21, 2009

There is an actual coding standards document where I work that seems to be pretty much ignored. The one for Java is pretty long and has some stupid things in it.

3. Set the IDE settings to use 4 spaces instead of tab and use 4 spaces for each indentation.
I have emacs set to always indent with spaces with 4 space increments, so I follow this. A lot of people use tabs.

5. NEVER put a TODO because we may never get back to it. Take extra time or add it to schedule, but do it right then and there.

7. Do not leave commented code in the code base.
These two are routinely violated. I tend to delete code rather than comment it out, but most other people prefer commenting it out.

9. Try to avoid string concate[sic] in the code, especially for the logger statements.
This is the first of many in a theme of being concerned about silly micro-optimizations.

24. Always use StringBuffers for string concate[sic]. This is to avoid memory leaks.
The memory leak statement is just wrong. Some people did follow this rule, leading to a bunch of explicit StringBuffer manipulations that would have better been written as "..." + expression + "..." etc.

8. Maintain 80 columns for the code; however, this restriction should not be applied for the logger statements or expressions that lead to the breaking of strings to next line by concatination[sic].

Remember that one exception to the 80 column rule is to never break up a string to fit it within 80 columns (the ide would never do this).
Example:
the following -
logger.finest("Total destinations obtained by the router" +
"for the operator = " + length);

Should Be :
logger.finest("Total destinations obtained by the router for the operator = " + length);

The first causes 3 new strings to be made (and then garbage collected) every time through this routine.

The second causes 2 new strings to be made
This demonstration of ignorance of implicit StringBuffer (or StringBuilder) usage explains the other silly rules.

27. In For Loop statements like
for (int i = 0; i < relatedContentIDs.length; i++)

always cache the Array/ArrayList length/size one time before the For Loop begins and use this cache value in i the For Loop statement.
Example :
int size = relatedContentIDs.length;
for (int i = 0; i < size; i++)

This is for better Memory Management. However, make sure that the collection size is not changed inside the loop because of addition/deletion of the collection element.
More silly micro-optimization obsession. The "better Memory Management" statement is wrong, but maybe it was an ignorant paraphrase of avoiding instruction cache misses. Anyhow, when using a Collection, it would be less error-prone to use an Iterator.

23. Do not use redundant/unecessary[sic] return statements.
I don't even know what this refers to. The Java compiler disallows unreachable code.

36. Do local caching as well as caching of statics.
I don't know what this means, but it sounds wrong.

Friday, September 18, 2009

So I got called in to looking into this problem of broken images on some pages. The information I was given was pretty vague, so I couldn't just look at the code. I got a Microsoft Word document with a screenshot of some broken images plus a URL and a login and a password. I had never looked at any code behind the site before.

I looked a little at the box it was running on, but had no idea where the page was, because it was hidden behind a mess of struts and tiles, and I don't know how to interpret the struts and tiles configuration to allow me to go from a URL to the actual files making up the page. Furthermore, since all the strings were localized, grepping through all the files for strings in the screenshot didn't work.

I looked at the access logs for the last few days, and, as I suspected, there were no failed requests for images.

So I tried to log in to the site using Firefox, but I never got a page back. I tried using elinks, and got an error page back. Looking at the access logs, I saw that the pages were eventually served up after 90 seconds. Something was failing and timing out. So I went back to Firefox and logged into the site by waiting more than 90 seconds, and then waiting 90 more seconds to go to the page that was supposed to have the broken images. There was nothing there. The system was misconfigured to connect to an inaccessible host, and timing out after 90 seconds. This problem was a separate issue, though it might have been put that way in an attempt to fix the broken image problem. I didn't have (or want) the permissions necessary to fix that configuration issue.

I kept looking through the files for the site and eventually came across some deep subdirectory that had some tiles files that looked like they were responsible for the broken images.

As I originally suspected, the images were from an internal URL. So it all worked fine for the ignorant developers in their development boxes. I could have confirmed that much more quickly if the original report had been better than a screenshot in a Microsoft Word document. I did report my original suspicions as a guess after having seen no failed requests for images in the access logs. My conclusions were still a guess, as I had only seen the files for the site for the first time a couple of hours before, but I recommended proxying the images from the internal host through the front end box.

Then the lead front-end guy came to me and said that the only solution was to copy the images from the back-end package into the front-end package. I disagreed and argued that they should proxy them through the front-end, so that they wouldn't have to maintain them in multiple places. But really, I don't care if they copy the images or if they proxy them, as long I don't have to deal with it again.

Wednesday, September 16, 2009

One thing that I am unhappy with in the codebase that I am currently working on is the way the server piece generates the XML response.

The client piece, which shares code with the server piece, parses the XML into a response object.

The server piece does not use the response object used by the client code when generating the XML response. Rather, it uses another response object that internally has a StringBuffer (which could better have been a StringBuilder) in which the XML is built up, piece by piece with each call.

The first time I had to deal with that code was because it didn't properly encode the special XML characters, <, >, and &. It was also a pain to extend. And calling things out of order would cause invalid XML to get generated. This was the reason for a bug that was recently discovered in a rarely used codepath.

It would make so much more sense to build the response using the same response object used by the client code. And then, only turn it into XML when it's done and ready to be sent to the client.

On the other hand, it generally works well enough, and changing it is stupid busy-work that I don't want to be bothered with. And nobody else really cares about it anyhow.

Monday, September 14, 2009

At my jobs, the overwhelming majority of code does simple, well-defined tasks. Writing that code is dead simple, so I get it done reasonably quickly, which is a lot more quickly than the overwhelming majority of my coworkers.

Here are a few of my guesses as to why lots of people are so slow at writing dead simple code, and why I'm faster at it than they are.
  • They make it more complicated than it should be.
  • They don't know the code well enough not to reimplement stuff that is already there.
  • At design meetings, while I tune out stuff that doesn't involve what I'll be working on, when the discussion does involve what I'll be working on, I'll advocate designs that are easy to implement and fit in the existing design.
  • When adding new features, I'll implement them in a general enough way to make similar future new features easy to add. (And, often, when other people do it, they reimplement it anyhow, instead of taking advantage what I've done.)
  • I'm not terribly fast at typing, but I use many emacs features to speed things up. For example, I use forward-list and backward-list all the time for navigation instead of hitting the arrow keys lots of times. I use backward-kill-word instead of backspacing lots of times. I use universal-argument to do things multiple times. I use keyboard macros and hippie-expand (but not together) for repetitive stuff.

Friday, September 11, 2009

Here's a recent Microsoft slogan: "Life without Walls". Here's a silly jab at it: Windows are just holes in walls. Without walls, there's no reason for windows.

Wednesday, September 9, 2009

Though I've never been heavily involved in testing, automated or otherwise, I've had some thoughts about it. There is a lot of effort put into manual testing where I work, and, late in a release cycle, if there is still a change that had to be put in, then incremental testing would done after that change was put in, as a full test cycle would be pretty expensive. In these cases, the tests that needed to be done would be specified informally, based on what code had been changed.

If a code coverage tool were integrated into the testing process, and if the version control system were also integrated, and if each test case that was run had the code that it covered recorded, then it would be possible to generate a list of test cases that would cover the changed code and a list of test cases that would only cover unchanged code.

There are obstacles for such a scheme, though. It would be expensive with a low return in a rapidly evolving code base. If there are multiple testers running on a single system, getting the coverage recordings would be difficult. If the code is stateful, the unchanged code paths can be affected by the changed code. Changes in configuration files or other external changes can affect the code paths taken. These and other reasons are probably why I've never heard of anything like this before.

Monday, September 7, 2009

When learning how to use an API, I generally start by copying examples. That's probably how most other people start. And the examples are usually enough to get stuff to work. And, I suspect that that's where most people stop, because I see plenty of code that makes clumsy use of APIs. I suspect that these people limit themselves to the examples that they have.

Here's one example, based on actual code using JDBC:

long id = resultSet.getLong(resultSet.findColumn("ID"));

It would be better written as

long id = resultSet.getLong("ID");

but I'm guessing it was written the way it was based on copying something else.

I generally start by copying examples, but once I get a feel for what I'm doing, I'll look at the documentation or the source code. Often, I'll find that there are better ways of doing things if what I'm doing is not exactly what is done in the examples. That's one of the reasons the second and third and so on times I write something, I'll write it better than the first and second and so on time I wrote it.

Friday, September 4, 2009

One horrible interface that I had to use were some calls that took all of their input parameters in a Hashtable, and returned all of their results in another Hashtable. It wasn't as if the parameters or the results were all that dynamic either. The inputs were parameters for a JDBC call of a stored procedure, and the results of the stored procedure were returned. After having to deal with it for a week or so, I wasted a few hours rewriting them to take a data structure for the input and return a data structure. After doing that, I decided to give up on it. It had been that way before I got there, and, as far as I know, it's still that way, years later. Even though it's a pain to deal with and more error-prone than it should be, it still works well enough.

Even worse, though, was this other code that passed this gigantic Properties around. It contained all the HTML form parameters, I think. But, for some reason, someone stuffed all the JVM system properties into it, among other things. And it was also used for passing other parameters around. I even added some more parameters to it when I had to make some stuff work, and I wasn't about to take the time or the effort required to understand the mess of the code there. The Properties was dumped to the debug logs at least twice in the codepath, which made for pages and pages of junk. I still preferred having it dumped in the logs twice than not at all, though.

Wednesday, September 2, 2009

Among the things I have to let go of when on coding in a team is the idea of modules staying independent. Other than coding by copying and pasting code and reimplementing things for which there are existing mechanisms, not much bothers me more than entangling modules with dependencies that they really don't need to have. And it's generally also a matter of being ignorant of existing mechanisms.

But all of these things are inevitable. I don't have the temperament for keeping tight-fisted control over a codebase, so I'll just let it go. Despite the corporate propaganda and HR slogans, it's not like I have a real passion for what my employer does. But it'll be on the list in the back of my mind as long as I'm working on the codebase as something that's not right.