tag:blogger.com,1999:blog-54736495867678214852024-03-13T22:01:55.692-07:00null or emptyBryant Fonghttp://www.blogger.com/profile/04129122715461127965noreply@blogger.comBlogger23125tag:blogger.com,1999:blog-5473649586767821485.post-39815845366391765692010-06-02T14:59:00.000-07:002010-06-02T14:59:28.727-07:00A taste of Ruby on Rails, part 2<b>NOTE: This is a continuation of <a href="http://www.nullorempty.com/2010/05/taste-of-ruby-and-rails.html">part 1</a>.</b><br />
<br />
Ruby on Rails is a great web development platform, but has a few drawbacks that you should understand before switching from ASP.Net.<br />
<br />
<strong>Catch #1: Library support, or lack there-of</strong><br />
When you're staring at Notepad, and you want to support uploaded images and auto-resize them, it's not easy to figure out where to start. With .NET, you have the standard library to poke around and can usually find what you need with the help of Intellisense. With Ruby, everything starts with a Google search. After deciding amongst five <em>identical-</em>sounding choices, you download an image plugin written 2 years ago by a kind developer, which doesn't install on Windows because said developer only has a Mac. You download the tarball of source files that you can't compile without a special version of Visual C++, and doesn't work against the latest version of Ruby you just downloaded. After it finally installs, you end up burning an afternoon debugging it. Of course, <em>it's free</em>. But only in terms of money -- it costs a lot of time (not spent coding, mind you), and <strong>time is more important than money </strong>(that's why you're even considering using RoR, right?).<br />
<br />
<strong>Catch #2: Convention over Configuration</strong><br />
One of the key tenets of Rails is convention over configuration. What this means is the language has an expectation of a "right" way to do things, and if you do it that way, lots of goodness happens for you automatically. The net result is you will find yourself making compromises just to make sure you stay on the right side of syntactical sugar.<br />
<br />
<strong>Catch #3: Stabilization</strong><br />
At Microsoft, perhaps only a third of the time is spent writing new code. Making the code functional is the easy part. Making it work in all the different user scenarios, under heavy load, with different data sets, and otherwise stabilizing the code, that's what we spend <strong>two-thirds </strong>of the time on. And what are you doing during much of this time? Debugging. <br />
<br />
Anyone who's ever used a debugger knows how critical it is to walkthrough code to comprehend what's going on, especially as your code gets more complicated. And, for anyone used to Visual Studio, the debugging experience in Ruby leaves a lot to be desired. So the thing I spent one-third of my time on is now super fast, but the thing that I spent two-thirds of my time on is now frustratingly slow.<br />
<br />
<strong>Catch #4: Performance </strong><br />
I can't stress how important speed is when it comes to the user experience. <strong>Milliseconds matter. </strong>Like other non-compiled languages, Ruby will execute slower than ones which compile to native bytecode. Moreover, the tools to measure and profile those milliseconds are still in their infancy. If you've never looked at a profile of your code, you are probably wrong about where your code spends its cycles.<br />
<br />
<strong>Conclusion</strong><br />
RoR is a great tool to have in your inventory, and like every language it has it's deficiencies. And, like any deficiencies, they can be mitigated -- for example, writing better unit tests to reduce time debugging, and using AJAX to improve user perceived response time. <strong>As long as you understand the trade-offs, you can decide what's best for your project. </strong>For my one-man "fun" web development projects, RoR is a winner.Bryant Fonghttp://www.blogger.com/profile/04129122715461127965noreply@blogger.com1tag:blogger.com,1999:blog-5473649586767821485.post-50184929676032300922010-05-21T10:16:00.000-07:002010-05-21T10:17:20.300-07:00A taste of Ruby and RailsAlthough I've done C, Perl, PHP, and Java-based web programming in past lives, I've spent the last 8 years or so developing on ASP.Net for a living. As a result, I believed Ruby on Rails (RoR) was a toy not meant to be used for anything serious. Because it's easy to learn, people use it to build cheesy websites. But, that doesn't preclude you from building serious websites, Twitter being the standard-bearer for Rails. Here's what I can tell you: <strong>a programming language is merely a tool</strong>. And if data-driven web programming is your <strong>nail</strong>, then RoR is quite the <strong>hammer</strong>. <br />
<br />
Here's the key difference: C developers think of a pointer as a basic building block. C# developers think of hashtables and lists as basic building blocks. <strong>RoR developers think of database tables as basic building blocks. </strong>Working on SharePoint, anytime we needed to add a new database table, it was a big deal. You had to write a bunch of CRUD operations and stored procedures. You had to write a bunch of UI to expose the CRUD operations. You had to write an object model. You had to write upgraders. You had to make sure it got backed up properly. All of this took, on average, a month for a developer to code and unit test. With Rails, all of this is <strong>inherent to the architecture</strong>. You design the database schema, and all of this functionality is immediately available, freeing you from the drudgery and allowing you the time to actually design and build something useful.<br />
<br />
You might say, well I can do all of these things with LINQ, the Entity Framework, or some third-party bolt-on solution. <strong>And you'd be right.</strong> But that's a bit like flying economy on a long-haul flight to Italy with a layover in London. Oh, you'll get there, but only after much discomfort, a strained neck, and having paid extra for your checked bags. Why bother when you can take a comfortable non-stop flight in first class -- and did I mention it was free?<br />
<br />
Of course, it is free only in the <strong>monetary</strong> sense. One of the main critiques of RoR is you pay a price in performance (partially because it is not a compiled language). But if it's good enough for Twitter's billion tweets a month, I think I'll manage. In a followup post, I will go over some of the other problems I've encountered which are not often discussed in online forums.Bryant Fonghttp://www.blogger.com/profile/04129122715461127965noreply@blogger.com0tag:blogger.com,1999:blog-5473649586767821485.post-327032918094815342010-04-22T15:38:00.000-07:002010-04-22T16:20:29.568-07:00Office has shipped!Big congratulations out to those working on <strong>Office 2010</strong> and <strong>SharePoint 2010</strong>, which is finally <a href="http://blogs.msdn.com/sharepoint/archive/2010/04/16/sharepoint-2010-reaches-rtm.aspx"><strong>DONE</strong></a>! This really was a monumental release for us. About halfway through the release, the manager for each product in Office was asked to give a short demo of something cool their team was building. I remember having to demo our product, watching the other demos, and thinking, "Damn, that's amazing stuff, I hope people thing my demo is <em><strong>half </strong></em>as cool." And later, when my team switched to owning a platform component everyone depended on, I remember thinking, "Damn, we better get this thing working soon or those demos will never actually ship." Well, everyone pulled together, and now <b>you</b> can try for yourself all those amazing features we've built for you.<br />
<br />
I found this statistic interesting: Microsoft announced their earnings today, and for the last quarter, the Business Division (which includes Office) represents about <strong>$2.6 billion </strong>in <strong>net profit </strong>for the company. To put that in perspective, that's <strong>more than all of Google</strong>. In fact, it's about the same as Google and Amazon put together. Just for Office. As an Office developer, it makes you think twice about how important each line of code is.Bryant Fonghttp://www.blogger.com/profile/04129122715461127965noreply@blogger.com0tag:blogger.com,1999:blog-5473649586767821485.post-55479773785598006692010-04-08T10:53:00.000-07:002010-04-08T10:53:18.567-07:00Collection of essays on software engineeringIf you haven't already read Joel Spolsky's books on software (<a href="http://www.amazon.com/gp/product/1590593898?ie=UTF8&tag=hordes-20&linkCode=as2&camp=1789&creative=390957&creativeASIN=1590593898">Joel on Software</a> and <a href="http://www.amazon.com/gp/product/1590595009?ie=UTF8&tag=hordes-20&linkCode=as2&camp=1789&creative=390957&creativeASIN=1590595009">Best Software Writing</a>), I'd highly recommend them. But while those are geared towards working on large projects at big companies, <a href="http://gettingreal.37signals.com/toc.php">"Getting Real" from 37 Signals</a> is a collection of essays about software engineering at a startup (and most of the lessons apply even if you are a team of one). Better yet, it's free, so what have you got to lose?Bryant Fonghttp://www.blogger.com/profile/04129122715461127965noreply@blogger.com0tag:blogger.com,1999:blog-5473649586767821485.post-42663157036940973272010-04-01T14:52:00.000-07:002010-04-01T14:52:37.966-07:00Internet Math<div style="clear: both;">This image from <a href="http://www.collegehumor.com/article:1803250">College Humor</a> is intended as a parody, but there's quite a bit of truth in there:</div><br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhl6JBzjjkSAD_Xuxk-Jsru6lHZ4iRODhUQyszCEOjQDw4HS8rZ2aUcZVQx85FoZkt1LELUid2EFO5tGxMeLi2d01cwZUuHNs5u07VEZgi0EYRx8FEZYbESUWJJD4pla1pr0ueueguKC1I/s1600/Internet-Math-2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="400" nt="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhl6JBzjjkSAD_Xuxk-Jsru6lHZ4iRODhUQyszCEOjQDw4HS8rZ2aUcZVQx85FoZkt1LELUid2EFO5tGxMeLi2d01cwZUuHNs5u07VEZgi0EYRx8FEZYbESUWJJD4pla1pr0ueueguKC1I/s400/Internet-Math-2.jpg" width="231" /></a></div><br />
It seems like most "new" startups are simply <strong>XX + Social</strong>, or <strong>YY + Mobile</strong>. But, that business model seems to hinge on <strong>XX </strong>and <strong>YY</strong> neglecting to notice that the startup is trying to eat their lunch, and not immediately add the same functionality and squash said startup like a bug.Bryant Fonghttp://www.blogger.com/profile/04129122715461127965noreply@blogger.com0tag:blogger.com,1999:blog-5473649586767821485.post-6947357103430033552010-03-23T22:35:00.000-07:002010-03-23T22:36:00.630-07:00Best feature of Outlook 2010Office 2010 is almost ready to ship! I'm an <strong>Outlook</strong> user by day, and <strong>Gmail</strong> user by night. But I find that Gmail doesn't scale well when you are being flooded with e-mail -- for example, basic UI metaphors like shift-click don't work, and labels just don't cut it compared to Outlook rules. So, here's my favorite new feature from Outlook 2010 for dealing with floods of e-mail:<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgZNUut0ZGvnaoMXPXpvw2nfEzIef0HCPkHjwJfrlOtpaAs1aCPdTCfZdyU30bdT3cMj40o1uZMletJNSefy3ZNI-gKCsR9PfY9D7ytm8yDPQ8TmtaJX80ORgC8xrN61_8gMFCNM04KpyU/s1600-h/cleanup.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" kt="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgZNUut0ZGvnaoMXPXpvw2nfEzIef0HCPkHjwJfrlOtpaAs1aCPdTCfZdyU30bdT3cMj40o1uZMletJNSefy3ZNI-gKCsR9PfY9D7ytm8yDPQ8TmtaJX80ORgC8xrN61_8gMFCNM04KpyU/s320/cleanup.jpg" /></a></div><br />
Basically, it deletes any e-mails that are entirely contained within replies later in the conversation. This is great for high traffic discussion aliases and long-winded threads. There's just something <strong>really gratifying </strong>about pressing a button and seeing half my Inbox disappear..Bryant Fonghttp://www.blogger.com/profile/04129122715461127965noreply@blogger.com0tag:blogger.com,1999:blog-5473649586767821485.post-2375562338273501662010-03-14T20:45:00.001-07:002010-03-14T20:45:50.270-07:00Uh-oh for Windows?For most people, the two biggest advantages of a PC over a Mac are that Macs <strong>cost more</strong>, and you <strong>can't play</strong> <strong>(most) games </strong>on a Mac. Most Mac owners I know either have a separate gaming rig or dual boot to Windows just for video games.<br />
<br />
Today marks an <strong>inflection point </strong>in the Mac vs PC war: <a href="http://www.valvesoftware.com/news.php?id=3568"><strong>Steam has been ported to Mac</strong></a>! The only games I play on a PC anymore are those from Valve (Left 4 Dead, Counterstrike, Half-life, etc) and from Blizzard (Starcraft, Warcraft, etc). Most other games are better experienced on a console. Well, both of those sets of games are now going to be released for the Mac <strong>on the same day </strong>as the PC!<br />
<br />
As someone who owns Microsoft stock, this is a big problem. You do not want an OS where your main differentiator is that it's cheaper, or to rely on mass-market inertia. My computer use is split amongst internet use, coding, creativity software, office software, and video games. If I were to buy a computer today, for the first time, <strong>I would actually consider a Mac</strong>. For the first time, Mac has achieved parity with PC across <strong>my </strong>usage scenarios. <br />
<br />
This is a <strong>dangerous time</strong> <strong>for Microsoft</strong>.. tread carefully.Bryant Fonghttp://www.blogger.com/profile/04129122715461127965noreply@blogger.com0tag:blogger.com,1999:blog-5473649586767821485.post-22126425470812992582010-03-11T21:56:00.000-08:002010-03-11T21:56:00.260-08:00Color calibration, or lack thereofEvery monitor displays color <strong>differently</strong>. If you've ever used dual monitors, you know what I'm talking about. The picture below is my Lenovo T500 on the left, a Dell 2005WFP on the right:<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEieEl-BDD-H1C1k7HUsa2T9KSzxFMRmOcd8MJx9fLUzFdDe6LfD5H-Ii0hzQ3ieaBgds52__LEEhUXdJr22QKoAFHfemPSXR1Twwp2JCOEXogBWNh2vDs1SNOqXIl1F-1X-N96J5R-z6HQ/s1600-h/monitors.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="340" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEieEl-BDD-H1C1k7HUsa2T9KSzxFMRmOcd8MJx9fLUzFdDe6LfD5H-Ii0hzQ3ieaBgds52__LEEhUXdJr22QKoAFHfemPSXR1Twwp2JCOEXogBWNh2vDs1SNOqXIl1F-1X-N96J5R-z6HQ/s640/monitors.jpg" vt="true" width="640" /></a></div><br />
I suppose how much of a color difference you see in the two monitors above depends on <strong>your</strong> monitor's color profile, but for me, the standalone monitor comes across as having greener greens and redder reds. In fact, my laptop portrays <strong>this blog </strong>as a nice cool blue, whereas on my monitor it is a hideous shade of green. My intention is most certainly the blue variant, but I have no idea what <strong>other people </strong>are seeing.<br />
<br />
Anyways, this is really important for web design and photography. So, I am using this as an excuse to go buy a <a href="http://www.dell.com/us/en/dfo/peripherals/monitor-dell-u2410/pd.aspx?refid=monitor-dell-u2410&s=dfo">Dell U2410 IPS monitor</a> and a <a href="http://spyder.datacolor.com/product-mc.php">Spyder3 color calibrator</a>. That will ensure I am seeing what I am "supposed" to see, but presumably it remains a <strong>crapshoot </strong>for the remaining 99% of the world with uncalibrated monitors. They, no doubt, will take a look at this blog and see some unflattering and garish hue. Yuck.Bryant Fonghttp://www.blogger.com/profile/04129122715461127965noreply@blogger.com0tag:blogger.com,1999:blog-5473649586767821485.post-54889543239176421812010-03-09T10:35:00.000-08:002010-03-09T10:35:28.702-08:00Microsoft Azure ServicesMicrosoft is getting ready to release their cloud computing platform, Azure, and there's a pretty good <a href="http://go.microsoft.com/fwlink/?LinkId=158011">overview</a> written by David Chappell. One snippet which I found amusing was:<br />
<blockquote>Windows Azure platform AppFabric provides cloud-based infrastructure services. Microsoft is also creating an analogous technology known as Windows Server AppFabric. [...] Don’t be confused; throughout this paper, the name “AppFabric” is used to refer to the cloud-based services. Also, don’t confuse the Windows Azure platform AppFabric with the fabric component of Windows Azure itself. Even though both contain the term “fabric”, they’re wholly separate technologies addressing quite distinct problems.</blockquote>Don't be confused? <strong>Really?</strong> Then don't call everything "fabric"! I thought Microsoft had learned from the "Windows Live" naming debacle. Somebody needs to buy Microsoft a thesaurus..Bryant Fonghttp://www.blogger.com/profile/04129122715461127965noreply@blogger.com0tag:blogger.com,1999:blog-5473649586767821485.post-88629167424255940962010-02-28T09:59:00.000-08:002010-02-27T13:23:30.884-08:00Algorithms for storing and querying hierchical treesI've often found myself needing to represent hierarchical data in my database -- navigation trees, forum threads, organizational charts, taxonomies, etc. I've been trying different approaches to maintaining a hierarchy, and thought others might be interested in my findings. For purposes of illustration, our sample tree is the following:<br />
<br />
<pre><b> 1
/ \
2 4
/ \
3 5</b>
</pre><br />
<b>Approach #1: Adjacency list</b><br />
The idea here is simple, you store each node's parent in a table:<br />
<br />
<table bgcolor="white" border="1" style="text-align: center;"><tbody>
<tr><td bgcolor="gray" colspan="2"><b><span style="color: white;">table: nodes</span></b></td></tr>
<tr><td> <b>id </b></td><td><b> parent_id</b> </td></tr>
<tr><td>1</td><td>null</td></tr>
<tr><td>2</td><td>1</td></tr>
<tr><td>3</td><td>2</td></tr>
<tr><td>4</td><td>1</td></tr>
<tr><td>5</td><td>2</td></tr>
</tbody></table><br />
This is trivial to implement, but hierarchical queries become hard. In order to query for all nodes under a given branch, you have to recurse through its children. If you don't have too many nodes, you can just read the entire table into memory and cache it -- which is sufficient for most web site navigation structures, for example.<br />
<br />
<b>Approach #2: Store the Path as a string</b><br />
Here, the idea is that each node stores its path as string. For example, a node might have a path of "1_8_13". Thus, you could find the children of node "8" by querying for all nodes with a path of "1_8_%".<br />
<br />
<table bgcolor="white" border="1" style="text-align: center;"><tbody>
<tr><td bgcolor="gray" colspan="2"><b><span style="color: white;">table: nodes</span></b></td></tr>
<tr><td> <b>id </b></td><td><b> path</b> </td></tr>
<tr><td>1</td><td><div style="text-align: left;"> "1"</div></td></tr>
<tr><td>2</td><td><div style="text-align: left;"> "1_2"</div></td></tr>
<tr><td>3</td><td><div style="text-align: left;"> "1_2_3" </div></td></tr>
<tr><td>4</td><td><div style="text-align: left;"> "1_4"</div></td></tr>
<tr><td>5</td><td><div style="text-align: left;"> "1_2_5" </div></td></tr>
</tbody></table><br />
This gives you the benefit of hierarchical queries, but only if you add an index on the "path" column, forcing SQL to do the heavy lifting. And, since it's a string column, your performance will not be as fast as if it were integer-based.<br />
<br />
<b>Approach #3: Nested subsets</b><br />
The idea here is that each subtree is kept within a range of IDs, and the range of its subtree is stored in the node. In the example, the subtree of 1 is (obviously) within the range of 1..5. However, you'll notice the subtree of 2 is NOT within the range of 3..5 because node 4 violates that rule. As a result, we need a mutable ID in order to maintain the subset.<br />
<br />
<table bgcolor="white" border="1" style="text-align: center;"><tbody>
<tr><td bgcolor="gray" colspan="4"><b><span style="color: white;">table: nodes</span></b></td></tr>
<tr><td> <b>id </b></td><td><b> mutable_id </b></td><td><b> min_mutable_id </b></td><td><b> max_mutable_id</b> </td></tr>
<tr><td>1</td><td>1</td><td>1</td><td>5</td></tr>
<tr bgcolor="#eeee99"><td>2</td><td>2</td><td><b>3</b></td><td><b>4</b></td></tr>
<tr><td>3</td><td>3</td><td>3</td><td>3</td></tr>
<tr><td>4</td><td bgcolor="#eeee99"><b>5</b></td><td>5</td><td>5</td></tr>
<tr><td>5</td><td bgcolor="#eeee99"><b>4</b></td><td>4</td><td>4</td></tr>
</tbody></table><br />
Note how we had to swap the IDs of 4 and 5, so that node 2 could have a valid nested subset range of 3..4. This can easily happen on insertions as well and force us to recompute large parts of the table if shifting is required. However, hierarchical reads are fairly inexpensive, as they just become numerical range queries.<br />
<br />
<b>Approach #4: Expanded tree</b><br />
The idea here is that you store the normal adjacency list, but maintain another table of the tree already recursively expanded-out:<br />
<table border="0"><tbody>
<tr><td valign="top"><br />
<table bgcolor="white" border="1" style="text-align: center;"><tbody>
<tr><td bgcolor="gray" colspan="2"><b><span style="color: white;">table: nodes</span></b></td></tr>
<tr><td> <b>id </b></td><td><b> parent_id</b> </td></tr>
<tr><td>1</td><td>null</td></tr>
<tr><td>2</td><td>1</td></tr>
<tr><td>3</td><td>2</td></tr>
<tr><td>4</td><td>1</td></tr>
<tr><td>5</td><td>2</td></tr>
</tbody></table></td><td valign="top"><br />
<table bgcolor="white" border="1" style="text-align: center;"><tbody>
<tr><td bgcolor="gray" colspan="2"><b><span style="color: white;">table: nodes_expanded</span></b></td></tr>
<tr><td> <b>id </b></td><td><b> expanded_parent_id</b> </td></tr>
<tr><td>1</td><td>1</td></tr>
<tr><td>2</td><td>1</td></tr>
<tr bgcolor="#eeee99"><td>2</td><td>2</td></tr>
<tr bgcolor="#eeee99"><td>3</td><td>1</td></tr>
<tr><td>3</td><td>2</td></tr>
<tr bgcolor="#eeee99"><td>3</td><td>3</td></tr>
<tr><td>4</td><td>1</td></tr>
<tr bgcolor="#eeee99"><td>4</td><td>4</td></tr>
<tr bgcolor="#eeee99"><td>5</td><td>1</td></tr>
<tr><td>5</td><td>2</td></tr>
<tr bgcolor="#eeee99"><td>5</td><td>5</td></tr>
</tbody></table></td></tr>
</tbody></table>Essentially, the expanded table acts as a hierarchy cache. For example, to get all nodes under the "2" subtree, just find all nodes with (expanded_parent_id == 2), which will return matches on 2, 3, and 5 as expected. The main benefit of this approach is that all your SQL queries are based on exact match, whereas the last two approaches use range queries. Likewise, while an insertion will require you to futz with the "nodes_expanded" table, the data in the "nodes" table stays intact. With the nested subsets approach, you may find your main "nodes" table locked on reads while all the IDs get shuffled around. <br />
<br />
So, to summarize:<br />
<table bgcolor="white" border="1"><tbody>
<tr bgcolor="gray"><td></td><td><div style="text-align: center;"><b><span style="color: white; padding: 5px;">Pros</span></b></div></td><td><div style="text-align: center;"><b><span style="color: white; padding: 5px;">Cons</span></b></div></td></tr>
<tr><td nowrap="nowrap" valign="top"><div style="text-align: right;"><b>Adjacency list</b></div></td><td valign="top"><ul style="margin-left: 1em; padding-left: 5px;"><li>Easy to implement</li>
<li>Minimum storage</li>
</ul></td><td valign="top"><ul style="margin-left: 1em; padding-left: 5px;"><li>Slow calculation of subtrees (can mitigate with in-memory caching)</li>
</ul></td></tr>
<tr><td nowrap="nowrap" valign="top"><div style="text-align: right;"><b>Path substrings</b></div></td><td valign="top"><ul style="margin-left: 1em; padding-left: 5px;"><li>Easy to implement</li>
<li>Handles hierarchical queries</li>
</ul></td><td valign="top"><ul style="margin-left: 1em; padding-left: 5px;"><li>Relies on SQL index on a string column</li>
<li>Inefficient storage (only using 0-9 and "_" in the char range)</li>
</ul></td></tr>
<tr><td nowrap="nowrap" valign="top"><div style="text-align: right;"><b>Nested subsets</b></div></td><td valign="top"><ul style="margin-left: 1em; padding-left: 5px;"><li>Handles hierarchical queries</li>
</ul></td><td valign="top"><ul style="margin-left: 1em; padding-left: 5px;"><li>Insertions can be expensive</li>
<li>Insertions can result in lock contention</li>
</ul></td></tr>
<tr><td nowrap="nowrap" valign="top"><div style="text-align: right;"><b>Expanded tree</b></div></td><td valign="top"><ul style="margin-left: 1em; padding-left: 5px;"><li>Handles hierarchical queries</li>
<li>Hierarchy is pre-cached as a simple "equality" join</li>
</ul></td><td valign="top"><ul style="margin-left: 1em; padding-left: 5px;"><li>Requires maintaining separate "nodes_expanded" table</li>
<li>Insertions can be expensive, but not against the main "nodes" table</li>
</ul></td></tr>
</tbody></table><br />
Later, I hope to implement and benchmark each approach against each other. Any other algorithms worth investigating?Bryant Fonghttp://www.blogger.com/profile/04129122715461127965noreply@blogger.com0tag:blogger.com,1999:blog-5473649586767821485.post-712082731650235312010-02-20T11:14:00.000-08:002010-02-20T11:14:00.264-08:00Windows 7 ShortcutsJust thought I'd share some shortcut keys I use all the time:<br />
<br />
<strong>Windows + D:</strong> Show Desktop<br />
<strong>Windows + Tab</strong>: 3D Flip<br />
<strong>Windows + #</strong>: Runs the #'th program on your Quick Launch<br />
<br />
And in Explorer:<br />
<br />
<strong>Shift+Right-Click on a folder/file:</strong> Additional options like "Open command window here"<br />
<strong>Alt+Up:</strong> Goes up a folder level in Windows Explorer (plus Alt+Left/Right for Back/Forward)Bryant Fonghttp://www.blogger.com/profile/04129122715461127965noreply@blogger.com0tag:blogger.com,1999:blog-5473649586767821485.post-64422469353663315032010-02-14T13:14:00.000-08:002010-02-14T13:14:00.368-08:00My love for dependencies ...Once upon a time, we had a developer whose full-time job was debugging random issues in some particular feature. That feature had a dependency on an external team who had no vested interest in this feature, and therefore using their library was a bit like using <strong>chopsticks </strong>(their library) <strong>to eat steak </strong>(of course our feature is the delicious steak). Sure, you can use the chopsticks, but every time you do you question whether you'd be better off without them and just eating the steak with your hands.<br />
<br />
Couldn't get any <em>worse</em>, right?<br />
<br />
So, when a different team approached us with a product that was a perfect <strong>fork and knife</strong> that they used to eat steak every day for the last three years, we chomped at the bit to get a hold of it. Long story short, their utensils were <strong>made of plastic </strong>and were constantly breaking, and now we have <strong><em>two</em> </strong>developers whose full-time jobs are debugging random issues in this feature. <br />
<br />
We long for the days of having chopsticks to eat our steak. Do not take dependencies lightly.Bryant Fonghttp://www.blogger.com/profile/04129122715461127965noreply@blogger.com0tag:blogger.com,1999:blog-5473649586767821485.post-24060495597474737632010-02-01T10:06:00.000-08:002010-02-10T09:56:30.712-08:00Hard Drive Backup with Live MeshI <strong>hope</strong> everyone out there is backing up their data. Up until now, I've used the<strong> </strong>tried-and-true method of copying my files periodically to another drive. Of course, in the event of data catastrophy, I would lose all my changes since the last xcopy .. which was .. about 9 months ago. A file backup gestation period, if you will.<br />
<br />
In any case, I'm now using <a href="http://mesh.live.com/">Live Mesh</a>. It's cross-platform and you get 5gb of online storage for free (you can sync unlimited data between machines). I've synchronized my musics, videos, and documents between all my machines which is pretty <strong>fantastic</strong>. In case you want to try it, here's what I would have liked to know <strong>beforehand</strong>:<br />
<ul><li>You <strong>cannot</strong> synchronize your Desktop folder.</li>
<li>Your <strong>first 5gb </strong>of synchronized files ends up in the cloud. Choose wisely.</li>
<li>You have to login with a LiveID, but it <strong>doesn't share cookies</strong> with the browser. So, if you will ever want to sync with a friend, create a new LiveID to share.</li>
<li>When you add a folder to be sync'd, it will show up on every other machine as a virtual folder. This can be very confusing when you've named them all "Documents" -- <strong>prefix folder names</strong> with the computer name.</li>
</ul>My next step is to set up a sync with my a friend in another state, in case my home with all my computers burns down. Overall, it was pretty easy to setup, although I now have a <strong>paranoia </strong>that one node will decide to delete something, and spontaneously trigger all my files to be deleted on every machine simultaneously.Bryant Fonghttp://www.blogger.com/profile/04129122715461127965noreply@blogger.com0tag:blogger.com,1999:blog-5473649586767821485.post-44152202484694677712010-01-27T21:43:00.000-08:002010-01-29T09:52:52.667-08:00Concurrency bug..OK, spot the bug in the code:<br />
<div class="codebox"><code><br />
<span style="color: blue;">object</span> m_lockObject = new <span style="color: blue;">object</span>();<br />
<span style="color: blue;">object</span>[] m_collection = <span style="color: blue;">null</span>;<br />
<br />
public object[] GetCollection() {<br />
<span style="color: blue;">lock</span> (m_lockObject) {<br />
<span style="color: blue;">if</span> (m_collection != <span style="color: blue;">null</span>) {<br />
<span style="color: #38761d;"> // already initialized</span><br />
<span style="color: blue;">return</span> m_collection;<br />
}<br />
<span style="color: blue;">else</span> {<br />
<span style="color: #38761d;"> // needs to be initialized</span><br />
m_collection = <span style="color: blue;">new object</span>[5];<br />
initialize(m_collection);<br />
<span style="color: blue;">return</span> m_collection;<br />
} <br />
}<br />
}</code></div>.. the bug is that a second call could come <strong>after </strong>m_collection is new'd up, but <strong>before </strong>it's initialized, resulting in an empty collection being returned. The first call works, the second call <strong><em>sometimes fails</em></strong>, and the third call onwards likely succeeds. Bugs like this can be a pain to track down as, depending on what these objects do, the symptoms will appear really strange...Bryant Fonghttp://www.blogger.com/profile/04129122715461127965noreply@blogger.com0tag:blogger.com,1999:blog-5473649586767821485.post-76161760902124491412010-01-05T10:34:00.000-08:002010-02-10T09:55:12.266-08:00Managing your time wiselyI'm one of those people who strive to be “efficient”. I learned this playing games like <a href="http://www.amazon.com/exec/obidos/tg/detail/-/0782122671/qid=1125652304/sr=8-1/ref=sr_8_xs_ap_i1_xgl14/103-5791352-9274266?v=glance&s=books&n=507846">Starcraft</a>. To win, you have to click like a madman to control everything at once. The best players were above <strong>200 clicks per minute</strong>. And, you better type at light speed, otherwise you will get clobbered while writing messages to your teammates. At work, this means I don't sit around for process recycle or rebuild, I always go quick-check something else while I wait. I've read that your brain thinks at around <strong>400 wpm (words per minute)</strong>, so even if you type at a zippy <strong>150 wpm </strong>then you are wasting braincycles. When I watch people type at a very reasonable 60wpm, it takes every ounce of resistance in my body not to rip the keyboard away and type for them.<br />
<br />
So yes, patience is not one of my virtues. As a result, I cannot believe a <strong>3.0ghz quad-code computer</strong> makes me wait. Ever. Everytime Outlook hangs while I'm in the middle of typing my e-mail, I can't help but <strong>flip it the bird</strong>. What on earth is it doing? If not for NetBIOS name restrictions, my computer's names would be <strong>!$(^$@(</strong> and <strong>%!%^(*.</strong> <br />
<br />
Anyways, some tips for dealing with e-mail:<br />
<br />
<ul><li><strong>Reply to the e-mail the first time you read it.</strong> It takes a few minutes to context switch into a problem, so make sure to only do it once. Don't “save this mail for later“, because you'll either forget, waste time reading it again, or you're making the other guy wait. Even a brief initial response is often enough for the sender to figure the problem out.</li>
<li><strong>Delete the e-mail as soon as you reply. </strong>Don't worry, it'll be in your trash for a while, and you have your "sent items" to fall back on too. But the net result will be a clean Inbox which reads like a to-do list so you won't lose track of things. </li>
<li><strong>If your response is going to be more than a paragraph or two, go talk in person.</strong> I could not believe how long it takes to craft a well thought-out e-mail -- try timing yourself sometime. And even then, the recipient usually just asks you to schedule a meeting and it quickly becomes clear they didn't even bother reading the mail. </li>
</ul>Some tips for software development:<br />
<ul><li><strong>Invest in your development environment. </strong>Spend time learning all the shortkeys, discovering ways to customize the tools you use every day, and get the hardware that will make you most productive. Start with a new monitor. A <strong>big </strong>one.</li>
<li><strong>Given that your typing speed is a constant, reduce the amount you have to type.</strong> I create batch files for everything -- "n" for notepad, "d" for diff, "b" to rebuild, shortkeys to take me directly to common directory paths, etc. I ditched my hardware KVM switch because of the two-second switching lag -- using software to swap desktops is instantaneous. It may not sound like much, but instantaneous is an order of magnitude better and can change the way you work.</li>
<li><strong>Automate repetitive tasks. </strong>If you find yourself doing the same thing over and over, you can save tons of time by automating it. I've written tons of tools that do repetitive, labor-intensive tasks automatically, and your peers will appreciate it too when you share it with them. </li>
</ul>Strategies that <strong>haven’t </strong>worked out for me:<br />
<ul><li><strong>Closing the door </strong>doesn’t prevent people from stopping by, and <strong>it shouldn’t</strong>. The fact that they invested the time to pay you a visit, means that it must be important to them. Ignoring them may only save you five minutes, but cost them an hour. </li>
<li>Having separate dedicated boxes for coding and e-mailing doesn’t allow me to focus single-mindedly on programming. It just makes me switch between machines all the time. </li>
<li>When I come in <strong>early</strong> in the morning, I don't get any additional work done. If I don't get sleep, I will spend the morning sipping tea and reading the news. <em>Even more than usual</em>.</li>
</ul>What strategies work for you?Bryant Fonghttp://www.blogger.com/profile/04129122715461127965noreply@blogger.com0tag:blogger.com,1999:blog-5473649586767821485.post-52277068405953839512009-10-29T17:25:00.000-07:002010-01-29T11:17:27.752-08:00Communication patterns and diversitySomeone recently sent me a tool which measures whether your brain is left/right-hemisphere dominant, and auditory/visual. I am <strong>left+visual</strong>, and prefer logic and visual patterns. In a discussion I will find myself going to the whiteboard, even sometimes when it's not necessary, and highly prefer people draw their ideas out because I like having something to stare at.<br />
<br />
Anyways, this sort of thing gets interesting when you have two people at the <strong>opposite</strong> ends of the spectrum. When they are trying to communicate, the visual guy may be drawing a diagram and asking "Why <em>can't you <strong>see </strong>this?</em>" to the auditory guy, who is thinking, "<em>Why can't you <strong>hear </strong>what I'm saying?</em>". Other communication examples are where one person wants to discuss big picture and the other wants to start with the details, or where one person prefers face-to-face and the other prefers e-mail discussions.<br />
<br />
With a diverse group of people, the challenge is understanding everyone's points of view and having both sides alter their <strong>behaviors</strong> to accommodate each other. I've heard someone tell me that each slide in a presentation should have at most one sentence or picture, and bullet-points are the work of the devil. But I think a good presentation should<strong> appeal to both</strong> auditory and visual people, and not just one half or the other. As another example, a meeting with documents sent beforehand, provides face-to-face time via the meeting while allowing people to send questions/comments via e-mail. Small behavior changes like these make all sides feel comfortable and help everyone <strong>communicate successfully</strong>.Bryant Fonghttp://www.blogger.com/profile/04129122715461127965noreply@blogger.com0tag:blogger.com,1999:blog-5473649586767821485.post-39967924354338568472009-08-02T22:43:00.000-07:002010-01-29T17:44:55.909-08:00If you think Microsoft code is bad, you should read <a href="http://www.thedailywtf.com/">DailyWTF</a>. It has great coding patterns like:<br />
<div class="codebox"><pre><span style="color: blue;"> try</span>
{
<span style="color: blue;">int</span> idx = 0;
<span style="color: blue;">while</span> (<span style="color: blue;">true</span>)
{
displayProductInfo(prodnums[idx]);
idx++;
}
}
<span style="color: blue;"><span style="color: black;"> </span>catch</span> (IndexOutOfBoundException ex)
{
}
</pre></div>And sweet UI dialogs like:<br />
<div class="codebox"><strong><span style="font-family: Courier New;"> Cancel print job? <br />
OK | Cancel</span> </strong></div>And stories about "<a href="http://thedailywtf.com/forums/25129/ShowPost.aspx">C-Pound</a>". Yes, geek humor.Bryant Fonghttp://www.blogger.com/profile/04129122715461127965noreply@blogger.com0tag:blogger.com,1999:blog-5473649586767821485.post-25279100585774531222009-06-08T15:53:00.000-07:002010-01-29T11:16:50.464-08:00Writing Office client codeWriting Office client code is ... an experience.<br />
<br />
<strong>First</strong>, everything is in unmanaged code, which means <strong>each</strong> line of code takes 5 minutes to write. Why? Because you have to check the APIs every time, to make sure you know who owns the memory and what the exact parameters are. If you don't, you will get an intermittent AV or corrupt the heap and crash the app. And, since none of the APIs are documented, you have to go trolling through the sources to figure out what the hell's going on.<br />
<br />
<strong>Second</strong>, you get to use classes with <strong>interesting names</strong>. For example, their web service APIs are still called things like HSUser -- long live HailStorm! Then you've got funny ones like “BpscBulletProof”, obscure ones like “MsoGelIInsertSortPx”, and classes like “MSOGRFXMLNS” (buy a vowel?).<br />
<br />
<strong>Third</strong>, Office has <strong>wrappers</strong> for almost everything. I started by writing everything in standard C/C++, and it all worked beautifully. But that was a big mistake, because none of it worked once I tried to merge it into the code depot. I wasted the better part of a week translating ATLSoap to CBaseHSUser, vector to MSOPX, CMap to LKRHash, RegQueryEx to MsoRegReadWz, CString to CMsoString, and the list goes on. <br />
<br />
<strong>Fourth</strong>, the wrappers are actually pretty neat -- <strong>until you have to debug one</strong>. Good luck figuring out what the author's intention was for “const WCHAR* const *rgwzArg” or “MSOPFNSGNPX pfnSgn” (I couldn't make that up even if I tried). The time I saved using a “convenient wrapper” is quickly negated by having to figure out why my memory keeps getting wiped out. It's like that movie Memento, but with a <strong>lot more swearing</strong>.<br />
<br />
I think that's enough ranting for one post, back to work.Bryant Fonghttp://www.blogger.com/profile/04129122715461127965noreply@blogger.com0tag:blogger.com,1999:blog-5473649586767821485.post-60983454138759999142009-04-22T11:57:00.000-07:002010-01-29T10:04:59.564-08:00OK, now this is getting ridiculous..Last time, they tore apart the roof and put it at my front door. This time, they got rid of the door!<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh7_cNOim0jfmg_YRN0hRo2SvUZyenUqmF6NIzcGTJFmYQLPtx9fDQjQlSpXKSZNcxWBnL44yhuPm6rMuSjsudSNbTjPcTMmHq1_O6a5JDnudHROHFhDTJMR_28mCxKpL_fU4FxDvgGnOo/s1600-h/nodoor.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" kt="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh7_cNOim0jfmg_YRN0hRo2SvUZyenUqmF6NIzcGTJFmYQLPtx9fDQjQlSpXKSZNcxWBnL44yhuPm6rMuSjsudSNbTjPcTMmHq1_O6a5JDnudHROHFhDTJMR_28mCxKpL_fU4FxDvgGnOo/s320/nodoor.jpg" /></a></div><br />
In it's place was a tarp that was stapled on all sides to the frame. The problem is, this is the only way out! Trapped inside, I mulled about, ate breakfast, checked my email, but still my door was not to be found. Figuring I had to leave for work, I eventually pried the tarp off and crawled underneath. None of the workers seemed bothered -- did they expect everyone to do that? Strange..Bryant Fonghttp://www.blogger.com/profile/04129122715461127965noreply@blogger.com0tag:blogger.com,1999:blog-5473649586767821485.post-83178666306900881952009-04-05T10:36:00.000-07:002010-01-29T11:17:49.450-08:00My roof, my roof, my roof is on ... the ground?I don't really know what goes on in my neighborhood, mainly because I never make it to the 7pm home owner meetings. One morning, I was awoken by a <strong>loud pounding </strong>that shook the whole room. When I opened my front door, this is what I found:<br />
<br />
<div align="left" class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh7AQEGWiwGQd_MIfxZ23SYE9Jju53c0zohMrm_poDkn45nerhs9BWqM6GBMJBgepO69R6juQ-oAbR8_qrc9dlW8qA0WCdKRzEpMlGLhKVil0JNi1cW-S2f2UDpfpzWtyMv7sgEmESRP4k/s1600-h/roof.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" kt="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh7AQEGWiwGQd_MIfxZ23SYE9Jju53c0zohMrm_poDkn45nerhs9BWqM6GBMJBgepO69R6juQ-oAbR8_qrc9dlW8qA0WCdKRzEpMlGLhKVil0JNi1cW-S2f2UDpfpzWtyMv7sgEmESRP4k/s320/roof.JPG" /></a></div><br />
Apparently, a huge pile of my <strong>roof</strong> was lying there -- about two feet high... I hope it doesn't rain this week?Bryant Fonghttp://www.blogger.com/profile/04129122715461127965noreply@blogger.com0tag:blogger.com,1999:blog-5473649586767821485.post-38096338200495292092009-03-02T00:46:00.000-08:002010-01-29T11:18:01.544-08:00Calling a web service from unmanaged codeJust wasted a few hours of my life with a seemingly <strong>trivial</strong> segment of code. Spot the error:<br />
<div class="codebox"><code><br />
HRESULT hrInit = CoInitialize(NULL); <br />
<br />
CUserProfileWebService webService;<br />
webService.Execute();<br />
<br />
if (SUCCEEDED(hrInit))<br />
CoUninitialize();<br />
</code></div>I was getting an Access Violation when the application exited, which means I was trying to access memory that no longer belonged to me. Turns out, the problem was due to scope. The "<strong>webService</strong>" object doesn't fall out of scope until <strong>after</strong> CoUninitialize has been called, so it doesn't know to clean itself up until it's too late. The fix is to enclose the webService variable in it's own scope, such as the following:<br />
<div class="codebox"><code> HRESULT hrInit = CoInitialize(NULL); <br />
<span style="color: red;"> {</span><br />
CUserProfileWebService webService;<br />
webService.Execute();<br />
<span style="color: red;"> }</span><br />
<br />
if (SUCCEEDED(hrInit))<br />
CoUninitialize();</code></div>In this instance, “<strong>webService</strong>” will clean itself up immediately after the <span style="color: red;">}</span> curly brace. Of course, it took <strong>forever</strong> to narrow it down to these five lines of code, at which point I realized my mistake. *sigh*Bryant Fonghttp://www.blogger.com/profile/04129122715461127965noreply@blogger.com1tag:blogger.com,1999:blog-5473649586767821485.post-20282344795165290072009-01-18T11:08:00.000-08:002010-01-29T09:40:11.033-08:00When to bug someone for help?<div class="entryText">Let's imagine a <strong>hypothetical</strong> scenario where a group with whom you have a dependency on assigns a bug to you, telling you it is not their problem. And, let's also say that you had little familiarity with this code. Would you:<br />
<br />
<strong>A) </strong>Spend an hour to generally understand the code, and then try to debug what was wrong?<br />
<strong>B) </strong>Spend an hour to generally understand the code, and then find someone to explain the specific problem to you?<br />
<strong>C)</strong> Find someone to explain the specific problem to you, then spend an hour to generally understand the code?<br />
<strong>D)</strong> Twiddle your thumbs until someone with a more vested interest in this problem takes it over from you.<br />
<br />
I think the answer is, it depends. All <strong>too many of us</strong> go with Option A, simply accepting ownership of the problem. Sure, you will learn the most by debugging everything yourself, but it will take the longest amount of time. Often times, you don't <strong><em>care </em></strong>about learning about this code and just want to hack through to find a reasonable fix. This approach would only be appropriate if you want to own this code <strong>long term</strong>, and the debugging time is essentially a time investment.<br />
<br />
Researching the issue first will allow you to <strong>absorb</strong> the most when you finally do talk to the expert, since you will actually <strong>understand</strong> what he's talking about. But talking to the expert first will save you ramp-up time, and has the additional benefit that they may realize they can fix it in <strong>less</strong> time it takes to explain it to you, and just fix it themselves. I feel it works best for cross-group collaboration to do your own <strong>due diligence</strong> first, except in cases where time is critical.<br />
<br />
Finally, there are times when <strong>even thumb twiddling</strong> might be appropriate. Sometimes, the bug is sufficiently complex that you need the issues surrounding it to bake before you can tackle your own problem. Sometimes, time is the only way for the other group to see the light. Sometimes, time will cause the <strong>genuine priority</strong> of the bug to emerge, when it is weighed against other issues for possible postponement, and stakeholders protest. I find the right things usually happen when they are being addressed by the people who care about it the most.</div>Bryant Fonghttp://www.blogger.com/profile/04129122715461127965noreply@blogger.com2tag:blogger.com,1999:blog-5473649586767821485.post-10701542920770868232009-01-01T08:57:00.000-08:002010-01-29T09:07:28.429-08:00Welcome ..I am a dev lead at Microsoft, which means I write some code and lead a hardy group of engineers writing cool features. I expect to write about software, engineering, technology, and the like.. but I intentionally named this blog "null or empty" because there's a 50/50 chance the blog stays exactly that. The name is a reference to the commonly used .NET api, String.IsNullOrEmpty(). Anyways, let's start with a comic:<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9yNX6uXK5HWuBPlPOzPchEw-SWSuMgbEDm0hV6vEdREoHGK5Bj7w8SSdnEz3hy-vmJPE4BZio3MTVCW7A_EoCEzjPzedC4c7S2aUOVwSSDDWWxkJVZ2fNQq5pEwURKQgUZ0PKXsh1M4Q/s1600-h/estimation_sm.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" kt="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9yNX6uXK5HWuBPlPOzPchEw-SWSuMgbEDm0hV6vEdREoHGK5Bj7w8SSdnEz3hy-vmJPE4BZio3MTVCW7A_EoCEzjPzedC4c7S2aUOVwSSDDWWxkJVZ2fNQq5pEwURKQgUZ0PKXsh1M4Q/s320/estimation_sm.gif" /></a></div><br />
If you can relate to this comic, hopefully you will be able to relate to this blog.Bryant Fonghttp://www.blogger.com/profile/04129122715461127965noreply@blogger.com0