tag:blogger.com,1999:blog-69096344390535228262024-03-05T02:42:22.255-08:00Joshua GoJoshua Gohttp://www.blogger.com/profile/00918550190150508968noreply@blogger.comBlogger88125tag:blogger.com,1999:blog-6909634439053522826.post-24655424522136191122010-08-08T16:00:00.000-07:002011-08-29T21:33:46.622-07:00Ways to modularize your Ruby code<p>In this post, I'll recommend several ways to properly organize code in Ruby projects. I'll also explain my reasoning and how I arrived at each solution.</p><p>Much of this builds on the excellent work of others in the Ruby community, and I've linked to these other writeups as appropriate in case you want to know more.</p><p>This subject is of particular interest to anyone who is packaging a gem library, but it should be handy to anyone who wishes to organize code within any Ruby project.</p><h4>Namespacing with modules</h4><p>Let's say you've got an isolated piece of functionality that doesn't depend on anything else, such as a simple key (random string) generator. You intend to always call it with its full namespace, so you don't get it mixed up with similarly named functions. To make something that you can call using <tt>Rigatoni::KeyGenerator.generate_key(n)</tt>, you'd use the following code.</p><pre class="prettyprint">module Rigatoni<br /> module KeyGenerator<br /> def self.generate_key(key_length = 5)<br /> puts "generate_key() called with length #{key_length}"<br /> end<br /> end<br />end</pre><p>Note that I defined the function as <tt>self.generate_key()</tt> — the <tt>self</tt> keyword is crucial.</p><p>That's a subtle but critical difference. If I didn't include the <tt>self</tt>,<br />I'd have to include <tt>Rigatoni::KeyGenerator</tt> first and then run the<br /><tt>generate_key()</tt> function, which isn't what I want to do.</p><p>If I want to save myself a little typing while having some semblance of a namespace to qualify my method call, I can do this:</p><pre class="prettyprint">include Rigatoni<br />KeyGenerator.generate_key()</pre><p>This is the best way to modularize a self-contained piece of Ruby code that's meant to be called independently.</p><h4>Extending functionality through modules</h4><p>The most common way in which I've seen Ruby modules used is to extend the functionality of existing classes. This is where they're used as <a href="http://www.rubyfleebie.com/an-introduction-to-modules-part-2/">mixins to extend a Ruby class</a>.</p><p>Even then, there are two ways to extend the functionality of a Ruby class with modules: instance methods and class methods. By default, including a module in your class definition will give you new instance methods.</p><p>If you want to define class methods in a module, you have to jump through some extra hoops.</p><pre class="prettyprint">module Moo<br /> module Ham<br /> def self.included(base)<br /> base.extend(ClassMethods)<br /> end<br /> <br /> module ClassMethods<br /> def foo()<br /> puts "foo called"<br /> end<br /> end<br /> <br /> def bar()<br /> puts "bar called"<br /> end<br /> end<br />end<br /> <br />class Bacon<br /> include Moo::Ham<br />end</pre><p>The module we define above is <tt>Moo::Ham</tt>. We have a dummy class, <tt>Bacon</tt>, which includes <tt>Moo::Ham</tt>. It includes both class and instance methods, which we can run with the following example code.</p><pre class="prettyprint"># Class method.<br />Bacon.foo<br /><br /># Instance method.<br />b = Bacon.new<br />b.bar</pre><p>This is a longtime Ruby idiom, and John Nunemaker unpacks this in his post, <a href="http://railstips.org/blog/archives/2009/05/15/include-vs-extend-in-ruby/">Include vs. Extend in Ruby</a>.</p><h4>Namespaced classes (for state-dependent modularity)</h4><p>In the examples up until now, we've dealt only with methods that could be run independently without needing something already in place.</p><p>Let's say all you're using to modularize your code is Ruby modules. Then you start to notice that a lot of the methods have the same argument passed in. Either that, or you find yourself setting up or populating some variable again and again in order to perform the work.</p><pre class="prettyprint">module Moo<br /> module Ham<br /> def some_func_foo(access_key, api_key, x)<br /> # Set things up with access_key and api_key.<br /> <br /> # Perform work with x.<br /> end<br /><br /> def some_func_bar(access_key, api_key, y)<br /> # Set things up with access_key and api_key.<br /> <br /> # Perform work with y.<br /> end<br /><br /> def some_func_baz(access_key, api_key, z)<br /> # Set things up with access_key and api_key.<br /> <br /> # Perform work with z.<br /> end<br /> end<br />end</pre><p>When you start to notice these things, it's time to turn your module into a class.</p><pre class="prettyprint">module Moo<br /> class Ham<br /> def initialize(access_key, api_key)<br /> @access_key = access_key<br /> @api_key = api_key<br /> <br /> # Do other stuff here to set up what you need.<br /> end<br /><br /> def some_func_foo(x)<br /> # Perform work with x.<br /> end<br /><br /> def some_func_bar(y)<br /> # Perform work with y.<br /> end<br /><br /> def some_func_baz(z)<br /> # Perform work with z.<br /> end<br /> end<br />end</pre><p>This keeps us from duplicating code. Note that we end up having to instantiate a class because the methods depend on the initial setup work being done, but our code is leaner and meaner this way.</p><pre class="prettyprint"># Old way. Gross.<br />Moo::Ham.some_func_foo(access_key_one, api_key_one, x)<br />Moo::Ham.some_func_bar(access_key_one, api_key_one, y)<br />Moo::Ham.some_func_baz(access_key_one, api_key_one, z)<br /><br /># New way. Nice.<br />mh = Moo::Ham.new<br />mh.some_func_foo(x)<br />mh.some_func_bar(y)<br />mh.some_func_baz(z)</pre><p>The major takeaway is that modules are not the only way to modularize our Ruby code.</p><h4>Handling dependencies</h4><p>Other times, you'll have methods that aren't state-dependent and which don't belong in a class. But they'll have a different kind of dependency: on external gem libraries being present.</p><p>Say you have a module, <tt>Foo</tt>, which you define in a file called <tt>foo.rb</tt>.</p><pre class="prettyprint"># foo.rb<br />module Foo<br /> ABACAB="abacab"<br /><br /> def print_foo<br /> puts "foo"<br /> end<br />end</pre><p>Then say you have another module, <tt>Bar</tt>, which depends on <tt>Foo</tt>.</p><pre class="prettyprint"># bar.rb<br />module Bar<br /> def self.bar<br /> require 'foo'<br /> include Foo<br /><br /> puts ABACAB<br /> print_foo()<br /> end<br />end</pre><p>You try to be a good citizen by pulling in <tt>Foo</tt> only when you need it. So now you're ready to run <tt>Bar.bar()</tt> from another script, <tt>run_bar.rb</tt>.</p><pre class="prettyprint"># run_bar.rb<br />require 'bar'<br /><br />Bar.bar()</pre><p>But then you run it, and you get an error with baffling and mixed results. The line referencing the constant <tt>ABACAB</tt> ran perfectly fine; the call to <tt>print_foo()</tt> failed. Let's move the <tt>require</tt> and <tt>include</tt> of <tt>Foo</tt> outside <tt>Bar</tt>'s module definition and see what happens.</p><pre class="prettyprint"># bar.rb<br />require 'foo'<br />include Foo<br /><br />module Bar<br /> def self.bar<br /> puts ABACAB<br /> print_foo()<br /> end<br />end</pre><p>When we run <tt>run_bar.rb</tt> again, we see that this works for us.</p><p>It seems a little wasteful to pull in <tt>Foo</tt>, but given the results of our little experiment here, we've got no choice: if we want to call methods in the <tt>Foo</tt> namespace, we've got to pull in <tt>Foo</tt> at the top — outside the module definition and outside the method definition.</p><p>Besides, if we're pulling in <tt>bar.rb</tt>, our real intent is to go after the full functionality that <tt>Bar</tt> provides. The functionality that <tt>Bar</tt> gives us depends on <tt>Foo</tt> anyway, and won't work at all in its absence. We're not really being wasteful.</p><h4>Summary</h4><p>There are two major ways to modularize our Ruby code: modules and classes. Despite the name, modules are not the only way to modularize. Use classes if the proper behavior depends on state.</p><p>Start with a module by default. If you find yourself creating too many methods that take in the same parameter again and again, turn your module into a class which populates the initial values when you instantiate it.</p><p>When a module depends on other libraries, pull these other libraries in at the top of the file, outside the module definition. Calls to methods in these libraries won't be found otherwise.</p>Joshua Gohttp://www.blogger.com/profile/00918550190150508968noreply@blogger.comtag:blogger.com,1999:blog-6909634439053522826.post-38586591559313222782010-07-14T13:59:00.001-07:002010-07-14T14:04:25.117-07:00JavaScript: Checking for undeclared and undefined variables<p>Up until now, the JavaScript I've written has typically been in pursuit of a bigger goal. This means that I worked around language quirks on the spot and moved on. I never stopped to consider the nitty gritty details of the language for commitment to long-term memory, because I wanted to get a working end product.</p><p>Recently, someone asked me about the best way to check for undefined JavaScript variables. I found myself at a loss, which was alarming to me since doing these kinds of checks is immensely important, practical, and a frequent fact of everyday programming no matter what programming language you're using.</p><p>The fact of the matter is that most of the time, I know where my variables are coming from. Something like this would work:</p><pre class="prettyprint"> var x;<br /><br /> if (x)<br /> x.some_method();<br /> else<br /> alert("we can't use x");<br /></pre><p>In the vast majority of cases where I had to perform a null check, I knew that the variable was declared somewhere because it was of my own making.</p><p>What if you're counting on something having been declared somewhere else and being there? Let's try this.</p><pre class="prettyprint"> if (y)<br /> y.some_method();<br /> else<br /> alert("we can't use y");<br /></pre><p>In this case, you should get a <tt>ReferenceError</tt> thrown. You'll see this if you're using the Firebug JavaScript console or the developer tools in <a href="http://developer.apple.com/safari/library/documentation/appleapplications/conceptual/safari_developer_guide/2safaridevelopertools/safaridevelopertools.html">Safari</a> or <a href="http://www.chromium.org/devtools">Chrome</a>.</p><p>After much experimenting and looking around the web, here's my definitive, reliable, robust, cross-browser way to perform this kind of check:</p><pre class="prettyprint"> if (typeof(z) != "undefined")<br /> z.some_method();<br /> else<br /> alert("we can't use z");<br /></pre><p>It also works for when you're the one in charge of declaring a variable, too.</p><pre class="prettyprint"> var w;<br /><br /> if (typeof(w) != "undefined")<br /> w.some_method();<br /> else<br /> alert("we can't use w");<br /></pre><p>So that fixes our problem. But look closer, and you may have a couple of nagging questions.</p><p><strong>Why in the world would you be referencing a variable you didn't declare?</strong> If we only ever had to deal our own code, we should always know where the variables that we're using are coming from. But that is not the case for many of us; we rely on using others' code, third-party modules, or frameworks all the time.</p><p>An easy example is if you want to send debugging output to <tt>console.log</tt> in Firebug. When Firebug is disabled, <tt>console</tt> isn't a declared JavaScript object. (Safari and Chrome have their developer tools more integrated so this problem doesn't appear.) It helps to first check if there's a debugging console available before you start writing to it.</p><p>You could also find yourself in a situation where you're expecting some object to be initialized because it's an external library. If it's loaded over the network, and loaded separately from the rest of your code, you'll want to recover gracefully if it's not available for some reason (like the remote server is down).</p><p><strong>Why do we have to compare to the string "undefined" instead of the keyword?</strong> Because typeof() always returns a string.</p><pre class="prettyprint"> console.log(typeof(j)); // "undefined"<br /> console.log(typeof(typeof(j))); // "string"<br /></pre><p><strong>This seems too simple/short. Will it work in all web browsers?</strong> This will work in any modern web browser that supports JavaScript. Just to be thorough, I've personally verified that it works as expected in Internet Explorer 8, Firefox 3.6, Safari 4, and Google Chrome 5.0.</p><p>Checking for undefined variables in JavaScript in this way has been around for a long time, and <a href="http://constc.blogspot.com/2008/07/undeclared-undefined-null-in-javascript.html">several</a> <a href="http://stackoverflow.com/questions/27509/detecting-an-undefined-object-property-in-javascript">other</a> <a href="http://www.webmasterworld.com/forum91/1268.htm">sources</a> suggest this method. I wrote this up mainly as an extended explanation for why things are the way they are, and to explicitly address quirks like <tt>typeof()</tt> always returning a string.</p><p><strong>What if it could also be null in some cases?</strong> Then you add a check for null. I've limited the check to checking for "undefined" to keep the explanation focused, but yes, in reality you'll want to do an additional check.</p><pre class="prettyprint"> var k;<br /><br /> if (typeof(k) != "undefined" && k)<br /> k.some_method();<br /> else<br /> alert("we can't use k");<br /></pre><p>The check for null is pretty straightforward; it's "undefined" that trips us up. Note that, as written, this code will only proceed to check for the null condition if <tt>k</tt> has been defined. This is because of <a href="http://en.wikipedia.org/wiki/Short-circuit_evaluation">short-circuiting</a>: if the first condition is true, there's no need to evaluate the rest of the other parts of the conditions.</p><p>When it comes down to it, the key insight is that JavaScript has "undefined" variables in two senses: first, when they're not even declared, and second, when a variable has been declared but hasn't been assigned a value. This is when it really is undefined in the strict sense.</p><p>We also saw that <tt>typeof()</tt> will always return a string, which means you need to compare its result to a string when you're running a conditional test.</p>Joshua Gohttp://www.blogger.com/profile/00918550190150508968noreply@blogger.comtag:blogger.com,1999:blog-6909634439053522826.post-57350379385769548192010-05-21T23:13:00.001-07:002010-05-21T23:13:18.271-07:00The status quoSometimes there are good but non-obvious reasons for keeping the status quo. Competing interests may have, over time, been balanced and counter-balanced to form a functioning ecosystem. And as with any ecosystem, reducing the functioning whole into its constituent parts in the foolish pursuit of extracting isolated benefits is an intractable problem.<br /><br />Other times, the status quo represents a deeply flawed system, fundamentally broken at the core. Such a system is characterized by people in power whose actions are driven primarily by the overriding interest in preserving their own favorable position, to the recurring detriment of the less favored.<br /><br />The tough part is looking at a situation and making the call as to which one of these models applies. In some cases, the status quo may be made up of both a well-balanced system with benefits to all, as well as a rotten portion entrenched for no good reason but the preservation of the holders of power. Fixing what's broken is a separate problem, and we should be concerned with fixing the problem only after we have correctly identified it. Joshua Gohttp://www.blogger.com/profile/00918550190150508968noreply@blogger.comtag:blogger.com,1999:blog-6909634439053522826.post-22612756386057144902010-04-17T22:08:00.001-07:002010-04-17T22:14:15.033-07:00Slowing down as an immersive experienceThrough my entire time as a student, I never used <a href="http://www.cliffsnotes.com">Cliffs Notes</a> or <a href="http://www.sparknotes.com/">SparkNotes</a> in place of assigned reading. I made it a point to read every book I was supposed to, down to the last word.<br /><br />The trouble for me was that everyone else who resorted to these "study guides" knew enough about the real books to do well in their classes. From the perspective of efficiently using my time to reach the objective of a good-enough, basic understanding for writing essays, discussing the books in class, and impressing teachers, I lost out.<br /><br />I remember one book in particular. <em>Crime and Punishment</em> really broke a lot of people, many of whom were like me and had, up until then, insisted on reading the book and not the summary booklet. But I was determined. I was hellbent on savoring that book and milking it for all it was worth — calculus, biology, and economics be damned.<br /><br />As expected, I ended up with the same general recollection of the book's contents as the more reasonable folks who resorted to the Cliffs Notes.<br /><br />But what I remember most poignantly is the <em>feeling</em> I had while reading that book. I became immersed in it to the point that I'd feel the cold sweat, delirium, and ever-present sense of dread that hounded <a href="http://en.wikipedia.org/wiki/Rodion_Romanovich_Raskolnikov">Raskolnikov</a> (the protagonist in the story) as he ran from the authorities and lived each day with the burden of his guilt.<br /><br />I highly doubt that it was even <em>possible</em> for anyone who read the Cliffs Notes to experience that.<br /><br />Whenever I approach any new text, it's in pursuit of that kind of total immersion. I know that I'm not going to remember every detail of what I read, but my ultimate takeaway from all the things I read is not the knowledge to be gleaned, as if I were some kind of one-man strip mining operation for facts. It's in putting myself in a position to be shaped and influenced; I want to see how the mind of another person works by temporarily forcing my mind into the mold of their thought process.<br /><br />In order to do this, it's absolutely necessary to slow down.<br /><br />Slowing down allows real life to happen, as events inject themselves into my reading experience. If I take a long enough and serious enough text and stew over it, its applicability quickly becomes apparent when I frame its ideas in the context of whatever I happen to be dealing with in my life.<br /><br />On the flip side, the <em>shortcomings</em> of a text <em>also</em> make themselves apparent when I slow down and let life happen in between. The opportunity afforded by this perfect setup allows me to weed out and carefully qualify newfangled notions, because when it comes to novel and interesting ideas, I tend to give them the benefit of the doubt and adopt them a little too eagerly. A tempering influence helps, and a tempering influence provided by direct observation is the best that anyone could ask for.<br /><br />Slow down to savor the richness of a text, and make time for ideas to run up against real situations. That way, you'll get to see how valid these ideas really are.Joshua Gohttp://www.blogger.com/profile/00918550190150508968noreply@blogger.comtag:blogger.com,1999:blog-6909634439053522826.post-2295795808753659582010-02-11T07:12:00.000-08:002010-07-14T14:52:28.216-07:00JavaScript: Variables in regular expressionsOften, you want to look for a particular pattern within a string. Let's say you know that you want to look for the string "revenue" inside a given string.<br /><pre class="prettyprint">function match_string_for_revenue(string_for_searching)<br />{<br /> return string_for_searching.match(/revenue/gi);<br />}<br />var our_string = "We are looking for ReVeNuE.";<br />alert( match_string_for_revenue(our_string) );<br /></pre><br />You can store the pattern as a regular expression in its own JavaScript variable, which makes your code more readable and its intention better known.<br /><pre class="prettyprint">function match_string_for_revenue(string_for_searching)<br />{<br /> var pattern_to_look_for = /revenue/gi;<br /> return string_for_searching.match(pattern_to_look_for);<br />}<br />var our_string = "We are looking for ReVeNuE.";<br />alert( match_string_for_revenue(our_string) );<br /></pre><br />But what if you want to generalize this matching and be able to search for any given pattern? Can you make it vary based on a parameter given, instead of keeping it in the code?<br /><pre class="prettyprint">/* NOT GOING TO WORK */<br />function match_string(string_for_searching, valuable_substring)<br />{<br /> var pattern_to_look_for = / + valuable_substring + /gi;<br /> return string_for_searching.match(pattern_to_look_for);<br />}<br />var our_string = "We are looking for ReVeNuE.";<br />alert( match_string(our_string, "revenue") );<br /></pre><br />Well, that didn't work. What happened? That last alert() should have given you a null, which means that it wasn't able to match the pattern as expected.<br /><br />Are we stuck? No. We just have to work around this using <tt>eval()</tt>.<br /><pre class="prettyprint">function match_string(string_for_searching, substring)<br />{<br /> eval("var pattern_to_look_for = /" + substring + "/gi");<br /> string_for_searching.match(pattern_to_look_for);<br />}<br />var our_string = "We are looking for ReVeNuE.";<br />alert( match_string(our_string, "revenue") );<br /></pre><br />This method will also work with Ruby and any other language that gives you the option to evaluate the contents of a string as code in that language.<br /><br />Sometimes you'll find yourself having to generate code dynamically from a given string because there's no other way but to write real code in that language in a way that goes beyond merely passing in a string. If the language doesn't support having a string in there during the course of its normal operation, that's where <tt>eval()</tt> can come in handy.<br /><br /><strong>Important Update (July 14, 2010)</strong>: It's important, as <a href="http://m-austin.com">Matt Austin</a> points out, to consider the security implications of using <tt>eval()</tt>. See his comment below. Additionally, <a href="http://coderrr.wordpress.com/">coderrr</a> rightly pointed out that there's a more proper way to do it using <tt>RegExp</tt>:<br /><pre class="prettyprint">function match_string(string_for_searching, substring)<br />{<br /> return string_for_searching.match(new RegExp(substring, "gi"));<br />}<br /><br />var our_string = "We are looking for ReVeNuE.";<br />alert(match_string(our_string, "revenue"));<br /></pre><br />This is the way I'd recommend doing it in the future. The specific use of <tt>eval()</tt> that triggered my idea for this post was free from the security concerns Matt raised, but seeing as how this post describes general usage, please use <tt>eval()</tt> sparingly or consider security implications of the kind Matt described.Joshua Gohttp://www.blogger.com/profile/00918550190150508968noreply@blogger.comtag:blogger.com,1999:blog-6909634439053522826.post-6198533239187952632010-02-02T07:50:00.000-08:002010-02-02T07:56:21.653-08:00The costs of configurable settings in your web applicationBy and large, the received wisdom when it comes to configurability and flexible settings for web applications is that more is better. Making something configurable frees up the engineer from having to make mundane (but necessary) changes that can be left to other people. It's desirable to change the state of software without having to rebuild or redeploy the code. There are a lot of obvious benefits to making software configurable, and these benefits are plain to all.<br /><br />What I'm going to focus on here are the <em>costs</em> of configurability, based on my experience and observations over five years of writing web applications full-time. These costs apply to other kinds of software beyond the realm of web applications. I just choose to limit the scope of discussion to web apps because these costs manifest themselves the most when there's a fast development cycle and rapid iteration. <br /><br />The following is intended to be a comprehensive, itemized breakdown of the costs of making an application setting configurable by the user. By itemizing and breaking these out separately, my intention is to make it easier to count the costs. For any given web application, it will be easier to see which of these apply, and to what extent.<br /><br /><strong>The up-front cost of developing it as a setting.</strong> It comes as no surprise that the up-front cost is higher and requires more time than hardcoding, and is thus the most immediate and salient concern for the lazy programmer. If you're treating it as an investment and the setting is important, then of course it will pay off later. But if you're moving quickly and pushing out new features every week or even several times a week, the up-front costs of the things you're doing will be very important.<br /><br /><strong>The cost of developing visual flexibility to accompany a setting.</strong> If your setting involves anything that has an impact on the visual presentation of any pages, be sure to consider that. For example, if you want to support different widths for a column of text, it is probably in your best interest to delineate discrete acceptable values rather than allowing continuous values. You'll know how to handle widths of 100, 150, and 300, for example, and you'll have to test them out to see how they look, but you probably don't want to allow just any width. Other situations may warrant a limited range on values; these values can be continuous, but restricted within a certain range. For example, if you have a setting for the maximum length of a summary text field, you'll probably want to test how your layout handles the minimum and maximum lengths of the summary text.<br /><br /><strong>The cost of attention when sifting through all the other configurable settings.</strong> Even if it's relatively easy to change a setting, your love of settings probably means you've got even more settings. If you take the simple approach in laying these out in a list of settings for you to scroll through, there's a cost you incur. The cost of paying attention to this setting in the midst of all your other settings is one reason to stop and think whether you really want it to be configurable, but it's also one way you can force yourself to be more discerning and make only the most critical things configurable.<br /><br /><strong>The cost of informing all stakeholders.</strong> This becomes more important as the size of your organization grows. The part to be concerned about here isn't in letting everyone know just that the setting exists or where to find it; that's a simple matter and thus negligible. It's in telling everyone involved and making sure that they understand what the setting does, whether it has any side-effects, valid values for the setting, and whether it's a critical setting that should be changed with caution. It's surprising how easily people forget, especially if it's a rarely used setting. <br /><br /><strong>The cost of determining a default fallback value.</strong> How failsafe do you want your code to be? Chances are you'll have some situations — such as bringing up a greenfield installation, restoring from backup, and database unavailability — when you won't have access to the setting. If the setting isn't available for some reason, what kind of default value do you fall back to and how do you proceed? Will this default continue to hold true and be valid as your software matures?<br /><br /><strong>The cost of abstraction and the cognitive load associated with translating it to concrete results.</strong> To make some things configurable, it may be necessary to generalize and turn the setting into an abstraction. Take the problem of content management functionality on your custom webapp. You may end up having to manage embedded Flash SWFs, images, simple blocks of text, or any combination of the above. Many content management systems call these "page elements" to encapsulate their full versatility, but this kind of abstraction is another step away from merely "image block" or "text block." What's the URL for the "graphical element" — in other words, the URL for the image? In using a general tool for a specific case, you end up having to refer to things in the most general terms first and translating them into their specific names, rather than calling them just what they are. This costs you time. Time is money, and it adds up.<br /><br />Now, given these costs, what is one to do? In software engineering and in politics, we resort to compromise. For example, we could hide a setting from users so that only developers or sysadmins can change it. This way, you maintain the configurability that you want and avoid many of the unnecessary costs listed above.<br /><br />It may also help to know the nature of each setting: are the acceptable values discrete or continuous? If they're discrete, such as officially supported sizes for visual elements, you can impose restrictions on values that you accept so that it's harder for the users of your application to shoot themselves in the foot.<br /><br />Ultimately, in any discussion of costs, no matter how focused and limited in scope, we must remember to consider the benefits. Neither the costs nor benefits considered alone give us a complete view of any situation, and each must be considered to give context to the other. The difference is that costs are difficult and unpleasant to pinpoint and isolate. They don't receive the attention they're due, but who could blame us? We prefer to look on the bright side and think instead about reaping benefits. We enjoy watching our projects and our companies grow. But it's by being aware of our costs and knowing what hampers us that we free ourselves up to build more of what we want.Joshua Gohttp://www.blogger.com/profile/00918550190150508968noreply@blogger.comtag:blogger.com,1999:blog-6909634439053522826.post-76691338378825306552009-12-16T13:08:00.001-08:002010-01-21T18:28:23.105-08:00The reasoning behind LumberjackSo why, in 2009, in a world of RIA frameworks, web-based applications, and a wide variety of blogging engines to choose from, would I write a desktop application in Java targeted exclusively for one company's proprietary blog platform?<br /><br />First of all, it <em>was</em> tempting to write this as an <a href="http://get.adobe.com/air/">Adobe AIR</a> application. It would have fit my requirement that it be cross-platform <em>and</em> run as a desktop application, but I've never written anything with Adobe development tools before. Given the limited time I had on weekends to work on it, I wanted to get something written as quickly as possible rather than spending all my time learning a new platform. With Java, I could just hit the ground running, and it was just a matter of referencing the Swing-specific documentation. It boiled down to what was expedient and familiar because it would allow me to build something quickly.<br /><br />With respect to the issue of making this application web-based, the main point is that I didn't want to start up a browser just to create new posts on Blogger. There's the Blogger Dashboard for that. Now, it's true that one could write a slimmer, lighter, faster-loading web-based client for Blogger without all the heavy clutter of the Blogger Dashboard, but it would still require that I start up a web browser; in the end, I wouldn't end up using it much. I wanted to build something that I would use and keep on using. (I have also been writing web applications for the past five years, and thought it would be fun to write something that ran on the desktop for once.)<br /><br />Finally, there's the issue of this program being a client specifically for one company's proprietary blogging engine. Why not make it work for <a href="http://www.wordpress.org/">WordPress</a>, <a href="http://www.typepad.com/">TypePad</a>, <a href="http://www.posterous.com/">Posterous</a>, and all the other major and popular blogging platforms? For that matter, why am I still using Blogger when there are so many newer, slicker platforms to choose from? I have to admit that I was tempted to switch out from Blogger — WordPress and Posterous in particular have impressed me the most — but when it comes down to it I'd rather have my blog on Google infrastructure than anywhere else. With that said, not everyone feels this way, so they choose newer, snazzier blogging software — which is how Blogger ended up neglected by hobbyist software developers. To this day, I still can't even log in to my Blogger account using <a href="http://drivel.sourceforge.net/">Drivel</a>, a desktop client with support for all sorts of blogging engines.Joshua Gohttp://www.blogger.com/profile/00918550190150508968noreply@blogger.comtag:blogger.com,1999:blog-6909634439053522826.post-84426254852951040942009-12-15T18:59:00.001-08:002010-01-21T18:28:34.711-08:00Introducing Lumberjack, a desktop client for Google BloggerEarlier this year, I <a href="http://joshua-go.blogspot.com/2009/02/blogger-client-as-java-swing-desktop.html">mentioned</a> that I was working on a desktop application, a client for Google Blogger, written in Java with the Swing GUI toolkit.<br /><br />I recently found some time to work on the enhancements that I said I wanted to do during my last update, and I'm ready to release the application to the world. Get a ready-to-run file at the <a href="http://github.com/joshuago/lumberjack/downloads">Lumberjack download page</a>. Just download the JAR file, save it, and run it; it will work as long as you have a Java runtime installed. If you're running Windows, Mac OS X, or Linux, you probably do.<br /><br />If you have trouble logging in, you may have to <a href="https://www.google.com/accounts/DisplayUnlockCaptcha?service=blogger">solve a Google CAPTCHA</a> first. You should only have to do it once.<br /><br />If you're a developer, you'll find the source code at the <a href="http://github.com/joshuago/lumberjack">Lumberjack GitHub repository</a>. Simply run <tt>ant</tt> to build the application. (Yes, I put a lot of effort into getting this one-step build working.)<br /><br />Patches, questions, and suggestions are most welcome. Just leave a comment on this post.Joshua Gohttp://www.blogger.com/profile/00918550190150508968noreply@blogger.comtag:blogger.com,1999:blog-6909634439053522826.post-56169121334014888522009-12-14T18:58:00.001-08:002009-12-15T09:28:57.109-08:00Idealism and retreatWhen I'm by myself, when it's quiet with no distractions pulling me in a million directions, I automatically start to dream and envision great things. All of my scattered realizations and fleeting memories somehow just coalesce to give me more clarity about myself and what I'd like to do. It's as if a master blueprint is forming in my mind. It's not a time for questions about implementation or feasibility, but for consideration of what should be done simply because it's the right thing to do. There's no cacophony pressing from without, clamoring that it can't be done or that it's not worth the time, effort, or attention.Joshua Gohttp://www.blogger.com/profile/00918550190150508968noreply@blogger.comtag:blogger.com,1999:blog-6909634439053522826.post-14396060471267235412009-11-27T12:10:00.000-08:002009-11-28T10:52:06.960-08:00Organic thankfulnessI've always had trouble keeping my sentimental cycles synchronized with what the calendar officially sanctions. This year is no different. I got into a very thankful mood early — well before Thanksgiving Day rolled around. Days before Thanksgiving proper, I was working on writing the bulk of my post-wedding "Thank You" cards.<br /><br />True to my high ideals and impractically grand ambitions, I didn't want to just write a generic, canned response to everyone; I wanted each card to be sincere and personalized for each recipient. For I follow the golden rule: "Do unto others as you would have them do unto you." If I am deeply insulted and take personal offense whenever someone sends me a generic message disguised as a customized one, why would I inflict the same on others?<br /><br />Faced with the task of writing so many customized notes to so many people, it was hard to even get started — but good old engineering experience soon kicked in. Whenever I find it hard to get started on something, I try to build momentum by doing the easy part first. Then, to maintain that momentum and to keep my focus tight, I periodically stop and ask myself what the main objective is, and re-align accordingly.<br /><br />So, I started with what was easy. I simply read the card that each person sent, so I could see what was written. That would give me something to riff off of. And sure enough, this worked in giving me something to start off with. But the unexpected (and much more helpful) side effect of reading these cards was that they just naturally made me feel thankful. Writing the response just seemed like the natural next step — in contrast to painfully extracting whatever random remnants of goodness I had left in me, or emotionally manufacturing shoddy imitations of good cheer for putting on paper.<br /><br />The trouble with letting nature run her course and supply material for us is this: what she supplies seldom fits our purposes exactly.<br /><br />And so, I had to rein in my overflowing thankfulness to keep it focused on thanking people specifically, rather than letting my responses meander all over the place.<br /><br />I remember one specific instance of an older relative, widowed for a couple of years now, whose long and happy marriage had instilled in me a deep and lasting belief that marriage in this day and age could work. She and her late husband had so many excellent qualities that I could easily have filled her card by heaping random praise on them. But by focusing on thanking her first, I actually had the chance to keep the note shorter and more meaningful by zeroing in on how they inspired me through their marriage specifically, rather than listing their virtues as individuals.<br /><br />I'm almost done writing these cards. Each card gives me a chance to take a natural sense of gratitude and then channel it in a way that renders it more meaningful by doing a better job of making it known.Joshua Gohttp://www.blogger.com/profile/00918550190150508968noreply@blogger.comtag:blogger.com,1999:blog-6909634439053522826.post-12545897807335171842009-07-05T23:45:00.000-07:002010-01-21T18:29:01.978-08:00Automated summaries and excerptsYou've seen them around the web: they're the blocks of text appearing under the headlines, giving you a little more information on what the linked article is about. If the headline didn't tell you enough, the summary or excerpt is supposed to serve as a sort of fall-back mechanism to tell you a little bit more.<br /><br />The websites of such longtime print titans such as <a href="http://nytimes.com">The New York Times</a>, <a href="http://economist.com/">The Economist</a>, and <a href="http://wsj.com">The Wall Street Journal</a> tend to have good summaries beneath their headlines. They know how summaries should be written — by hand. They are, after all, the professional producers of such content and have a vested interest in getting that content viewed.<br /><br />Those who are a few steps removed from the production of such content, on the other hand, make it readily apparent that they just don't care. Two of the most surprising offenders are Google and Apple — ironically, two media darlings who appear regularly in feature articles.<br /><br />A quick jaunt over to <a href="http://finance.google.com">Google Finance</a> gives us an example of their automated summary text. If news articles could get circumcised, then Google Finance summaries would be the equivalent of the unwanted foreskins.<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiMGX5rxFS-uTaVVSHDWc2H_s-huHZCFWiwvCVqBezXCX7YY-7_noIbMZ7iWdOdN4nF7R702hGU_2FosWx1IYb3k0P6Xr5In0INsRK7QC_ol24Xd12ed6rsv0mVHkhZwVde_oup3xPlJnR7/s1600-h/automated_summary_google.png"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 111px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiMGX5rxFS-uTaVVSHDWc2H_s-huHZCFWiwvCVqBezXCX7YY-7_noIbMZ7iWdOdN4nF7R702hGU_2FosWx1IYb3k0P6Xr5In0INsRK7QC_ol24Xd12ed6rsv0mVHkhZwVde_oup3xPlJnR7/s400/automated_summary_google.png" border="0" alt="By Brian Love PARIS, July 5 (Reuters) - World leaders are bound to express the hope that the worst of the global economic crisis is passing when they meet this week, but they are under pressure, too, to manage a Chinese challenge to<br />decades of dollar ..." id="BLOGGER_PHOTO_ID_5355221299523276386" /></a><br /><br />Where do we even begin when trying to enumerate what's wrong with this? First of all, the summary text is much too long to reasonably hold the attention of the typical reader who is scanning the page; it's unreasonable to even call it a summary.<br /><br />And even if you read the entire thing, it doesn't make sense because it trails off. Decades of dollar what?<br /><br />And then there's the extraneous information: the name of the author, the location where the article was wired from, and the name of the wire service. I'm sure the author is a swell guy, that Paris is a lovely city, and that Reuters is excellent at what it does, but what is all that doing in the summary text?<br /><br />Oh, but Google is all about automating stuff, even if it turns out a little ugly. Let's look at what Apple does; we <span style="font-style:italic;">know</span> they've got a better sense of design than anyone around.<br /><br />Sorry to be the one to tell you this, but the headlines on the <a href="http://www.apple.com/startpage/">Apple start page</a> don't fare much better. Here's just one entry from the entire embarrassing list.<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_GqChQxNcn_U8snbclgph08N4i29bwI_mD8AH9Z9EVirQHVOa7_RbANaE2aUdTvLgWoEUo-80BNmxYbJKZIdRT-Xb61I-LnEoCv3FvoYujNH2Apk3g4gDKFwZmFdoXAwMNeRTs_mAhzHg/s1600-h/automated_summary_apple.png"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 72px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_GqChQxNcn_U8snbclgph08N4i29bwI_mD8AH9Z9EVirQHVOa7_RbANaE2aUdTvLgWoEUo-80BNmxYbJKZIdRT-Xb61I-LnEoCv3FvoYujNH2Apk3g4gDKFwZmFdoXAwMNeRTs_mAhzHg/s400/automated_summary_apple.png" border="0" alt="If you'd like to try something both delicious and healthy this holiday weekend, this about serving your guests Barbecue Glazed Alaska Salmon with..." id="BLOGGER_PHOTO_ID_5355221607234746402" /></a><br /><br />Well, if anything could be called an improvement, I suppose we can give this one a little bit of credit. The summary is at least short enough to read and doesn't contain extra cruft. But it still doesn't make sense when considered as a self-contained sentence. Barbecue Glazed Alaska Salmon with <span style="font-style:italic;">what</span>? Why should I click “Learn more” when you can't even bother taking the time to edit the summary to give me a coherent sentence up front? Is this entire article made up of sentences that are going to trail off and leave me hanging just like the summary did?<br /><br />Why, in the middle of 2009, do we still see these amateur-looking automated excerpts on websites run by widely respected technology companies?<br /><br />I'm not the one making decisions inside the conference rooms in these companies, so I can't say for sure. But I can venture a guess. These two companies are highly respected, to a large extent, because they're also highly profitable. Assuming that people don't really care whether the summaries are readable or not, it certainly makes sense to cut costs and forgo hand-editing of summaries. And being technology companies, of course they'd want an automated, technological solution; when all you have is a hammer, everything looks like a nail.<br /><br />So these world-class companies knowingly produce these barely passable, poor excuses for excerpts. What does that mean? What's the implication? From this, we can conclude that these excerpts aren't meant to be read. If they aren't meant to be read, then they aren't meant to be taken seriously. They're merely filler text, a cheap imitation of what big-boy news organizations have done for years. Maybe in the age of <a href="http://en.wikipedia.org/wiki/Continuous_partial_attention">continuous partial attention</a>, this makes sense for a new kind of audience.<br /><br />As for me, I'm going to remain stubborn and demand only the best from my news sources — including summaries and excerpts. Is it really too much to ask for a complete sentence these days?Joshua Gohttp://www.blogger.com/profile/00918550190150508968noreply@blogger.comtag:blogger.com,1999:blog-6909634439053522826.post-25860915228577344352009-04-15T00:50:00.000-07:002010-01-21T18:29:12.660-08:00Finely tuned collective effortAs someone who is rabidly individualistic, working with other people isn't something that's hard-wired into me. For this, I've been chided by friends and family members who hold collective effort up as a sacred cow. They cannot possibly fathom why anyone would question the merits of working together.<br /><br />I remember reading (probably in the Harvard Business Review) that this teamwork mindset is prevalent especially among my generation, which grew up playing team sports and doing group projects in school. Contrast this with the modus operandi of previous generations of workers, who were much more individualistic: put your nose to the grindstone, pull your weight in the organization, and let your merits stand on their own.<br /><br />In this sense, I am very much a traditionalist.<br /><br />But I've been through some fiery projects in school and during my consulting days working with clients. Massive requirements and short deadlines have a way of focusing the mind and forcing the casting aside of closely held ideology. I've seen teams coming together to accomplish something that was more than the sum of the parts, and seeing that in action made me more open to the idea.<br /><br />Still, I maintain what I consider a healthy skepticism towards a widespread and blind allegiance to the nebulous concept of collective effort.<br /><br />I acknowledge that it produces tremendous benefits as many sets of eyes and differing perspectives hammer away to solve problems, and that work can be parceled out and done in parallel, resulting in undeniable time savings. Knowledge can be shared to increase the human capital of everyone involved; it makes everyone better off by increasing the raw capability of the team so that the team's maximum output doesn't merely hold constant.<br /><br />But reaping these benefits does not come automatically.<br /><br />Any time you pool resources to exert greater leverage, you also put yourself at risk of exercising power in the wrong direction or of misallocating those resources so that all you're left with is a colossal heap of waste. Capital intensive industries such as auto manufacturing earn billions in profit in good times, but when crisis strikes, the cost of all that unused capacity is crippling.<br /><br />I don't want to come off sounding alarmist or overly pessimistic about teamwork gone wrong. Truth be told, it rarely ends in a blazing mess. Even the most dysfunctional team is just a misconfigured engine that nonetheless manages to sputter along, misfiring occasionally, but still operational. I'd guess that this is how most haphazardly assembled teams manage to get by.<br /><br />If you choose to go it alone, it's like pedaling along on a bike: you can get in and out of places, but even the car with the misfiring engine can go faster than you can. Working alone feels much more elegant and affords the independent worker more agility. This is where the continued appeal of individual effort arises from. But for most undertakings worth their salt these days? A team effort is the smart choice, just like you need a car to really enjoy all that Southern California has to offer.<br /><br />But again, let's be honest. Driving a car with a misfiring engine isn't very much fun. It's nerve-wracking, and the only people amused by it are the people who are laughing at you from a safe distance.<br /><br />What can you do to pull off collective effort like a finely tuned engine?<br /><br /><span style="font-weight:bold;">Share knowledge.</span> This makes you better equipped for the future.<br /><br /><span style="font-weight:bold;">Exploit different strengths and don't strive for homogeneity.</span> This is where differing viewpoints and other sets of eyes can really come in handy.<br /><br /><span style="font-weight:bold;">Implement systems of coordination and common convention to maintain coherence.</span> It's ridiculously easy for everyone to start doing things their own way.<br /><br /><span style="font-weight:bold;">Know the difference between parallel and serial tasks.</span> As tasks in this knowledge-based economy become increasingly complex, it's not as easy as painting a room and asking everyone to take one wall.<br /><br /><span style="font-weight:bold;">Gather the right people for what you want to do.</span> It does you no good to get an excellent tax accountant when what you need is a good plumber. No offense to tax accountants.<br /><br />Question your assumptions: do you really need a team for what you're trying to do? Do you really need a large one, or would a small one suffice? And are you prepared to put in the hard work for everyone involved to get the maximum benefit out of the experience?Joshua Gohttp://www.blogger.com/profile/00918550190150508968noreply@blogger.comtag:blogger.com,1999:blog-6909634439053522826.post-3699659900906635312009-03-28T17:41:00.000-07:002009-03-29T01:37:17.604-07:00Enough with civility: confronting line cutters and queue jumpersLast Saturday, Sophia and I went to Disneyland to celebrate my birthday and to spend some time together, since our work schedules haven't really overlapped favorably in recent weeks.<br /><br />We were in line for the <a href="http://disneyland.disney.go.com/disneyland/en_US/parks/attractions/detail?name=MatterhornBobsledsAttractionPage&bhcp=1">Matterhorn</a>, and as decent, upstanding Disneyland patrons, we took our places at the back of the snaking line.<br /><br />It was a long line, but it was what one would expect for a Saturday at Disneyland. It was moving at a good clip — faster than the <a href="http://en.wikipedia.org/wiki/Interstate_405_(California)">405</a> near <a href="http://www.santamonica.com/">Santa Monica</a> during rush hour, anyway.<br /><br />About halfway through, a suspicious looking Asian guy wearing sunglasses sidled up next to me from out of nowhere. For a good three seconds, he stood there without saying a word. I thought that he was expecting to be recognized, but upon closer inspection I could recall no previous association with him.<br /><br />When he finally said something, he said, "Hey man, you mind if I get behind you? The line is really long and I don't want to wait in the back."<br /><br />Flabbergasted at the audacity of the request, my verbal faculties sputtered, and the first thing to come up was, "No."<br /><br />As I realized that I had actually issued an unintentionally affirmative and welcoming response, I stepped in to clarify. "I mean, yes, I do mind. And I mean that no, it is not fine with me if you get behind me."<br /><br />He responded, "Oh, come on. Why not?"<br /><br />"Because we began at the back of the line, like everyone else, and so should you," I said with an annoyed and furrowed brow. (At this point, it was only one brow. That's how annoyed I was: unibrow annoyed.) I gestured to the very back of the line. "You should start heading over there. You shouldn't be here." <br /><br />He kept a straight face. "Oh, alright." And that was that.<br /><br />Or so I thought. I turned around five minutes later, and he was just standing there — right behind me!<br /><br />I looked at the people right behind him to see if they were upset in any way. It was a wholesome-looking white American family, and they didn't seem bothered by anything at all. It looked like they were having a good time.<br /><br />I am certain that the scumbag probably chose to ask me because white people would be more likely to think we were together since we were Asian — which is a reasonable assumption to make. But the guy was alone, and I felt a little sorry for him; maybe someone close to him died. You never know what the story is with people, I figured, so I decided to forget about it.<br /><br />But five minutes later, the scumbag's wench joined him. At this point, I was fuming, and was very close to causing a scene. Sophia told me to just forget about it since we were there to have a good time, and I knew that anger tends to make me act irrationally, so I just fumed for a while and hoped it would pass.<br /><br />It has been a week, and it has not passed. <br /><br />While writing this up, I found out that <a href="http://tastyresearch.com/2006/09/21/cutting-in-line/">queue jumpers get away with it most of the time</a>. In the future, please do everyone a favor and cause a scene, and I will do the same.<br /><br />Now, I understand that it may be difficult to think quickly of what to do. If you need some ideas, here are a few things you can do to people who cut in line.<br /><br /><span style="font-weight:bold;">Wait until you get to the front of the line and <span style="font-style:italic;">then</span> tell the people in charge.</span> Make the cutters return to the back of the line all over again.<br /><br /><span style="font-weight:bold;">Loudly and clearly proclaim to everyone behind the cutters of what they have done.</span> Populist outrage is a powerful force, and public humiliation is a long neglected tool. Put them together and you've got a great combination.<br /><br /><span style="font-weight:bold;">Take a picture of the cutters.</span> If they cut in pretending to know you, why not play along? Say loudly, "Oh, hey, what does your new driver's license picture look like?" Remember their names, and then post their names, their pictures, and what they did, so that employers can find them when they perform background checks. (Make the page <a href="http://en.wikipedia.org/wiki/Search_engine_optimization">SEO friendly</a> so these dirtbags are easier to find.)<br /><br /><span style="font-weight:bold;">Get physical.</span> The line cutters don't belong there, so you're just righting a wrong and putting things back as they should be. A simple shove should do the trick, but be prepared for some pugilism should you go this route. This is particularly well-suited for those of you who don't resort to violence — because it's your first choice, not a last resort.<br /><br />It's about time these scoundrels get what they deserve. If it helps, just bottle up whatever road rage you have, and instead dish it out to someone who actually deserves it. Disneyland may be the happiest place on earth, but happiness will remain incomplete as long as we remain complacent about people who blithely dismiss the ideals of justice and fairness.Joshua Gohttp://www.blogger.com/profile/00918550190150508968noreply@blogger.comtag:blogger.com,1999:blog-6909634439053522826.post-89275776819002108652009-03-27T12:39:00.000-07:002009-03-27T17:57:06.182-07:00Vision and chaosAmong the key elements of my father's network of enterprises are the fixer-upper houses which he rents out. As soon as my brother and I were old enough to be of use on these construction sites, Dad would take us along.<br /><br />I hated the messiness of the building process. The floor would typically be littered with drywall chunks. Shattered roof tiles sat in piles on the front yard, and sawdust was sprinkled over everything.<br /><br />When it was all done, though, with everything cleaned up, I felt accomplished for having been a part of bringing about the final outcome. It was more than easy to forget the messy process that brought about the end result: forgetting was automatic. It actually took me conscious effort to remember what it took to get there.<br /><br />While I was in the thick of it, it was discouraging to see the mess in front of me, because it just didn't seem possible that everything could be made right again. All I saw was a seemingly intractable mess. My father, on the other hand, never seemed fazed by it. His vision of the end result was not clouded by temporary worry because he was certain of what we were working toward. He saw things not as they were, but as they should be.<br /><br />Over the years, I learned to embrace the temporary mess, provided there was a plan and a vision for building something beautiful from it. Still, this sort of unflinching confidence doesn't just come at the flip of a switch. It took many messes and subsequent turnarounds to deeply ingrain this kind of optimism in myself. Even now, I need a conscious and intentional self-reminder not to be overwhelmed when confronted with a seemingly insurmountable task.<br /><br />The boy knows he is a man when he can see not only what is in front of him, but what he's going to make of it.Joshua Gohttp://www.blogger.com/profile/00918550190150508968noreply@blogger.comtag:blogger.com,1999:blog-6909634439053522826.post-60079363724797755552009-03-10T01:22:00.000-07:002009-03-10T10:15:25.578-07:00My automobile's cooling system and its plastic partsI've learned a lot about <a href="http://www.autohausaz.com/mercedes-auto-parts/mercedes-cooling-systems.html">my car's cooling system</a> over the past couple of weeks. There's nothing like the prospect of a melted engine to focus the mind. Typically, I would be content to leave it to the mechanic, but the cooling system has many moving parts, and I'm the one who sees firsthand all the symptoms when driving it in various situations.<br /><br />At the very least, anyone in my position would have to take careful note of which circumstances triggered certain events. Such diagnostic tips can help the mechanic narrow things down so that he won't charge you as much for diagnosing the problem. Ideally, we'd also prefer that he fix everything that's wrong with a component as vital as the cooling system.<br /><br />I've had to watch the reading on the temperature sensor, for one. The key is to never let the needle hit the red zone at the top of the temperature gauge. If it does, your engine's <a href="http://en.wikipedia.org/wiki/Head_gasket">head gasket</a> and other crucial parts are in critical danger of melting, distorting, or breaking. The repairs for those problems are much more expensive than those to the cooling system.<br /><br />When I went to the mechanic this morning, I took in various observations that would help him narrow down the problem and know where to look. I noticed that the fans were going full speed because of the higher running temperature, so I told him that the fans were extremely loud after a short drive. From various sources online, I made sure to observe any difference between city driving and high speed freeway driving, but there was none, so this meant there was one less option to consider.<br /><br />With the cooling system in my car, things have been failing left and right in a sort of chain reaction as the increased running temperature of the car's engine puts a lot of parts under extra stress. Whatever parts failed and needed replacing were just worn out and should have been replaced long ago. Rubber rings had become as hard as plastic. One plastic pipe had become so brittle from age that it broke off; I had to re-fasten the <a href="http://en.wikipedia.org/wiki/Hose_clamp">hose clamp</a> just to keep the engine running cool enough to drive to the mechanic. Metal parts such as the <a href="http://www.expertvillage.com/video-series/5216_thermostat-housing.htm">thermostat housing</a> and the <a href="http://www.ehow.com/how_7679_tell-cars-water.html">water pump</a> showed signs of corrosion; in the case of the thermostat, it wouldn't open to let coolant flow as it should.<br /><br />The mechanic told me some interesting tidbits while we were ruminating aloud on the absurdity of car makers — including Daimler and BMW — using so many plastic parts all over the cooling system. According to him, the move towards plastic parts is justified by lower cost of materials and making the car lighter so the engine doesn't have to pull as much weight. One thing he observed was the increasing failure rate of newer cars — and he said it wasn't unusual for people with new cars still under warranty to come to his shop with worn out plastic parts.<br /><br />Suffice it to say, that made me very hesitant about paying a premium for a newer model Mercedes-Benz or a BMW. If I end up buying a new car soon, it may well be a Hyundai, a Honda, or a Toyota. If everyone's using plastic parts, I may as well pay less.<br /><br />In any case, I'm surprised that my old car has lasted this long, considering the long distances I drive on a regular basis. It's a 1996 Mercedes-Benz C220. I've been very fortunate to have the car running within its prescribed temperature limits, despite all the hand-wringing and pulling over to the side of the road, fraught with worry.Joshua Gohttp://www.blogger.com/profile/00918550190150508968noreply@blogger.comtag:blogger.com,1999:blog-6909634439053522826.post-37949883000530720282009-02-26T23:58:00.001-08:002009-02-27T00:05:52.748-08:00Feisty Ford FocusPeople who drive small cars sure have colorful personalities. Yesterday, on the way to work, I saw a <a href="http://www.fordvehicles.com/cars/focussedan/">Ford Focus</a> with a license plate frame that said, "Don't laugh. It's paid for." It was refreshing to see someone being flashy about their financial responsibility — a quality which is rarely flaunted.Joshua Gohttp://www.blogger.com/profile/00918550190150508968noreply@blogger.comtag:blogger.com,1999:blog-6909634439053522826.post-76234387327640582752009-02-25T00:15:00.001-08:002010-01-21T18:30:29.703-08:00Lessons from refactoringI enjoy the process of refactoring software — especially software that was written by other people. It forces me to understand things inside and out (which is a tough point to arrive at when all you do on that code is the occasional drive-by bug fix). On top of that, since the desired functionality is known already, it's all redesigning of the data model and writing code. There are fewer product-level questions that come up. The specification is pretty much set.<br /><br />For this particular project, though, I have the unique challenge of making the new design pass muster with multiple parties. People are picky and they always have something to say, so I took special care this time to anticipate any objections and address them so they'd get a better sense of my thought process. I've still had to make changes — and I make them gladly — but I've found that having to explain each decision I'm making generally serves to clarify my thought process.<br /><br />It certainly helps to have some time to think hard about these changes, too. It's a healthy thing to let a data model sit for a bit and soak into the minds of all stakeholders while their input is incorporated. It also helps when people are interested enough to give detailed and insightful feedback. Sure, it generally doesn't hurt to have more sets of eyes looking at a plan, but having a team of people analyzing it harnesses collective historical memory — great for backward compatibility — and makes the plan more bulletproof by ensuring that it addresses concerns from many points of view.Joshua Gohttp://www.blogger.com/profile/00918550190150508968noreply@blogger.comtag:blogger.com,1999:blog-6909634439053522826.post-87318003218124352402009-02-14T03:06:00.001-08:002010-01-21T18:35:07.704-08:00Blogger client as a Java Swing desktop applicationEver since I started using Blogger, I've been looking for a decent, no-nonsense desktop client to write my posts. I personally find it to be a lot of hassle to load up a big, heavy web browser and log in to Blogger to make my posts. (To be fair, <a href="http://dropline.net/past-projects/drivel-blog-editor/">Drivel</a> was quite excellent in the past, but doesn't look like it's maintained anymore.)<br /><br />After a little poking around at Google's API documentation, I am pleased to announce that I'm writing this post from a desktop application of my own making. It's a Java desktop application using Swing, so it will run on Windows, Mac OS X, Linux, and any other major desktop platform with a Java runtime.<br /><br />So far, I can select from a list of blogs, write in post titles (to be friendly to those search engines), write post content, and submit new posts. It's really basic, but it's quite a milestone and a major motivator for me to at least have gotten this far.<br /><br />Soon to come on the feature front, I'm aiming for loading old posts to update, deleting posts, and smoothing out the user experience. (Right now, the app looks and feels like the weekend side project that it is. It doesn't even scroll when the text is too long.)<br /><br />On the code quality front before I release this thing out into the wild, I'd like to do some release engineering, automate the build process, and just separate the various concerns (like GUI drawing code, network connections, and application logic.)<br /><br />In any case, it feels good to have my software development methodology validated: get a dirty prototype up and running, and count on the morale boost to spur further development. So far, it feels pretty good. The hardest part is done and the challenge from now on is just finding spare moments to work on refining this thing.<br /><br /><span style="font-weight: bold;">Update 02/20/2009:</span> I've implemented updating existing posts, and I'm updating from the improved client right now. Of course, to get to that point, it had to load up old posts. A cleanup of the code base before proceeding is probably in order, because I'm kind of going nuts trying to figure out where everything is and where new things should go.<br /><br /><span style="font-weight: bold;">Update 02/22/2009:</span> I've created the build system to use <a href="http://ant.apache.org/">Ant</a> and package the app into a neat little .jar file. I also fixed a minor usability bug to make the app more pleasant to post with and give you feedback when a post has been submitted. The refactoring into a better architecture will have to wait; my mind's capacity for refactoring is mostly taken up by a big refactoring proposal I'm putting together at work.<br /><br /><span style="font-weight: bold;">Update 12/16/2009:</span> It's open to the public! Please see the <a href="http://joshua-go.blogspot.com/2009/12/introducing-lumberjack-desktop-client.html">Lumberjack release announcement</a>. The code has been cleaned up, made more maintainable, and a few features have even been added to make the application more pleasant to use.Joshua Gohttp://www.blogger.com/profile/00918550190150508968noreply@blogger.comtag:blogger.com,1999:blog-6909634439053522826.post-80632793632853452392008-12-23T01:17:00.001-08:002010-01-21T18:29:45.173-08:00The trouble with budget surplusesThe trouble with a government (or any big organization) running a budget surplus and sitting on a big pile of cash is that people start asking questions like, "Why don't we do something with that money, especially since there's so much to fix?" At that scale, having some cash ready for a rainy day is not an excuse that people are able to handle, especially when the quoted figure is in the billions of dollars. Surpluses may seem like a lot of money, although when divided on a per-person basis, they seldom amount to much at all.<br /><br />In this deficit-infested time, it's easy to rail against profligacy in our budgets. I'm not trying to engage in what-ifs here. I'm suggesting a way we can avoid this same problem in the future.<br /><br />We've been told that it's wise to save up for the future, to have some cash on hand just in case — folk wisdom that was reinforced by the big collapses this year. We also know that it's hard for most people to hear about a surplus in the billions of dollars without wanting to spend it on something.<br /><br />If an even moderately sized government were wise and careful in its spending, it would quickly accumulate a large surplus. That large surplus would then get people dreaming about capital improvements, social services, and other goodies that drain the public purse.<br /><br />Those who advocate for smaller governments would say that the problem could be solved by striking the problem at the root — shrinking the government and therefore its potential to accumulate large sums of money. But pools of large capital certainly have their advantages, and whether by conscious choice or mere political expediency a large government must stay large, it could do better to quote the figures based on the population of the governed; that is, on a per-person basis. This might make billion-dollar surpluses less distasteful to the vast majority of us, whose visceral reaction to large dollar amounts placed before us is to spend imprudently.Joshua Gohttp://www.blogger.com/profile/00918550190150508968noreply@blogger.comtag:blogger.com,1999:blog-6909634439053522826.post-7879073340253575332008-12-11T23:59:00.000-08:002008-12-12T01:22:51.612-08:00Small business owners: don't be a jerkMy girlfriend Sophia is an assistant manager at <a href="http://www.abercrombie.com/">Abercrombie and Fitch</a>, and is thus bombarded with more than her fair share of rude customers. One story she told me this week was about a woman who went ballistic after asking to try on one of the mannequins' jackets and being told no.<br /><br />Now, people are just crazy, and Sophia has told me many stories like this before. What was so different this time?<br /><br />Well, the crazy lady played the business owner card: if it were <span style="font-style:italic;">her</span> store, she would have <span style="font-style:italic;">gladly</span> taken the jackets off the display mannequins. This know-it-all "business owner" then proceeded to hound Sophia for her full name and pressed her for her employee ID so she could file a formal complaint, and refused to go through the normal channels.<br /><br />It is precisely this self-serving arrogance and provincial, narrow-minded ignorance that keeps small business owners from being taken seriously. As a former small business owner myself, I know that the burden is heavy: you've got to worry about employees, customers and growing your business. On top of that, you have the responsibility to make sure any legal paperwork is in order and that taxes are taken care of. But just because you're able to handle this does not mean that you know all there is to know, and that your way of doing things is the only way.<br /><br />In response to this growing sense of self-importance, here are three things to keep in mind. (I use these reminders to keep myself in check, too.)<br /><br />a) <span style="font-weight:bold;">Rules and processes have a place, even if you choose to forgo them.</span> As a small business owner, you can get by with fewer rules and processes in place. In fact, in most cases you do much better when you're flexible. But larger businesses have a much harder time being flexible; it's not impossible, just much harder. They have to manage everything more strictly in order to hold together the larger whole. A little sloppiness in your store can be passed off as "charming." In a national chain where customers expect extreme tidiness and consistency, that sloppiness is not charm. It is chaotic, and it is poor business.<br /><br />b) <span style="font-weight:bold;">Not everyone enjoys the same latitude to call the shots as you do.</span> You may be your own boss, but most people have someone else to answer to. I've found that being a business owner, seeing the bigger picture, and having the power to remedy things has turbocharged my ability to take the initiative, even after going back to working for someone else. Still, despite having passion for my line of work and understanding its larger implications, I have much less scope to make important decisions. In large companies, even CEOs don't wield absolute power, because they have a board of directors and shareholders to please.<br /><br />c) <span style="font-weight:bold;">You are not special, so don't expect special treatment.</span> A couple of years ago, I received a parking ticket by mistake. I knew that I had moved my car in time, and so I decided that I would write in to contest it. One of my co-owners suggested that I take a tough stance and mention that I was a business owner — as if that had anything to do with my guilt or innocence. I mentioned it anyway, thinking that a little reminder about my contribution to the community wouldn't hurt. Still, I didn't want to rely on that mostly irrelevant fact, so I put much more effort into stating the facts of the case. I drew a diagram of where I had parked, when I had moved my car, when I had been ticketed, and why it was a mistake. In the end I got the ticket waived, but I have a good hunch it had more to do with stating the facts than mentioning that I was a "business owner."Joshua Gohttp://www.blogger.com/profile/00918550190150508968noreply@blogger.comtag:blogger.com,1999:blog-6909634439053522826.post-13018114368137583522008-12-06T19:39:00.000-08:002008-12-06T20:32:34.556-08:00Conceptual tools for allocating finite resourcesI have lived and worked in Greater Los Angeles for over a year now. Since the area is one of the world's major population centers, it only makes sense that I'd come across plenty of opportunities to observe the interplay between two forces: lots of people and limited resources.<br /><br />It's a common problem, but always a multifaceted one. Coming up with a solution means that planners (who could be church volunteers, business managers, or software engineers) must consider various factors. Having to stop and think about this can slow down decision making, and even when there's time to ponder and plan, thinking of a solution from scratch is more error-prone than referring to a proven set of guidelines and rules.<br /><br /><b>Pricing</b><br />Consider the situation when lots of people are elbowing each other to buy concert tickets. There's no increasing the supply of these tickets since a concert hall or stadium can only hold a set amount of people without the <a href="http://www.lafd.org/">Los Angeles Fire Department</a> throwing a hissy fit.<br /><br />Many eager economics students and libertarians automatically want to apply the law of supply and demand here: why not just raise the prices? In fact, that's what some organizations have done: Britney Spears concert tickets for <a href="http://www.staplescenter.com/">Staples Center</a> start at $150.00. (How do I know this? My girlfriend told me. Really.)<br /><br />As the purveyor of tickets, you can do that when you have some idea of how much demand there will be. But what if demand shoots up while your supply stays the same? When it looks like you're about to sell all your inventory and are hours away from turning away your customers and disappointing them, you <span style="font-style:italic;">could</span> double your prices. But then, your customers would be angry that you're gouging them. Your company would appear disreputable for "arbitrarily" changing prices on consumers.<br /><br />If you're looking at it from an economist's point of view, there's no problem there: you're just changing the price to meet demand. But from a public relations vantage point, you run a very real risk of alienating your customers. It'll look like you're exploiting them, when all you're doing is ensuring availability (while making a few extra bucks).<br /><br />On top of that potential PR nightmare, there's a downside that even economists must acknowledge. It's the phenomenon that economists call "sacrificing equity for the sake of efficiency." That's all fine and high-sounding, but what does that mean? It means, "It's not fair." And the customer will feel this way. Your costs haven't changed: it still costs you the same to make it, doesn't it? This gives your customers a reason not to trust you, and therefore not to come back.<br /><br />Changing your prices is certainly an effective tool to curb or increase demand, but raising them is an extremely delicate matter in the age of consumers who expect posted prices to stay the same.<br /><br /><b>Waiting list: first come, first served</b><br />A common alternative to raising prices is to let people buy stuff on a first come, first served basis. The folks at Ticketmaster do this, in case the $150.00 concert tickets sell out (which they often do).<br /><br />As you may recall from your economics class, this is considered more equitable, but not as efficient: the people selling tickets could make a lot more money just by raising the prices. (If you do <span style="font-style:italic;">not</span> recall from your economics class, shame on you for not paying attention. But hey, now you know. Trust me on this one.)<br /><br />As far as fairness is concerned, lower prices with waiting lists are an improvement over simply raising the price. But if you're not one of the first served, you probably won't feel like it was very fair. Maybe you had to be at work that time of day, or don't have time to wait in line.<br /><br /><b>Lottery</b><br />A different spin on fairness is to let people enter a lottery to let them buy what they want at lower prices. If you were to enter such a lottery and your number got drawn, you could then buy what you're after. Typically, submissions to this lottery are accepted during a specified time window. So if you have to be at work when the lottery first opens, that's not a problem. You have just as good of a chance as the first person who entered that lottery.<br /><br />This was part of Saddleback Church's approach when they hosted the <a href="http://edition.cnn.com/2008/POLITICS/08/16/warren.forum/index.html">Saddleback Civil Forum with Barack Obama and John McCain</a> during the presidential campaign. Since Saddleback Church is very high-profile, it was important that a purely pricing-based model was avoided. Taking an exclusively pricing-based allocation of tickets to see the candidates would have looked too much like <a href="http://www.biblegateway.com/passage/?search=James%202:1-12;&version=31;">favoritism towards the rich</a>, a Biblical no-no.<br /><br />Then again, Saddleback Church also had to cover its costs, so it actually opted for a brilliant hybrid approach that consisted of various tiers, each with a different price. It was a multi-tiered lottery. Higher-priced tiers would likely yield a smaller pool and a higher chance of being selected. You can't please everybody, but such a creative and enlightened approach likely pleased quite a few free market die-hards while at the same time placating those who were concerned about equality. If only governments and businesses were as wise.Joshua Gohttp://www.blogger.com/profile/00918550190150508968noreply@blogger.comtag:blogger.com,1999:blog-6909634439053522826.post-14257474117530736522008-10-25T15:59:00.000-07:002008-10-25T16:54:27.314-07:00Look where you're baby steppingWhen you're starting out from scratch, it's hard to predict what you're going to need. I just moved into a new apartment, and my kitchen cupboards are empty because I've decided that I'm not going to even try to predict what I might need in the future. The way I intend to re-acquire the supplies I'll need is by doing stuff.<br /><br />I mean, sure, there's cooking oil, salt, and pepper. But for a little more exotic Chinese cooking, I'll need some sesame oil or star anise. Beyond the basics that I'm <span style="font-style:italic;">certain</span> I'm going to actually use, it's impossible to predict what other odds and ends I will personally end up using. And soliciting advice from others in the past hasn't been good for me: what works for other people often does not work for me.<br /><br />For me, the only way I have been able to know — with certainty — what I'll need is to actually start trying to do things. If I'm trying to figure out which ingredients I'll need to cook a meal, I just start trying to cook. If anything is missing, its absence becomes readily apparent.<br /><br />These are kind of like kitchen <a href="http://en.wikipedia.org/wiki/Use_case">use cases</a>, if we're going to resort to software development parlance. It's like I'm going agile, or adopting Test Driven Development with my cooking processes. I could theoretically start out with nothing at all — to illustrate the method theoretically, I actually <span style="font-style:italic;">should</span> start from nothing. I would soon find that I need a cutting board, a knife, and whatever ingredients my recipe calls for. For a basic meal I would also find that cooking oil, salt, and pepper come in handy.<br /><br />After cooking a basic meal, I know with confidence that I have everything I need to cook — wait for it — a basic meal. That common use case is taken care of and I can cook basic meals in the future with confidence. Now, what if I were to go ethnic? I'd soon run into some cases where I'd need to stop and get some more ingredients.<br /><br />The idea is that, after a while, I would eventually be able to confidently prepare a broad range of meals. This confidence is rooted in the knowledge that I have verified that I have everything needed — <span style="font-style:italic;">by actually having done it in the past</span>.<br /><br />Of course, it's possible to carry out this kind of method to absurd extremes. If you know for sure you're going to need it, then make a note and take care of it. Be reasonable in what decisions you leave for later and which ones you can make now. In the cooking example, it's great for didactic purposes to start from scratch, but in practice it's dumb not to have the basics like cooking oil or a cutting board.<br /><br />What about when developing software? You can leave scalability concerns for later if you're just starting to write a web application, but no matter what, you are going to need a machine and a relational database. (If you're writing something, like an offload server, <span style="font-style:italic;">because</span> it needs to be scalable, then you damn well better take scalability into account.) To maintain your sanity, it might even help to sketch out a data model. Take baby steps, but know where each step will end. Don't be content with merely knowing that your foot will be in the air and then end up somewhere on the ground at some point. After all, this nebulous "goal" can just as easily be achieved by tripping and falling.Joshua Gohttp://www.blogger.com/profile/00918550190150508968noreply@blogger.comtag:blogger.com,1999:blog-6909634439053522826.post-59474842958390026612008-09-18T23:18:00.000-07:002008-09-19T00:20:25.165-07:00Getting more out of a sentenceThere's a very simple, effective, and systematic way to amp up the amount of insight you get from the things that you read.<br /><br />It's probably best to explain with an example. The following is from Ed Catmull's article for the Harvard Business Review, "How Pixar Fosters Collective Creativity."<br /><br /><blockquote>We must constantly challenge all of our assumptions and search for the flaws that could destroy our culture.</blockquote><br /><br />How much thought can this spark in your mind?<br /><br />If we just read this sentence several times, each time with an emphasis on only one word, we get a new angle on the general idea being conveyed. Something different is emphasized.<br /><br /><blockquote><span style="font-style:italic;">We</span> must constantly challenge all of our assumptions and search for the flaws that could destroy our culture.</blockquote><br /><br />The emphasis is on the <span style="font-style:italic;">we</span>. This tells me that whatever this sentence is talking about involves a team effort. Everyone on the team must be involved. It's a "we" rather than a "me." But does that necessarily have to be the case? It starts with <span style="font-style:italic;">somebody</span>, right? Why not with individuals?<br /><br />It's okay — a good sign, even — to ask questions about the truth or applicability of a passage. If you're a reader with a healthy sense of skepticism, those kinds of questions immediately arise. A lot of the same questions will come up: is it necessarily so? Is it true all the time? The more you ask these kinds of questions, the more you hash things out and figure them out for yourself.<br /><br />Let's look at another word.<br /><br /><blockquote>We must constantly challenge all of our assumptions and <span style="font-style:italic;">search</span> for the flaws that could destroy our culture.</blockquote><br /><br />This time, the emphasis is on the word "search." This tells me that the flaws are often hidden; otherwise, they wouldn't require searching. Now, I'm an imaginative guy, so then I imagine myself searching frantically and then getting tired or discouraged. When I'm tired or discouraged, I ask questions. Why am I searching, anyway? What exactly am I searching for, again? How do I really know when I've found it?<br /><br />That's only two words, and I could go on for a while, but I think the general method has been made clear. The biggest plus going for this method is that it is systematic; I can just emphasize each word until I'm plumb out of new thoughts. When I'm done with all the words in the sentence, I know I'm done.<br /><br />Personally, I've found that this process also helps me internalize ideas. Even if I don't remember the exact wording of a sentence, the dance of thoughts anchored around a single idea tends to leave a lasting impression. I imagine this is because the thoughts that come to mind immediately tend to be those of the most concern to me. When it comes to things that truly concern me, I have an easy time remembering.Joshua Gohttp://www.blogger.com/profile/00918550190150508968noreply@blogger.comtag:blogger.com,1999:blog-6909634439053522826.post-54746316326241951962008-09-17T22:27:00.000-07:002010-01-21T18:29:55.314-08:00Ruby on Rails: optimizing a slow-rendering pageRecently at work, I was faced with the problem of fixing a slow-rendering page. The page is part of an internal content management system, where the content includes video, images, and text. We have over a thousand pieces of content being managed with this tool and the slow page was a listing of all the content along with key overview-type information for each content package. <br /><br />Everyone was fine with this page loading slowly, since we knew that there was a lot of content. The problem arose when the page would only show around 50 items and then stop loading; the server configuration was set to automatically stop requests that took too long to process.<br /><br />I knew that the best course of action would be to start measuring. I took a look at the logs to see how long the request was taking and the approximate tasks where time was being spent. At <a href="http://www.jibjab.com/">JibJab</a>, we take pride in caching the hell out of everything. Only a tiny fraction of time was spent hitting <tt><a href="http://www.danga.com/memcached/">memcached</a></tt>. And it looked as though <tt>memcached</tt> was doing its job: almost no time was spent hitting the database. But a ridiculous percentage of the time was spent "rendering" the page.<br /><br />That meant the onus was on my code to speed things up. Since the slowdown had become more and more apparent as we added more content, my guess was that the loop was the main source of the problem. Anything that would be only a tiny bit slow on its own would lead to a delay over a thousand times greater.<br /><br />The first thing I removed was a call to <tt>cycle()</tt>, which I was using to zebra-stripe the table display for easier reading. In its place, I put in an index variable that would be modulo-two to determine its index into an array of two choices (even row or odd row). To keep the same feature, I had to dirty up my code if I wanted it to run more quickly. And indeed that shaved off about one-fourth of the rendering time.<br /><br />But the page was still taking over ten seconds to render, so I continued my hunt. A pattern I saw many times within each iteration of the loop was an old friend, <tt>link_to</tt>. I am not terribly attached to this particular friend, however, so I got rid of it and replaced it with some good old <tt><a></tt> tags, which bought me a good second or two.<br /><br />So far, I felt pretty good about what I had been able to do to extract render-time savings. But there was still room to shave the time down, and luckily for me, <tt>Inflector.humanize</tt> was being called in each iteration like there was no tomorrow. I decided that there indeed would be no tomorrow for these expensive-looking helpers, and after removing all five calls inside the loop, I got total render time down to a very respectable two-second neighborhood.<br /><br />The lesson learned? Sometimes code has to be a little messier so it can be fast enough to be used. Specifically, watch out for those beautiful-seeming Rails helper functions; they do a lot of work for you, but sometimes you're just better off doing the work yourself.Joshua Gohttp://www.blogger.com/profile/00918550190150508968noreply@blogger.comtag:blogger.com,1999:blog-6909634439053522826.post-77013171961593175762008-09-16T23:14:00.000-07:002010-01-21T18:30:15.487-08:00Bilateral versus multilateral trade agreementsNews sources everywhere seem to lament the collapse of the Doha round of trade talks because countries would then have to resort to bilateral or regional trade agreements. For any supporter of free trade this would be understandable, but if we acknowledge the short term havoc wreaked by rapid changes to trade flows that do not give workers and business owners time to adjust, a gradual and stepwise approach to the opening up of markets seems to be a more prudent way to go.<br /><br />One of the major problems cited by those who favor a broad trade policy is the inconvenience of having to keep track of different rules for different countries. I would go about solving this by building a hash table of trade rules. Basically it would mean that given a country and a product as input, we would get the policies regarding that product as output. This would be a large table, but we would ask and receive only what we are interested in.<br /><br />There's also the advantage of isolated policies having limited effects, rather than opening up to the entire world in one go. Maybe there is something I'm overlooking, but I don't consider the use of bilateral agreements to be an indicator of failure. I see great potential for reaping the benefits of free trade with less of the shock typically expected by any opening up.<br /><br />The collapse of grand schemes for international trade deals is really a familiar scenario that just happens to be playing out on a macro level: a project's planners have decided that they have bitten off more than they can chew, and decide to reduce the scope of the project. Everyone does this, and while we may feel disappointed at envisioning the results of the grand project and having to instead settle for something less, we eventually resign ourselves to the reality that we are doing all that we can, and it is a whole lot better than nothing at all.Joshua Gohttp://www.blogger.com/profile/00918550190150508968noreply@blogger.com