Posts Tagged ‘hack’

Solving the root of the problem

Saturday, December 13th, 2008

I realized that there are two kinds of code maintainers in this world (to tell you upfront, I lied).

The first kind will find workaround around the symptoms of the problems. The second will attempt to find the root cause of the problem and stem it at the source. They are very different kind of maintainers. The first kind will solve problems quickly and appears to have higher productivity. But only the second kind adds value to this entire enterprise of maintaining a piece of software code. In fact the first will destroy values.

Recently, I fixed a major problem with one of my component by fixing its root cause. Then I went on to fix the other components that depend on it. Oh boy. You should have seen the spaghetti code people added to those other components (I wrote the original code of more than half of them, but I don’t recognize them anymore). People wanted to add new functionality, discovered that there is problem with the component I wrote, and instead of fixing it, they try all sorts of workaround. I returned to the component because there is an interesting feature I was asked to write, and I thought it’s one cool features. I hit upon the same problems and so on and so forth. Anyway…

Let’s see. Imagine you have a UI blocks that you use to, well, let’s say, show a tabular data. My code basically wrote a renderer that renders these tabular data with the UI blocks from this mythical framework. I wrote the entire set of data model and the renderer quite some time ago. It exposes many APIs and event hooks for other programmers to write additional features into the table (say, make the table sortable by its headings). Myself and others have added several other features into the table and it gets blown up. The problem is, the renderer assumes a 1 row per logical data, and pretty recently, it is started being used to render a table with several rows per logical data (where several is variable, even within one table). The model and renderer still works properly. But the API that exposed the rendered table (say getRowUIElement, or insertRowOnIndex) fails miserably.

Instead of fixing the root cause (the renderer was not able to provide correct API for these new kind of tables), many chose to re-code their features by accessing the UI blocks directly, thus breaking the abstraction provided by the renderer. That was an easy fix for most of them though they are indeed very awkward (a pluggable feature require user to pass in a function that basically convert a data model index into an actual index in the UI block—if each logical data renders into three rows, this function will convert row 2 from the data model to index 6 in the actual UI block). Needless to say, the code became very messy.

When I encountered the same problem, I was in better position to solve this problem since I knew the code well. So I practically rewrote the renderer, which turned out to be a straightforward job (really, the actual diff was less than 40 lines on the code!). The nightmare began when I started to make all the new features work without hacks (who love ugly code? I don’t.). It was insane! These people are no doubt smart if they could think of all these sort of workarounds. Some of them actually worth a second look if only to appreciate the way the writer slips around the problem altogether. Yet, in almost all cases, it is infinitely easier to just change the renderer, rather than make all these features almost unreadable.

I lack sleep now, but I think it’s a job well done this time around (I don’t have many codes I’m proud of, but this is certainly one of them).

Remember, think simple. Write simple code! Simple code is easy to maintain and easy to understand. Almost always, avoid workaround when possible. Try to discover the root cause.

Now there is the third kind of maintainer (see, I lied). The ones who wrote a workaround, filed a bug against the root cause, either fix the root cause (or get people who are more familiar with the code to fix it), and rewrote the workaround with the proper way. These are the guys you want when handling critical system. Hey, it’s true that solving the root problem is a good thing, but sometime you need to act decisive and fast. Workarounds may be the only way to push that piece of bugfix out before too many people got affected. But remember to fix the root cause! Remember, you do not want to build workaround around a workaround around another workaround (which seems to be pretty common nowadays).

John Resig’s degrading script tags

Thursday, November 13th, 2008

I just re-discovered this interesting Javascript trick from John Resig’s blog (link here). Under usual circumstances, I would frown when I see eval in Javascript code, the one construct I tried to avoid like a plague. But I don’t resist interesting usage of eval. This is one of them. Basically, the trick allows you to have a script tag with an embedded Javascript, like this:

<script src="some_js_file.js">
  // Do something with the loaded file.
  callSomething(); ...
</script>

Interesting huh? To make this work, the loaded script file itself will end with the a two-liner that basically find the script element and evaluates the innerHTML of the element:

var scripts =
    document.getElementsByTagName("script");
eval(scripts[scripts.length - 1].innerHTML);

Ingenious. As an added bonus, the content of the script element will not be executed if the Javascript fails to download (since, obviously, the last two lines that we just added won’t be executed).

Also interesting is how this hack utilizes the synchronous property of Javascript loading. In most browsers except the most recent ones (most of them still in beta/nightly), whenever a script tag is encountered, the rendering stops until the script is downloaded and executed. That means by the time the script is loaded, it knows for sure that the script tag it is in is the last one on the DOM since nothing else has been rendered. Thus, you can access the element by using the above method (see scripts[scripts.length - 1] part of the code).

I’m a little bit behind with how newer browsers download its Javascript. I suspect that the method above may not work. I guess it’ll depend on the heuristics the browsers use to make Javascript download not blocking. I heard that Firefox will actually assume that the script does not do anything to the DOM and continues rendering, in which case the above technique may not work. (I’m sure I’m missing something, probably there are some heuristics that FF used that I’m not aware of.)

Well, still, it is an ingenuous way of utilizing eval.

min-width and max-width with Javascript (IE Hack)

Wednesday, November 5th, 2008

So, recently some of the pages I wrote require me to apply certain CSS feature not supported by IE6. The max-width and min-width. They are what they say, provide a minimum and maximum width requirement for a box element. They work like a charm in Firefox, Webkit, and Chrome, just not IE.

Well, readers who are familiar with cross-browser (in)compatibility problems probably would have encountered similar situations when features work differently in different browser. Short hacking and Google search usually helps a lot. Not in this case. Most pages (well, I only went through the top 20-40 pages in Google search, with several combination of keywords) mention the pretty standard CSS expression hacks, like this one. It’s not good enough. CSS expressions are slow. Intuitively, it should run on every resize. But that’s not the case. Steve Souders research into CSS expressions are still fresh in my mind. Expressions executes on every page resize and mouse move events1! Not good!! Definitely not good!

The next obvious move is to use Javascript and register your own handler. Unfortunately, either the CSS expression hacks are too popular or nobody publishes Javascript code that does this, I could find no results in Google. So I didn’t waste any more time and roll out my own hacks. The hacks is darn simple (okay, not darn simple, but not too difficult either). The idea is to enclose the div where you want to set your max-/min-width with a wrapper div that should have a width of 100%. This wrapper div will take the form of the enclosed div should you not place max-/min-width. Subsequently, register a listener to window’s resize that check whether this wrapper div’s width exceeds the max-width or decreases below the min-width and then set the width of the enclosed div appropriately. The crucial part of the code is the following (I simplify it to register directly on window.onresize, which is obviously a bad code, but illustrates the concept well).

// This one for max-width
window.onresize = function() {
  enclosedDiv.style.width =
      wrapperDiv.clientWidth > maxWidth + 1 ?
          maxWidth + 'px' : 'auto';
}

// And this one for min-width
window.onresize = function() {
  enclosedDiv.style.width =
      wrapperDiv.clientWidth < minWidth + 1 ?
          minWidth + 'px' : 'auto';
}

Replace enclosedDiv and wrapperDiv by the appropriate elements as mentioned in earlier paragraph. Btw, You only need to do this in IE. In other browsers, just set the CSS properties appropriately.

Notice the maxWidth + 1 and minWidth + 1? Without the + 1, IE will lock because of race condition. (This page also mentioned this problem, so it seems like a common problem, and not because of my incompetence.)

Right now I’m writing a library that is specialized for this problem. Look forward for it soon. The libraries will handle multiple resize handlers graciously (well, technically, you only need 1 resize handler that resize all the divs in the page in the correct order; and you define the order). The library should also handle creation of the wrapper for IE automatically. In fact, 90% of the library is done, but I’m not confident of releasing it because I haven’t actually tested it at all! I’m going to replace my hacks with the library, if it works fine after we test it, it’ll show up in this blog.

Javascript is fun (yes, cross-browser compatibility is painful, but isn’t it the thing that makes Javascript colorful?)!

  1. http://developer.yahoo.net/blog/archives/2007/07/high_performanc_6.html []