<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-9239626</id><updated>2011-04-21T18:15:15.178-07:00</updated><title type='text'>kuantanzai</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://kuantanzai.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9239626/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://kuantanzai.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>kuantanzai</name><uri>http://www.blogger.com/profile/07836860726508898603</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>18</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-9239626.post-111936778335387224</id><published>2005-06-21T08:28:00.000-07:00</published><updated>2005-06-21T08:29:43.356-07:00</updated><title type='text'>Moving to YeAndNoni.com</title><content type='html'>I have moved my blog to &lt;a href="http://www.yeandnoni.com"&gt;YeAndNoni.com&lt;/a&gt;. This blog will no longer be updated.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9239626-111936778335387224?l=kuantanzai.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuantanzai.blogspot.com/feeds/111936778335387224/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9239626&amp;postID=111936778335387224' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9239626/posts/default/111936778335387224'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9239626/posts/default/111936778335387224'/><link rel='alternate' type='text/html' href='http://kuantanzai.blogspot.com/2005/06/moving-to-yeandnonicom.html' title='Moving to YeAndNoni.com'/><author><name>kuantanzai</name><uri>http://www.blogger.com/profile/07836860726508898603</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9239626.post-111870087230581412</id><published>2005-06-13T14:58:00.000-07:00</published><updated>2005-06-13T15:16:52.523-07:00</updated><title type='text'>I'm Jealous of People Who Work at FogCreek</title><content type='html'>If you are a fan of Joel and his company, like I am, you would have followed closely the progress of Project Aardvark and what the interns are doing. Last Friday afternoon Joel sent an &lt;a href="http://www.projectaardvark.com/posts/hickswright/june/13.html" target="_blank"&gt;email&lt;/a&gt; to FogCreek employees:&lt;br /&gt;&lt;br /&gt;"I thought it might be fun to go to a movie instead of working...&lt;br /&gt;&lt;br /&gt;We have 12:45 lunch reservations at Chevy’s in Battery Park City and 2:25 reservations for the opening of Mr. &amp; Mrs. Smith, so let’s officially close the office at noon today.&lt;br /&gt;&lt;br /&gt;Joel"&lt;br /&gt;&lt;br /&gt;The reaction from one of the interns was "I Love This Place!". Hence the saying "a little goes a long way".&lt;br /&gt;&lt;br /&gt;I'm working on a similar plan with my team of developers. Our team has been putting in some crazy amount of hours the past few weeks. I know I will be getting a budget to spend for this kind of stuff for my department. The biggest hurdle is convincing management to let us off early. But with the number of hours the team has thrown in lately I am fairly confident this will work out in our favor.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9239626-111870087230581412?l=kuantanzai.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuantanzai.blogspot.com/feeds/111870087230581412/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9239626&amp;postID=111870087230581412' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9239626/posts/default/111870087230581412'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9239626/posts/default/111870087230581412'/><link rel='alternate' type='text/html' href='http://kuantanzai.blogspot.com/2005/06/im-jealous-of-people-who-work-at.html' title='I&apos;m Jealous of People Who Work at FogCreek'/><author><name>kuantanzai</name><uri>http://www.blogger.com/profile/07836860726508898603</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9239626.post-111786484833040078</id><published>2005-06-09T12:00:00.000-07:00</published><updated>2005-06-09T12:32:24.946-07:00</updated><title type='text'>Satay Malaysian Grille</title><content type='html'>So Satay finally opened its doors to the public a couple of weeks ago. Last Friday, my wife and I went to the only Malaysian restaurant in Vegas.&lt;br /&gt;&lt;br /&gt;Satay is at the corner of Spring Mountain Rd and Valley View. Spring Mountain Rd is Vegas Chinatown. Naturally, the building looks like a chinese temple. Satay shares the building with Starbucks and T-Mobile. The parking lot seems a little too small. But because it's small it always seems to be packed - good for business.&lt;br /&gt;&lt;br /&gt;&lt;img src="http://www.completenorthamerica.com/city_brk/las-vegas-strip.jpg" align="left" style="margin-right: 10px;" /&gt;My first criticism about the building itself is the lack of lighting. It's important for a restaurant to be bright. People like to dine in bright places, especially in Vegas where you need to live up to the casino lighting standards. The restaurant has glass windows all around. Unfortunately, one of the windows let you peek behind the bar. And usually behind the bar is not a pretty sight. You see a lot of hoses and metal sinks.&lt;br /&gt;&lt;br /&gt;The entrance is just typical glass doors. Not a bad thing, but not too interesting either. We were first greeted by a waterfall glass screen. Classy. The hostess then sat us at a table in the smoking area because the non-smoking area was full. No big deal because nobody was smoking in the smoking area at that time.&lt;br /&gt;&lt;br /&gt;We looked at the menus. Typical Malaysian restaurant menu. No surprises there. We went straight for the Malaysian favorites: &lt;em&gt;roti canai&lt;/em&gt; $3.50, &lt;em&gt;nasi lemak&lt;/em&gt; $7.95 and &lt;em&gt;beef rendang&lt;/em&gt; with rice $8.95. Hmm... may be some &lt;em&gt;kangkung belacan&lt;/em&gt; later if the portions were too small.&lt;br /&gt;&lt;br /&gt;The waitress was nice. She's not Malaysian. I can tell from the way she speaks english. Because Malaysians learn British english at school but watch American shows at home, we spell "colour" with an 'u' but speak with an accent that is closer to the American accent. Since a lot of Malaysian chinese speak english and we watch chinese shows from Hong Kong, we have developed our own Malaysian english accent. We tend to inject into our sentences sounds adopted from the Hong Kong slangs such as "meh", "ah", "loh" that do not mean anything by themselves but would emphasize emotion - "You serious, meh?", "Why you so like that, ah?", "You tell him, loh." Malaysians who have lived in english speaking countries for a long time tend to lose that accent because we would only get a puzzled "Huh?" if we kept our accent. So that we don't sound funny, we dropped those weird sounds but kept the rest. Our intonation would still tell that we are Malaysians.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;An Introduction to Malaysian Food&lt;/strong&gt;&lt;br /&gt;&lt;img src="http://www.froggyknits.com/weblog/archives/penangroti-thumb.gif" align="right" /&gt;Anyway, first, some introduction to the food... &lt;em&gt;Roti canai&lt;/em&gt; was originally known as &lt;a href="http://www.geocities.com/Tokyo/Market/7773/roticanai.html" target="_blank"&gt;&lt;em&gt;paratha&lt;/em&gt;&lt;/a&gt;, a crispy South Indian Flatbread. &lt;em&gt;Paratha&lt;/em&gt; is a plain pastry. But creative Malaysians started to stuff butter, eggs, bananas and sardines in the flatbread and thus &lt;em&gt;roti canai&lt;/em&gt; was born. &lt;em&gt;Roti canai&lt;/em&gt; is usually served with chicken curry. There are many types of &lt;em&gt;roti&lt;/em&gt;. &lt;em&gt;Roti paper&lt;/em&gt; is a flatbread so thin it disintegrates into tiny flakes if you apply a little too much pressure, thus the name. &lt;em&gt;Roti planta&lt;/em&gt; is stuffed with a popular brand of margarine called &lt;a href="http://www.unilever.com.my/planta_i.htm" target="_blank"&gt;Planta&lt;/a&gt;. Malaysians are not too creative when it comes to naming food. But the names are self explanatory.&lt;br /&gt;&lt;br /&gt;&lt;img src="http://allmalaysia.info/msiaknow/malaysiana/images/customs/nasilemak5.jpg" align="left" style="margin-right: 10px;" /&gt;&lt;em&gt;Nasi lemak&lt;/em&gt; is another well-known Malaysian dish. The dish is a combination of white rice cooked in &lt;em&gt;santan&lt;/em&gt; (coconut milk), &lt;em&gt;sambal&lt;/em&gt; (chili paste with onions and anchovies), deep fried &lt;em&gt;ikan bilis&lt;/em&gt; (anchovies), fried peanuts, wedges of half-boiled eggs or fried eggs, slices of cucumber and curry chicken, &lt;em&gt;beef rendang&lt;/em&gt; or &lt;em&gt;mutton rendang&lt;/em&gt;. &lt;em&gt;Nasi lemak&lt;/em&gt; is often served on banana leaves on a plate or wrapped up in banana leaves for those in a hurry. Most Malaysian foods are spicy and bursting with flavors. Dishes are cooked with a complex combination of spices that make each dish distinct in taste, appearance and aroma.&lt;br /&gt;&lt;br /&gt;&lt;img src="http://www.shermay.com/images/cookbooks_new_vol2_beefrendang.jpg" align="right" /&gt;&lt;em&gt;Beef rendang&lt;/em&gt; is another Malaysian favorite. 20,000 miles away from Malaysia, you can still find this dish in any Malaysian restaurant in the US. &lt;em&gt;Rendang&lt;/em&gt; is much like dry curry. The gravy is thick and spicy. Good &lt;em&gt;rendang&lt;/em&gt; takes about three hours to cook so that the sauce thickens well and the beef tenderizes and fully absorbs the rich flavor of the spices. &lt;em&gt;Beef rendang&lt;/em&gt; is usually served with plain white rice or &lt;em&gt;nasi lemak&lt;/em&gt;.&lt;br /&gt;&lt;br /&gt;&lt;img src="http://www.sbestfood.com/Photos/pagisore/Kangkungbelachan.jpg" align="left" style="margin-right: 10px;" /&gt;&lt;em&gt;Kangkung belacan&lt;/em&gt; is really water convolvulus cooked in what some people would consider stinky shrimp paste. On cooking with &lt;em&gt;belacan&lt;/em&gt;:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.malaysianfood.net/orderbelacan.htm" target="_blank"&gt;"You'll be well advised to ventilate your kitchen and light a scented candle; as it has a pungent scent like 'stinky cheese'"&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.malaysianfood.net/orderbelacan.htm" target="_blank"&gt;&lt;/a&gt;Yes, Malaysians are also well-known for our sometimes "weird" taste buds. If you like &lt;em&gt;belacan&lt;/em&gt;, you may also want to consider equally aromatic &lt;em&gt;salted fish&lt;/em&gt;, &lt;em&gt;cencaluk &lt;/em&gt;(another shrimp paste) and &lt;em&gt;&lt;a href="http://www.nagani.com/food/ingredient/d/" target="_blank"&gt;durian&lt;/a&gt;&lt;/em&gt; (king of the fruits, this one has a scent so strong it is banned from airplanes, hotels and restaurants).&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Associating and Hoping for Special Treatment&lt;/strong&gt;&lt;br /&gt;Anyway, we saw this other lady. My wife said she used to work at this Vietnamese restaurant that we frequent for the &lt;em&gt;pho&lt;/em&gt; (rice noodles). So we said hi, hoping she would recognize us so we don't look like idiots. At first she was puzzled, but then she smiled and remembered us. She now works at Satay as a manager of some sort. I'm guessing she's part owner because she's dressed different from the other waiters and orders people around. We chat a little and exchanged some background information. She's from Ipoh. Been in the states for 14 years. It's interesting to meet people from your home country. I guess Satay is beginning to establish itself as a place where all Malaysians in Vegas gather to meet and eat. I can't help hoping for some kind of special treatment simply because I am Malaysian too.&lt;br /&gt;&lt;br /&gt;&lt;em&gt;Special Treatment #1?&lt;/em&gt;&lt;br /&gt;We noticed there was an empty booth in the non-smoking area and the lady responded with "No... (a pause) problem" and smiled. &lt;em&gt;Roti canai&lt;/em&gt; was served first. The curry for the roti was excellent. The &lt;em&gt;roti&lt;/em&gt; was a little mushy. Seems to me it was one of those frozen &lt;em&gt;roti canais&lt;/em&gt; you can get at the asian supermarkets. The portion was small. But for an appetizer it's acceptable. I think it's one and a half pieces of &lt;em&gt;roti &lt;/em&gt;sliced into quarters.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;The Food&lt;/strong&gt;&lt;br /&gt;Waiting for the main entrée seemed like forever. At one point the waitress told us our food was coming and she had asked the chef to cook faster. We didn't mind the wait. But I wondered what she meant by "cook faster". I stopped myself when I imagined the chef skipping the part where she was supposed to wash the vegetables and wait until the meat is well cooked.&lt;br /&gt;&lt;br /&gt;While waiting my wife and I observed the interior design of the restaurant and the people who were eating there. The furnishing was made to mimic high-end restaurants. Strong straight lines with bold colors of black, red, yellow and gold. The booth where we sat had a canopy with curtains tied to both seats. I suppose it could be used to give people some privacy by closing the curtains. But then again, how many people would want that kind of privacy? Furniture is made of cheap materials although effective. I have no problem with cheap materials as long as they appear to be expensive. It's understandable you would want to cut some corners somewhere especially when you were just starting out. However, I thought the overall design lacks distinction. Being the only Malaysian restaurant in town, you have an advantage. You serve food that no one else serves. So why stop there? Re-create the Malaysian dining atmosphere in your restaurant. I think that will make your restaurant more appealing, interesting and talk of the town. You found a niche where you could be a leader in. Why would you want to go into some other niche and compete with the French and Italian restaurants in the casinos that buys million dollar original paintings just to decorate a wall?&lt;br /&gt;&lt;br /&gt;While I played critic, the first entrée, &lt;em&gt;beef rendang&lt;/em&gt;, was finally served. The portion was ridiculously tiny. Two pieces of beef with a little gravy and some white rice. My immediate response to my wife was "Should we get the &lt;em&gt;kangkung belacan&lt;/em&gt;?" She nodded. I am a food fanatic and I think pretty much most people are. I learned presentation comes first because we see the food first. The &lt;em&gt;beef rendang&lt;/em&gt; was served on a giant white ceramic plate with a tiny portion in the center. I'm okay with that. It sort of creates an illusion that you are being served a big portion. And when you are served a generous portion, you are happy. But the illusion didn't quite work for me. Next, the color of the &lt;em&gt;beef rendang&lt;/em&gt; is just not right. Too light colored. A good &lt;em&gt;beef rendang&lt;/em&gt; is lightly blanketed with shredded, fried coconut flesh covered in reddish brown gravy. The two pieces of beef are too big. The beef should be cut into small cubes that can be easily fed into your mouth without a need for a steak knife. The scent has a little too much shrimp and ginger and lacks spice. As predicted the taste is just not right. I am disappointed. For a price so ridiculous for a portion so tiny, I expected the &lt;em&gt;beef rendang&lt;/em&gt; to taste, at the very least, right if not excellent.&lt;br /&gt;&lt;br /&gt;The wait for the &lt;em&gt;nasi lemak&lt;/em&gt; was not as long. Again same big plate with tiny portions. We weren't too surprised after the &lt;em&gt;beef rendang&lt;/em&gt;. I told the waitress to bring us a &lt;em&gt;kangkung belacan&lt;/em&gt;. She didn't understand what I said and asked me to point to the entrée on the menu, thus confirming my suspicion that she is not a Malaysian. The egg was missing from the &lt;em&gt;nasi lemak&lt;/em&gt;. At this point, I am convinced the restauranteur did not do enough research before s/he opened the restaurant or s/he forgot what a &lt;em&gt;nasi lemak&lt;/em&gt; should have as sides. Naturally, the curry chicken served with the &lt;em&gt;nasi lemak&lt;/em&gt; had more potatoes than meat. I think skimping on little things like that could cost the business in the long run. The &lt;em&gt;ikan bilis&lt;/em&gt; (deep fried anchovies) looked good. There were two thin slices of cucumber. Unbelievable. You had to skimp on cucumbers too? The &lt;em&gt;sambal&lt;/em&gt; color was just plain wrong. It was dirty yellow. This led me to think maybe the owner is not even Malaysian. Take a look at the photo above. The &lt;em&gt;sambal&lt;/em&gt; is the red stuff at the top. Yeah, unless you are color blind, in which case you shouldn't be driving, you should be able to tell something must be amiss with your &lt;em&gt;sambal&lt;/em&gt; recipe. The white rice was just that; plain white rice. It should have been cooked in coconut milk but I doubt the chef knew that. The curry chicken was rather bland and the gravy wasn't thick enough. Cooking a good Malaysian curry chicken is very difficult to master. It takes a lot of spices and a lot of patience. Good curry chicken usually takes hours to cook.&lt;br /&gt;&lt;br /&gt;&lt;em&gt;Special Treatment #2&lt;/em&gt;&lt;br /&gt;The lady whom we talked to earlier told us the chef was stingy. So she brought us an extra serving of the very wrong &lt;em&gt;sambal&lt;/em&gt;. We didn't touch the extra serving because the &lt;em&gt;sambal&lt;/em&gt; tasted like shrimp paste when there shouldn't be any shrimp paste. But we appreciated her gesture and said thank you.&lt;br /&gt;&lt;br /&gt;Thank goodness the k&lt;em&gt;angkung belacan&lt;/em&gt; was served before we finished the &lt;em&gt;nasi lemak&lt;/em&gt;. This was the only dish the chef got right. The gravy was thick but not too much so that the &lt;em&gt;kangkung&lt;/em&gt; didn't soak in it. The gravy tasted good with the stinky shrimp paste called &lt;em&gt;belacan&lt;/em&gt; and dried, red hot chilies. I would recommend the &lt;em&gt;kangkung belacan&lt;/em&gt; if you were to stop by at Satay. Just a word of caution: for some reason eating &lt;em&gt;kangkung&lt;/em&gt; causes drowsiness. So don't eat too much &lt;em&gt;kangkung&lt;/em&gt; and drive. Maybe now that Satay's in town Vegas ought to have a law against eating &lt;em&gt;kangkung&lt;/em&gt; and drive. But I'll leave it up to the creativity of the law enforcers to find a breathalizer for &lt;em&gt;kangkung.&lt;/em&gt; Perhaps the mere presence of the stinky &lt;em&gt;belacan&lt;/em&gt; is an indication of &lt;em&gt;Driving Under Kangkung Influence&lt;/em&gt; (DUKI).&lt;br /&gt;&lt;br /&gt;&lt;img src="http://sweety.jp/suzuko/mm26.jpg" align="right" /&gt;We ended our night of splurging on ridiculously expensive Malaysian food that we can get four times cheaper back home with a helping of the irresistable dessert, &lt;em&gt;Air Batu Campur&lt;/em&gt; (ABC). It means mixed ice. It's a bowl of peanuts, sweet corn, red beans, &lt;em&gt;cincau&lt;/em&gt; (black jelly) and sometimes green and red jelly coverd with shaved ice, red syrup and sweetened condensed milk. The Satay version of ABC was missing the sweetened condensed milk. But it's very hard to disappoint when you are serving an artery clogger. So if you just want to meet Malaysians in Vegas but not spend too much for too little food, go to Satay and order an ABC.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9239626-111786484833040078?l=kuantanzai.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuantanzai.blogspot.com/feeds/111786484833040078/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9239626&amp;postID=111786484833040078' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9239626/posts/default/111786484833040078'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9239626/posts/default/111786484833040078'/><link rel='alternate' type='text/html' href='http://kuantanzai.blogspot.com/2005/06/satay-malaysian-grille.html' title='Satay Malaysian Grille'/><author><name>kuantanzai</name><uri>http://www.blogger.com/profile/07836860726508898603</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9239626.post-111765517023770019</id><published>2005-06-01T18:05:00.000-07:00</published><updated>2005-06-01T17:06:20.600-07:00</updated><title type='text'>Speaking English to Jackie Chan</title><content type='html'>Working with a team of programmers who are non-native English speakers can be a real challenge. I was raised speaking Mandarin at home, went to a school that taught Malay as the first language, married a wife who speaks Indonesian and conversed casually with friends in Cantonese. However, I started learning English when I was 5 so I think I've got the basics nailed down.&lt;br /&gt;&lt;br /&gt;My fellow colleagues are excellent programmers, but not excellent English speakers. One guy is so smart it makes it hard sometimes to understand what he's trying to say. He likes to use metaphorical scenarios to help explain what he's trying to say. It usually ends up sounding like "Suppose I have an apple. You have an orange. If you give me orange... Apple costs $1.00. I won't give you apple. You pay me $0.50 then I give you apple... You tell me orange costs $3.00. I don't care. I want $0.50 then I give you apple..." As soon as he throws in a donut into the equation I begin to think &lt;em&gt;yummmmm &lt;/em&gt;and start drifting to Donut Land. It reminds me of a scene in Rush Hour 2 where Jackie Chan was getting off a plane and greeted by Chris Rock. Chris Rock was mouthing some words while Jackie Chan seemed to be ignoring him completely. Thinking Jackie Chan doesn't speak English, Chris shouted at Jackie Chan "Do you understand the words that are coming out of my mouth?" to which Jackie Chan replied "Yes, I do."&lt;br /&gt;&lt;br /&gt;Today I had first hand experience of what my colleague must have gone through trying to explain himself to me. I realized my colleague must have felt the same way Chris Rock did when he was talking to Jackie Chan. The programmers had a meeting with the inventory department to discuss our idea of streamlining the process of acquiring inventory. Because some of the information is classified and I signed one of those damn NDAs I will use metaphorical examples to give you a better idea of what we were trying to communicate to the inventory people.&lt;br /&gt;&lt;br /&gt;Let's begin with some background about how our company operates:&lt;br /&gt;&lt;br /&gt;Suppose our company, DVD Exchange, Inc., specialized in exhanging points for renting DVDs. Let's say John purchased a plan from Blockbuster where he gets 1,000 points every year. If he has unused points from previous year, they get rolled over to the current year. Now, John wanted to rent Star Wars Episode III. So he went to Blockbuster and found that all the Star Wars DVDs were rented out. Next day at the office, Christine told John about DVD Exchange and how he could exchange his points through DVD Exchange and get his Star Wars DVD from Hollywood Video instead. John got all excited and decided to call DVD Exchange.&lt;br /&gt;&lt;br /&gt;Currently DVD Exchange would handle John's call like this:&lt;br /&gt;&lt;br /&gt;DVD Exchange Rep: DVD Exchange. Hello, may I have your name please?&lt;br /&gt;John: Hi, I'm John.&lt;br /&gt;DVD Exchange Rep: Hi John. How may I help you?&lt;br /&gt;John: I would like to exchange my Blockbuster points for the Star Wars Episode III DVD.&lt;br /&gt;DVD Exchange Rep: OK, let me check the availability of that DVD. One moment please...&lt;br /&gt;John: (whistling, tapping his fingers on the receiver, pretending to be patient)&lt;br /&gt;DVD Exchange Rep: I apologize for the wait. Unfortunately, we don't have any Star Wars III DVD available at this moment.&lt;br /&gt;John: (A little frustrated) Are you sure? What about Hollywood Video or the other video stores?&lt;br /&gt;DVD Exchange Rep: I'm very sorry, sir. They don't show any availability.&lt;br /&gt;John: (Starting to lose his patience) So what do I do with all these damn points if I can't use them?&lt;br /&gt;DVD Exchange Rep: We could buy them from you, sir.&lt;br /&gt;John: You can do that? (Getting excited again)&lt;br /&gt;DVD Exchange Rep: Absolutely, sir. We will reimburse you for the points you are giving us.&lt;br /&gt;John: Awesome! Sign me up.&lt;br /&gt;DVD Exchange Rep: Sure. How many points do you have, sir?&lt;br /&gt;John: Umm... About 1,300 points.&lt;br /&gt;DVD Exchange Rep: Okay. Are any of these points rollover points?&lt;br /&gt;John: Yeah.&lt;br /&gt;DVD Exchange Rep: How many points do you get per year?&lt;br /&gt;John: 1,000.&lt;br /&gt;DVD Exchange Rep: How much do you have to pay per year?&lt;br /&gt;John: $200. Oh wait, I paid more last year though.&lt;br /&gt;DVD Exchange Rep: Okay. How much did you have to pay last year?&lt;br /&gt;John: Well, I don't remember exactly. But I think I have rollover points from the past three years. And I paid a different amount for each of those years.&lt;br /&gt;DVD Exchange Rep: I see, sir. I will need to have the points that were rolled over every year and how much you paid for them.&lt;br /&gt;John: I don't remember them exactly.&lt;br /&gt;DVD Exchange Rep: That's fine, sir. Can you give me some rough figures? You just need to send us the previous year receipts later.&lt;br /&gt;John: Okay...&lt;br /&gt;&lt;br /&gt;Once the DVD Exchange Rep collected all that information, the inventory department will evaluate John's points. The inventory department tries to find some DVDs that we could acquire with John's points and rent out again at a profit. If the inventory department finds something, then we will reimburse John for the points that we could use. For example:&lt;br /&gt;&lt;br /&gt;Suppose John has 1,359 points available. 1,000 are points from current year 2005. 221 points are from year 2004. 138 points from 2003. He paid $200 in 2005, $220 in 2004, $250 in 2003. Every year he gets 1,000 points.&lt;br /&gt;&lt;br /&gt;The inventory department found some DVDs we could acquire for a total of 987 points. We will only pay John for those 987 points that we are using. We always use the oldest rollover points first because rollover points will expire, say in 4 years. This way John will not lose his 2003 points next year.&lt;br /&gt;&lt;br /&gt;So, based on the following:&lt;br /&gt;&lt;br /&gt;138 points from 2003 at ($250 / 1000) per point = $34.50&lt;br /&gt;221 points from 2004 at ($220 / 1000) per point = $48.62&lt;br /&gt;628 points from 2005 at ($200 / 1000) per point = $125.60&lt;br /&gt;&lt;br /&gt;Assuming the information given by John is accurate, John is asking $208.72 for his 987 points. Now, we are not going to pay him $208.72 for DVDs that we deem only worth $180 lest we want to make a loss instead of a profit. Next, we tell John to send in his previous year receipts to prove that he paid as much as he claimed for those points. If everything checks out, we make an offer to pay him $180 for those 987 points. On the other hand, if from the receipts we found that John actually paid $150 in 2003 and $160 in 2004, we will offer to pay him only as much as what he paid for those points. This way we don't overpay him.&lt;br /&gt;&lt;br /&gt;Phew, that was a toughie.&lt;br /&gt;&lt;br /&gt;Now, we programmers like to think we are smarter than everyone else. And to make things worse programmers are lazy. We like to simplify things. Maybe we just can't get refactoring out of our heads. So we thought why do we need all that information about rollover points and how much they cost? Their accuracy is not reliable anyway. So we came up with this clever idea where we only ask for the total points available and the asking price for those points. We would still arrive at the same outcome and make the same offer to John. But since we are not asking for the rollover points breakdown we may be overpaying John for his points. However, if we asked for the previous year receipts we will not overpay John for his points.&lt;br /&gt;&lt;br /&gt;If you are still with me, thank you for your patience. I must be doing an okay job explaining myself then. Now, let's get to my tao of communication for programmers.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;People Are Frustrated Because You Are Speaking Klingon&lt;/strong&gt;&lt;br /&gt;Presenting your idea well is crucial in getting your points across. When you can't explain your ideas well people can't understand you. They get confused, frustrated and tired from arguing with you. And they start to get the idea that you are stupid. During a meeting one of the inventory department staff said "I thought the programmers are supposed to do what we told them to. Why do we want to confuse ourselves with what they think?" Ouch! Sure enough that meeting ended up a disaster. There was a lot of frustration and confusion. People were just tired and angry that we wasted their time getting no where.&lt;br /&gt;&lt;br /&gt;I made a point to present ourselves better and apologized for our lack of communication skills. When we got back from the meeting, I rounded up my team and cleaned up our acts. We wrote down our idea on a piece of paper and made sure every programmer are on the same page. I made a big deal out of choosing the correct words and terminology in presenting our idea so as not to confuse people. For instance, back to John's example:&lt;br /&gt;&lt;br /&gt;Total number of points&lt;br /&gt;Total available points&lt;br /&gt;Total points that John is giving us&lt;br /&gt;Total points that John is willing to give us&lt;br /&gt;&lt;br /&gt;could all mean different things to different people. In our subsequent meeting, I started out explaining exactly what I meant by my terms and made sure that everyone understood what I meant. In some situations, I played translator by using terms in the inventory department context when my colleague was using his terms. &lt;strong&gt;Choose your words wisely&lt;/strong&gt;. Do not use apple when you should use orange. Go the extra effort by explaining in detail what you meant to make sure everyone understand you the way you intended.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Speak Clearly&lt;/strong&gt;&lt;br /&gt;I get by with my English. I can't write a novel. I will probably get a B- if I were to take an English paper. But people can understand you even if you speak broken English. To prove my point, read the following sentences and you will understand exactly what I meant although it is grammatically incorrect:&lt;br /&gt;&lt;br /&gt;Homer Simpson fat stupid. Homer son Bart naughty. Lisa Simpson smart.&lt;br /&gt;&lt;br /&gt;You don't have to be a journalist to be able to communicate clearly. But you will need to make sure there is no ambiguity when you are speaking. Don't say "Marge tall blue hair" when you really mean "Marge's hair is blue and tall." and not "Marge is tall and has blue hair." If you have an accent that people find hard to understand speak at a slower pace.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Get Your Facts Straight&lt;/strong&gt;&lt;br /&gt;When citing examples, make sure the examples are in context, logical and accurate. If you started out with an example where an apple costs $1.00 and an orange costs $1.20 don't get all excited and say an apple costs $1.50 halfway through. Listen to yourself as you speak. If you realized you said something wrong, immediately stop and correct yourself. Otherwise, your listeners will think you make no sense while you wonder if they understand English.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Use Simple Metaphors&lt;/strong&gt;&lt;br /&gt;Metaphors are great ways to help your audience look at something from a different perspective. But don't go overboard and use complicated metaphors that people have no concept of. Don't use extracting hydrogen from water as a metaphor for ordering a meal at McDonald's. Sometimes, metaphors when used right can immediately get you an "Ahh. I see what you mean." from your audience.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Do Not Leave Out Important Information&lt;/strong&gt;&lt;br /&gt;I learned this one the hard way. Today we spent an hour trying to convince the inventory department that our method will get them the exact same result they will get with their current method. The one most important little piece of information that we forgot to mention was that in both method we will collect the receipts from the customer to verify their points and how much they paid for them.&lt;br /&gt;&lt;br /&gt;We started out drawing a T-comparison chart with their method on the left and ours on the right. We made up a scenario and walked through both methods to prove that they would both arrive at the same end result. We did arrive at the same result but the inventory department still think we are missing something. They were questioning the reason for not collecting the rollover points breakdown information upfront. I explained that it would be redundant and the information collected is not reliable anyway. They acknowledged that the initital breakdown information is inaccurate and therefore unreliable. So I had to prove that their method involves redundant steps. I labelled their first step of collecting the rollover points breakdown as #1 on the left. And I labelled our first step of collecting points without the breakdown as #1 on the right. Next I added a #2 step where they ask for the receipts from the customer on the left and added the same #2 step on the right under our method. Instantaneously, I got an "Ahh. I see what you mean." Almost at the same instant, I realized what a dumbass I was for leaving that out in the first place. Suffice it to say that today's meeting ended in our favor.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Do You Understand The Words That Are Coming Out of My Mouth?&lt;/strong&gt;&lt;br /&gt;Despite Chris Rock's disrespectful method of getting his point across to Jackie Chan, he was making a good effort. He was trying to get an acknowledgement from Jackie Chan and he got it. When presenting your ideas it is crucial to get an acknowledgement every step of the way so you don't let anyone detour and arrive at a completely different conclusion than what you intended. If you see blank faces repeat yourself with a more detailed explanation of what you just said. Then follow up with "Do you understand what I'm saying?" Make eye contact with everyone as you ask that question. If they don't understand, their body language will give you a hint even if they don't admit it.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;My learning experience has helped me communicate more effectively. If you are a programmer and have trouble communicating your ideas, it could hinder your career growth. Team leaders tend to be the most outspoken person in a team. If English is not your first language, speak more, read more and write more. You may not write a bestseller novel and make millions, but you'll be a good candidate for a promotion and raise.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9239626-111765517023770019?l=kuantanzai.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuantanzai.blogspot.com/feeds/111765517023770019/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9239626&amp;postID=111765517023770019' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9239626/posts/default/111765517023770019'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9239626/posts/default/111765517023770019'/><link rel='alternate' type='text/html' href='http://kuantanzai.blogspot.com/2005/06/speaking-english-to-jackie-chan.html' title='Speaking English to Jackie Chan'/><author><name>kuantanzai</name><uri>http://www.blogger.com/profile/07836860726508898603</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9239626.post-111653577650068266</id><published>2005-05-19T13:32:00.000-07:00</published><updated>2005-05-19T23:01:58.926-07:00</updated><title type='text'>The Perfect Compensation Package for Programmers</title><content type='html'>One of my readers (2 readers and counting) liked my previous article, &lt;a href="http://kuantanzai.blogspot.com/2005/05/business-of-managing-programmers.html" target="_blank"&gt;"The Business of Managing Programmers"&lt;/a&gt;. Ok, the reaction is really more like, "Okay, smarty pants, how about some solution?" So, I decided to try to create the perfect compensation package that will hopefully influence those employers who are willing to change to retain key programmers.&lt;br /&gt;&lt;br /&gt;Many of these ideas come from Joel's &lt;a href="http://www.joelonsoftware.com/articles/fog0000000040.html" target="_blank"&gt;"How do You Compensate Programmers?"&lt;/a&gt;. Some of them are from my experience as an employee and how I would want to be treated. I am not an employer so I am writing strictly from my informed opinion and experience as a lead developer managing a small team.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;The Two Perspectives of The Perfect Compensation Package&lt;/b&gt;&lt;br /&gt;Obviously, the employer and the employees never share the same view of what makes a good compensation package. The employer wants to minimize the compensation package, whereas the employee wants to maximize compensation package. Usually, one party ends up exhilarated and the other ends up "Yeah, okay." Ah, but therein lies the fallacy of employer and employee's misaligned interest with regards to compensation. See, most employers may think their goal is to hire the cheapest, best programmer. But "cheapest, best" are an oxymoron.&lt;br /&gt;&lt;br /&gt;Let's put on our shopper's hat for a minute. Unless you are Bill Gates, when you walk into your neighborhood PotteryBarn you get a sticker shock realizing &lt;a href="http://ww1.potterybarn.com/cat/pip.cfm?src=shpcfurhofdsk%7Crshop%2Fshpcfurhofdsk%7Crshop%2Fshpcfur%7Crshop&amp;pkey=cfurhofdsk&amp;amp;gids=p4602" target="_blank"&gt;a simple wooden desk&lt;/a&gt; costs $1,199. Head over to the Cheapo-Mart, you can get a just-as-functional desk for a mere $250. So you ask yourself, "Why in the world would I want to pay an extra $949 for PotteryBarn's desk when I can get a desk 10 times cheaper that will support my monitor just as well? "&lt;br /&gt;&lt;div class="sideBar"&gt;&lt;br /&gt;Now if you studied microeconomics, you would have learned about the &lt;a href="http://www.investopedia.com/university/economics/economics3.asp"&gt;demand curve&lt;/a&gt; which dictates that &lt;strong&gt;all else being equal&lt;/strong&gt;, demand goes down as price increases. What microeconomiccs don't teach you is that &lt;a href="http://www.joelonsoftware.com/articles/CamelsandRubberDuckies.html"&gt;prices also send signals&lt;/a&gt;. A Porsche is more reliable than a Hyundai. Starbucks latte tastes better than Wendy's coffee. Chevron regular unleaded gasoline is better than Apu's Kwik-Gas-Mart regular unleaded gasoline. You get the idea.&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;The naive shopper will go for the cheaper desk because he thinks they are the same thing. Three months later, he noticed the desktop starts to warp due to the weight of his 20" monitor. So he goes and buy another one. The desk warps again and this continues for one year. So he ended up paying $1,000 for a desk that's supposed to cost him only $250. Of course, this is an exaggerated example.&lt;br /&gt;&lt;br /&gt;However, when you shop for a programmer like a naive shopper you will end up exactly the same way. If you were lucky enough to hire a top programmer who took your pittance just because he needed money to get his landlord of his back, you may be able to keep him for a few months. Once he gets a better offer, he'll be gone faster than you can say "I'll give you a raise." All of a sudden your development slows to a crawl because what's left of your cheap programmers are too dumb to make any progress. May be some time later, you found another superstar willing to code for food. You repeat the same mistake of taking him in, paying him minimal wage and hope he feels so debted to you he never leaves. Surprise, surprise. He leaves again. And the cycle continues and you wonder why your company stops growing. Funny thing is companies who hire desperate programmers on lousy compensation packages never seem to learn.&lt;br /&gt;&lt;br /&gt;If you want to be the next Microsoft, do yourself a favor and adopt my Perfect Compensation Package for programmers. This will guarantee that you hire the best programmer you can afford. If after following my suggestion you still could not find a programmer, make me an offer.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;1. Salary is guaranteed to be at least the market rate.&lt;/strong&gt;&lt;br /&gt;Before you hire, do some research and find out what your competitors are paying their top talents. You will get a range of salaries. To err on the side of caution, pay the amount closest to the highest range that you can afford. If you realized you were underpaying, do not wait for review time to adjust the pay. Your employee will appreciate a nice surprise more than an expected raise. Do not wait until your programmer finds out that you are underpaying. Usually, when your programmer approaches you about dissatisfaction of pay, this means she already has a better offer and she's ready to leave.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;2. Fully paid health insurance for employee, spouse and family.&lt;/strong&gt;&lt;br /&gt;Do not skimp on this benefit. This is one of the most important factor that I would consider after salary. Companies who do not pay for the health insurance for spouses and families are scamming their employees. Why? Because the employee ends up with $10,000 less per year paying for his family's health insurance. Plus, you make it so easy for other employers to steal your superstar.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;3. Great office with great equipment.&lt;/strong&gt;&lt;br /&gt;Programmers sit at the desk longer than you sleep on a bed everyday. Make sure your programmers are comfortable. Give them high back executive chairs to sit in. Provide work areas with windows and privacy. No straight line of sight between two programmers. Buy good, fast computers with big LCD screens. Your programmers will be eternally thankful for this. If your office is better than your average programmers' home offices, where do you think they would rather be?&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;4. Entertainment in the office.&lt;/strong&gt;&lt;br /&gt;Programmers are not like your normal employees. Programmers are programmers because we love what we do and &lt;strong&gt;we actually enjoy what we do&lt;/strong&gt;. This is the one most important factor that drives us to &lt;strong&gt;work overtime voluntarily&lt;/strong&gt; sometimes. And what better way to encourage programmers to work overtime for free than entertainment. Get a wall mounted LCD screen TV complete with cable for your programmers' office. XBox or Playstation will provide us with jolts of excitement to keep us awake throughout those 24-hour work days.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;5. Work hour flexibility.&lt;/strong&gt;&lt;br /&gt;If you expect your programmers to work more hours, give us the flexibility to come in whenever we want during business hours. After putting in a 16-hour day the previous day, your programmers will be pissed if you expect us to come in at 8:30 am. Give your programmers keys to the office so we can come in during weekends when we have nothing better to do.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;6. Frequent evaluation.&lt;/strong&gt;&lt;br /&gt;Conduct evaluations at least twice a year. Notice I used the word evaluation, not review. &lt;a href="http://www.joelonsoftware.com/articles/fog0000000070.html" target="_blank"&gt;Performance reviews do more harm than good&lt;/a&gt;. Evaluation is meant for you to evaluate how you are doing at managing your programmers. Evaluations are like real estate appraisals. Every time you do evaluation, expect the value of your programmer to go up, just like your house. A programmer who has learned about your business is worth more than a programmer who is just coming on board. Prepare to offer a raise. Make use of the opportunity to get some feedback about what needs improvement. Remove all barriers that reduce productivity of your programmers and you end up with a lean, mean team of programmers. When you do offer a raise make sure you offer the same raise for the other programmers lest you want the other programmers leaving due to unfair compensation.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;7. Immgiration services and relocation expenses reimbursement.&lt;/strong&gt;&lt;br /&gt;Follow the golden rule: Hire the best programmer you can afford and you'll do fine. If the programmer happens to need immigration services, offer to pay for it in full. It costs you more at the beginning, but you are almost guaranteed the programmer's loyalty for the next 5 years. Likewise for out-of-state candidates. If paying for relocation expenses will get you loyalty for the next 3 years, it's more than worth it.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;8. Fun day.&lt;/strong&gt;&lt;br /&gt;Plan for some workday fun activities. That's right. That's what I said, workday fun. The keyword being &lt;strong&gt;workday&lt;/strong&gt;. Why workday? Am I crazy? On the contrary, if you plan for a fun day on a weekend or on holidays you are doing nothing to improve the work environment because we don't work on weekends or holidays! As a matter of fact, programmers will not participate in these weekend "fun day" activities because you are taking away their personal time. Be creative. Fun day can be a short trip to a movie theater for three hours, or a picnic at the park two blocks away, or two hours of bowling at the mall downtown. These are cheap ways to &lt;strong&gt;tell&lt;/strong&gt; your programmers to take a break and it goes a long way.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;9. Training and books.&lt;/strong&gt;&lt;br /&gt;Continuous learning is the only way a programmer can improve herself and advance in her career. You must allocate budgets for training and books for your programmers. You cannot tell your programmers you don't have a budget for training. Training doesn't always have to be expensive. Send your programmers to conferences or user group meetings. Organize training classes internally. Encourage programmers to pick a topic and do some research, then share with colleagues what they learned.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;10. The itsy-bitsy stuff.&lt;/strong&gt;&lt;br /&gt;Finally, take every advantage you could to improve the work environment by focusing on the itsy-bitsy stuff that cost you very little but mean a whole lot to the programmers. Weekly free lunches, paid for home Internet access, health club membership, free beverages, paid dry cleaning, occassional free tank of gas, etc. When you focus on these tiny details frequently, it tells your programmers that you care. Don't be afraid to go all out to help an employee. His loyalty and passion for his job will quickly repay you tenfold. Offer to help a programmer move to a new apartment. The more you surprise your programmers with out-of-kindness gestures, the more indebted the programmers will feel towards you. And the more productive and loyal the programmers will be.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;This is what I think the perfect compensation package for a programmer should be. I'm sure a lot of the great companies already have this in practice which is why they are so successful. Programmers are not hard to satisfy. We just crave some appreciation.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9239626-111653577650068266?l=kuantanzai.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuantanzai.blogspot.com/feeds/111653577650068266/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9239626&amp;postID=111653577650068266' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9239626/posts/default/111653577650068266'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9239626/posts/default/111653577650068266'/><link rel='alternate' type='text/html' href='http://kuantanzai.blogspot.com/2005/05/perfect-compensation-package-for.html' title='The Perfect Compensation Package for Programmers'/><author><name>kuantanzai</name><uri>http://www.blogger.com/profile/07836860726508898603</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9239626.post-111637633145956709</id><published>2005-05-17T16:52:00.000-07:00</published><updated>2005-05-17T19:36:03.433-07:00</updated><title type='text'>More Debate on DataSet vs DataReader</title><content type='html'>Scott Mitchell posted another article, &lt;a href="http://aspnet.4guysfromrolla.com/articles/051805-1.aspx" target="_blank"&gt;More On Why I Don't Use DataSets in My ASP.NET Applications&lt;/a&gt;, summarizing comments from people reading his first article, &lt;a href="http://aspnet.4guysfromrolla.com/articles/050405-1.aspx" target="_blank"&gt;Why I Don't Use DataSets in My ASP.NET Applications&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I agree with Scott about how it all boils down to what you are after: development-time efficiency or run-time efficiency. As I have pointed out earlier in my &lt;a href="http://kuantanzai.blogspot.com/2005/05/dataset-vs-datareader.html" target="_blank"&gt;comment&lt;/a&gt; about his first article, planning for run-time efficiency during development time may not be a good idea. I did not elaborate further about my opinion. Instead I just quoted Martin Fowler. This time, I will elaborate and hope you will see the point I am trying to make.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;DataReader with the Domain Model&lt;/b&gt;&lt;br /&gt;If you are going to use DataReader, and you don't want to leave the responsibilities of closing the DataReader in the hands of the caller, you will need to use the domain model. Scott Mitchell did an excellent job describing in brief how to do this in his first article:&lt;br /&gt;&lt;blockquote&gt;"You could, instead, create a class that has as its properties the database fields that you are storing in the DataSet. Then, when you wanted to cache the data, you could use a DataReader to read the query from the database and iterate through the records returned. For each record you'd create an instance of the custom class, set its properties to the field values of the query, and add the custom class to an ArrayList (or, preferably, a strongly-typed collection object). You'd then cache this collection of custom objects."&lt;/blockquote&gt;&lt;br /&gt;He added that this method proves to be more efficient and maintainable.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Do Not Misinterpret The Proof&lt;/b&gt;&lt;br /&gt;In my &lt;a href="http://kuantanzai.blogspot.com/2005/05/dataset-vs-datareader.html" target="_blank"&gt;previous comment&lt;/a&gt;, I admitted that I was convinced that a DataReader is faster than a DataSet. And I am still.&lt;br /&gt;&lt;br /&gt;But, after thinking about it, I think the &lt;a href="http://www.devx.com/vb2themax/Article/19887/1954?pf=true" target="_blank"&gt;test&lt;/a&gt; is a little misleading. Let's think about it from the perspective of how we are going to use the data using both methods.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;DataReader / Domain Model Method&lt;/i&gt;&lt;br /&gt;If you are using a DataReader, the best way is to use the domain model. So, in order to populate a DataGrid, you will need to create instances of a custom class that represents each row in the DataReader. For a more object oriented approach, you probably want to create another class to represent a strongly typed collection of those instances.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;DataSet Method&lt;/i&gt;&lt;br /&gt;If you are using a DataSet, it is not necessary to use the domain model to populate a DataGrid. All you need to do is just pass it back up the tiers to the user interface, and bind it to the DataGrid.&lt;br /&gt;&lt;br /&gt;Now, let's revisit the Speed Freak's test. What Craig Davis did was comparing how quickly a DataReader retrieves data to a DataSet. Granted, the test proves that retrieving data using a DataReader is faster than a DataSet. But it doesn't prove that using a DataReader with the domain model is more efficient than a DataSet! The test ignores the fact that when using a DataReader with the domain model, there is a cost in creating those instances of classes to represent each row in a DataReader. I'm not saying that a DataSet is more efficient than a DataReader. What I am saying is in circumstances where your class is doing some crazy processing whenever it gets populated, a DataSet may be more efficient.&lt;br /&gt;&lt;br /&gt;Suppose you have a Contact class with properties like FirstName, LastName, StreetAddress, UnitNumber, City, State, Country and so on. For some crazy bleeding-edge technology requirement, whenever the address is set, you need to call Google Maps service to verify the address and store the URL address to the satelite view. Now, tell me if a DataReader with the domain model is faster than a DataSet.&lt;br /&gt;&lt;br /&gt;Of course, quoting Joel Spolsky &lt;a href="http://www.joelonsoftware.com/articles/fog0000000036.html" target="_blank"&gt;"... with a contrived example, you can prove anything."&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Is It Really More Maintainable?&lt;/b&gt;&lt;br /&gt;Most queries return a result where each row represents an object. What if we have queries that return a result where each row represents multiple objects? How do you group these objects together? One way to do that is to create another class that contains these multiple objects.&lt;br /&gt;&lt;br /&gt;Imagine a row in a recordset that represents two objects: a Husband and a Wife. You would have in the recordset the FirstName and LastName of both Husband and Wife. Using a DataReader with the domain model, you would map the fields accordingly to two objects a Husband and a Wife. To group them together, let's create a Couple class that contains a Husband and a Wife. Easy.&lt;br /&gt;&lt;br /&gt;Now, what if we have a recordset that represents three objects: a Husband, a Wife and a Kid. Piece of cake. We create another Family class that contains a Husband, a Wife and a Kid.&lt;br /&gt;&lt;br /&gt;OK, let's have a recordet that represents four objects: a Husband, a Wife, a Kid and a Dog. Again, we create say, a Household class that contains a Husband, a Wife, a Kid and a Dog.&lt;br /&gt;&lt;br /&gt;But you can see my point, as we have more and more multi-object recordsets we may end up creating more and more classes for each multi-object recordset. Is this really more maintainable?&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Is Runtime Efficiency More Important to You Than Design Time Efficiency?&lt;/b&gt;&lt;br /&gt;Again, at the end of the day, it all boils down to what you are after:&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Design Time Efficiency&lt;/i&gt;&lt;br /&gt;If you want to use a DataReader with the domain model instead of DataSet, is the gain in runtime efficiency worth the design time lost in designing the classes?&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Runtime Efficiency&lt;/i&gt;&lt;br /&gt;Without running a profiler, how do you know, with the instantiation of all those objects, a DataReader will still come out faster than a DataSet?&lt;br /&gt;&lt;br /&gt;&lt;b&gt;My Take&lt;/b&gt;&lt;br /&gt;So, what's my stand on this argument? It depends. But then again, "it depends" can make anyone sound like they know what they are talking about.&lt;br /&gt;&lt;br /&gt;Choose the simplest route you know that achieves your end goal because the simplest route will take the least amount of time. If your end goal is to display top ten bestsellers, and you know using a DataSet is the easiest way you know, then go ahead use it. Once it works, refactor mercilessly. But never jump to conclusions about which method is faster than the other. Always run a profiler to help you pinpoint the bottleneck.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9239626-111637633145956709?l=kuantanzai.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuantanzai.blogspot.com/feeds/111637633145956709/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9239626&amp;postID=111637633145956709' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9239626/posts/default/111637633145956709'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9239626/posts/default/111637633145956709'/><link rel='alternate' type='text/html' href='http://kuantanzai.blogspot.com/2005/05/more-debate-on-dataset-vs-datareader.html' title='More Debate on DataSet vs DataReader'/><author><name>kuantanzai</name><uri>http://www.blogger.com/profile/07836860726508898603</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9239626.post-111634170178298415</id><published>2005-05-17T07:46:00.000-07:00</published><updated>2005-05-17T07:56:17.633-07:00</updated><title type='text'>Michael Jackson IS Guilty</title><content type='html'>&lt;blockquote&gt;&lt;a href="http://www.msnbc.msn.com/id/6308419/" target="_blank"&gt;Jackson said one of the reasons he sleeps with these young boys is because children haven't betrayed him. "Adults have let me down," he said. Welcome to the real world Michael Jackson.&lt;/a&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;blockquote&gt;However, the one thing Mesereau is facing that Cochran didn't have to is the fact that his client has admitted to half his crime, and that's sleeping with his alleged victim. See, that's tantamount to O.J. admitting to being on Bundy Drive with a butcher knife at about 10:15 p.m. on the night of the murders of his ex-wife and her friend.&lt;/blockquote&gt;&lt;br /&gt;Forget the trial. Do you still need a reasonable doubt when a man sleeps with young boys? Seriously, the first thing you'd think when somebody admits sleeping with children is "You are one sick f#ck!"&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9239626-111634170178298415?l=kuantanzai.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuantanzai.blogspot.com/feeds/111634170178298415/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9239626&amp;postID=111634170178298415' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9239626/posts/default/111634170178298415'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9239626/posts/default/111634170178298415'/><link rel='alternate' type='text/html' href='http://kuantanzai.blogspot.com/2005/05/michael-jackson-is-guilty.html' title='Michael Jackson IS Guilty'/><author><name>kuantanzai</name><uri>http://www.blogger.com/profile/07836860726508898603</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9239626.post-111611676778815703</id><published>2005-05-14T15:21:00.000-07:00</published><updated>2005-05-15T01:40:09.666-07:00</updated><title type='text'>The Business of Managing Programmers</title><content type='html'>A lot of software companies have not the slightest idea what business they are in. Ask a manager to describe what his company does and the following responses sound awfully familiar:&lt;br /&gt;&lt;blockquote&gt;"We are in the consulting business where we help businesses solve hard problems using technology tools we develop."&lt;br /&gt;&lt;br /&gt;"We are a software company that develops solutions for financial institutions."&lt;/blockquote&gt;&lt;br /&gt;Some tries to be a little more creative:&lt;br /&gt;&lt;blockquote&gt;"We are in the customer service business. Our main focus is to make our&lt;br /&gt;customers happy."&lt;/blockquote&gt;&lt;br /&gt;Others try to squeeze in as many big words as possible:&lt;br /&gt;&lt;blockquote&gt;"We provide creative solutions utilizing innovative and state-of-the-art&lt;br /&gt;technology to implement reliable and scalable architecture ..." (I don't even&lt;br /&gt;know what that means. But it does sound impressive.)&lt;/blockquote&gt;&lt;br /&gt;Most often, these are the companies that began with tremendous growth then slowly but surely withered to their death.&lt;br /&gt;&lt;br /&gt;The successful companies will quickly recognize that they are in the business of managing people - smart people. Why do the companies who don't realize this fail? Simple. They thought they were in a completely different kind of business. Consequently, talents who helped them grew exponentially in the beginning left them for the successful companies who recognized and worshipped their people. So how does a successful company manage its people? Let me drop a few hints:&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;1. I am Your Most Valuable Asset&lt;/strong&gt;&lt;br /&gt;Pop quiz: If you own a restaurant, what's your most valuable asset?&lt;br /&gt;Answer: Your chef.&lt;br /&gt;&lt;br /&gt;Here's another one: If you own a hair salon, what's your most valuable asset?&lt;br /&gt;Answer: Your hair stylists.&lt;br /&gt;&lt;br /&gt;Now, for the final million-dollar question: If you own a software company, what's your most valuable asset? Who makes your software? Can you function without them? Do you have a company without them? What happens if all of them decided to quit? Do you realize how much you rely on them? Catch my drift?&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;2. I Want This, and That, and This, and ...&lt;/strong&gt;&lt;br /&gt;Surprise. Despite what most people think, programmers don't work well in dark, tiny closets. Programmers socialize too. We may not hang out much with the sales guys, but that's just because we don't feel intellectually challenged discussing Britney Spear's new reality show. We need small but meaningful incentives. We enjoy playing games, trips to the movie theater, free gym memberships, weekly company lunches and free soda in the kitchen. And would it kill you to provide free gourmet coffee? We need fast computers with huge screens and comfortable executive chairs. We like a window view and a large desk. We want an office like &lt;a href="http://www.joelonsoftware.com/articles/BionicOffice.html" target="_blank"&gt;this&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;3. Listen to Me&lt;/strong&gt;&lt;br /&gt;Never let managers decide how things should be done. Programmers are smart enough to decide. Convince us why we should listen to you and ignore the fact that your idea of rewriting an existing software module will throw away years of hardwork. Just remember, when it comes to developing software we are smarter than you are.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;4. I Would Rather Code&lt;/strong&gt;&lt;br /&gt;A promotion to a Systems Manager sounds tempting but no thanks. We would rather code. You are probably better off eliminating the Systems Manager position and save yourself $100,000 a year so you can pay our raises. We can manage ourselves, thank you. Just tell us what you need and leave us alone.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;5. Do You Like Working Overtime?&lt;/strong&gt;&lt;br /&gt;Programmers are like you - we have lives outside of work. We want to be with our families. You don't own us like the machines you own in your factory. Plan your projects around us. We will decide the deadline given the number of features you are requesting or the number of features you will get given the deadline.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;6. Don't Hire Morons&lt;/strong&gt;&lt;br /&gt;Yes, unfortunately, there are smart and stupid programmers. Stupid programmers don't deserve to be programmers. They are more suited for &lt;a href="http://www.msnbc.msn.com/id/7680734/" target="_blank"&gt;scooping dog poop&lt;/a&gt;. The not-so-bright programmers slow us down and waste your money. If you are not sure whether a candidate is a moron, we will be happy to interview the candidate and let you know.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;7. Show Me The Money&lt;/strong&gt;&lt;br /&gt;Top programmers can always find a better job. If you are not willing to shell out at least the going market rate for a top quality programmer, we wish you good luck. Hope you get named in the &lt;a href="http://www.fuckedcompany.com/" target="_blank"&gt;fuckedcompany&lt;/a&gt; list next year. If you are smart, you would want to pay a little above market rate to keep your talents.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;8. Stop Wasting My Time&lt;/strong&gt;&lt;br /&gt;Don't call the programmers with a question like "Bob just sent me an email with his photo. But my outlook shows a big box with an X in the corner." Call your tech support. Unless you want to pay us $30/hour to do tech support start directing your calls to the right people. Hiring incompetent programmers have the same effect. Every time these faux programmers ask us why the code they just copied from somewhere couldn't compile they distract and frustrate us.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;9. Evaluation Is for You to Learn What You Have Done Wrong&lt;/strong&gt;&lt;br /&gt;If you are lucky enough to have hired top talents at your company, do all you can to retain them. Conduct evaluations at least twice a year. Don't treat evaluation as an opportunity to make the programmer prove that she is worthy of a raise. If you hired a smart programmer from the beginning, she would have earned the raise and may have made you more money than you could imagine. You should take the opportunity to learn what areas are you lacking effort in to keep your assets happy. Do you have a bugdet for training your staff? You would be surprised at how much you could learn simply by looking at evaluation with a different perspective.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;10. Be Nice&lt;/strong&gt;&lt;br /&gt;If you need to scream at your programmers, there could be only two reasons: you hired a dumbass or you are a dumbass. If you hired a superstar, you shouldn't ever need to shout. We respond better to positive reinforcement. The last thing you want to do is to wash your high pay and excellent benefits package down the drain by creating a hostile work environment simply because you missed a couple of anger management sessions.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Programmers are your assets, except that we have brains and feet. Unless you keep us happy, the moment a better opportunity shows up you can kiss our geek ass goodbye. At the end of the day, you would be left with all the retards and keep wondering why bugs never get fixed.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9239626-111611676778815703?l=kuantanzai.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuantanzai.blogspot.com/feeds/111611676778815703/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9239626&amp;postID=111611676778815703' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9239626/posts/default/111611676778815703'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9239626/posts/default/111611676778815703'/><link rel='alternate' type='text/html' href='http://kuantanzai.blogspot.com/2005/05/business-of-managing-programmers.html' title='The Business of Managing Programmers'/><author><name>kuantanzai</name><uri>http://www.blogger.com/profile/07836860726508898603</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9239626.post-111585091474889208</id><published>2005-05-11T15:09:00.000-07:00</published><updated>2005-05-11T15:35:14.766-07:00</updated><title type='text'>Read Joel</title><content type='html'>The past few weeks I have been obsessively reading Joel's blog. I cannot believe how much one could learn from his postings. My advice to programmers and entreprenuer-wanna-bes is to read them for the knowledge, for the fun, whatever, just read them. They'll do you more good than harm.&lt;br /&gt;&lt;br /&gt;I will be posting my comments on some of his articles here as I read them. These will serve as short notes for myself and anyone who cares. For those who would like a head start, jump to &lt;a href="http://www.joelonsoftware.com/navLinks/fog0000000247.html" target="_blank"&gt;Joel on Software Archive&lt;/a&gt; now.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9239626-111585091474889208?l=kuantanzai.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuantanzai.blogspot.com/feeds/111585091474889208/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9239626&amp;postID=111585091474889208' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9239626/posts/default/111585091474889208'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9239626/posts/default/111585091474889208'/><link rel='alternate' type='text/html' href='http://kuantanzai.blogspot.com/2005/05/read-joel.html' title='Read Joel'/><author><name>kuantanzai</name><uri>http://www.blogger.com/profile/07836860726508898603</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9239626.post-111531940572999532</id><published>2005-05-05T11:23:00.000-07:00</published><updated>2005-05-05T12:02:33.793-07:00</updated><title type='text'>Scott Bellware's Excellent C# Coding Style</title><content type='html'>Scott Bellware has put together an excellent &lt;a href="http://www.bellware.net/documents/CSharpCodeStyleGuide_v1.2.pdf" target="_blank"&gt;C# Code Style Guide&lt;/a&gt;. His recommendations are consistent with my coding style except for one.&lt;br /&gt;&lt;br /&gt;Under the section Returning Values I prefer:&lt;br /&gt;&lt;pre&gt;&lt;div class="snippet" style="height: auto;"&gt;&lt;br /&gt;if (condition)&lt;br /&gt;{&lt;br /&gt;    return x;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;return y;&lt;br /&gt;&lt;/div&gt;&lt;/pre&gt;&lt;br /&gt;over:&lt;br /&gt;&lt;pre&gt;&lt;div class="snippet" style="height: auto;"&gt;&lt;br /&gt;return (condition ? x : y);&lt;br /&gt;&lt;/div&gt;&lt;/pre&gt;&lt;br /&gt;Because return skips the execution of the remaining statements within a method (except for those statements in the finally block), using the former style allows you to, later on, add code easily before the second return statement.&lt;br /&gt;&lt;br /&gt;The ternary operator is best avoided whenever possible. There is one situation that I could think of where a ternary operator is more readable then the if-else block:&lt;br /&gt;&lt;pre&gt;&lt;div class="snippet" style="height: auto;"&gt;&lt;br /&gt;if (x &gt; 0)&lt;br /&gt;{&lt;br /&gt;    y = x;&lt;br /&gt;}&lt;br /&gt;else&lt;br /&gt;{&lt;br /&gt;    y = z;&lt;br /&gt;}&lt;br /&gt;&lt;/div&gt;&lt;/pre&gt;&lt;br /&gt;should be written as:&lt;br /&gt;&lt;pre&gt;&lt;div class="snippet" style="height: auto;"&gt;&lt;br /&gt;y = (condition &gt; 0 ? trueExpression : falseExpression);&lt;br /&gt;&lt;/div&gt;&lt;/pre&gt;&lt;br /&gt;But do NOT use this if trueExpression or falseExpression is an expression that contains more than an identifier, i.e.:&lt;br /&gt;&lt;pre&gt;&lt;div class="snippet" style="height: auto;"&gt;&lt;br /&gt;y = (x &gt; 0 ? x + 1 : z - 1);&lt;br /&gt;&lt;/div&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9239626-111531940572999532?l=kuantanzai.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuantanzai.blogspot.com/feeds/111531940572999532/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9239626&amp;postID=111531940572999532' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9239626/posts/default/111531940572999532'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9239626/posts/default/111531940572999532'/><link rel='alternate' type='text/html' href='http://kuantanzai.blogspot.com/2005/05/scott-bellwares-excellent-c-coding.html' title='Scott Bellware&apos;s Excellent C# Coding Style'/><author><name>kuantanzai</name><uri>http://www.blogger.com/profile/07836860726508898603</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9239626.post-111515949852495369</id><published>2005-05-03T13:40:00.000-07:00</published><updated>2005-05-03T15:31:38.526-07:00</updated><title type='text'>DataSet vs. DataReader</title><content type='html'>Scott Mitchell wrote a very interesting article recently &lt;a href="http://aspnet.4guysfromrolla.com/articles/050405-1.aspx" target="_blank"&gt;about him rarely using DataSets in ASP.NET applications&lt;/a&gt;. His article included &lt;a href="http://www.devx.com/vb2themax/Article/19887/1954?pf=true" target="_blank"&gt;proof&lt;/a&gt; supporting the superiority of DataReader performance wise. Most of the comments on Scott's blog supported his abstinence from DataSets in ASP.NET applications. &lt;a href="http://scottonwriting.com/sowblog/posts/3615.aspx#3631" target="_blank"&gt;Mike's comment&lt;/a&gt; seem to be the only one with valid points about why he would rather use DataSets although I do not think it is a strong argument.&lt;br /&gt;&lt;br /&gt;I am convinced that DataReader is faster than DataSet based on Craig "Speed Freak" Davis' test results. Nevertheless, many of us, myself included, tend to see things from only one perspective at any one time; that is the way it is presented to us. If one were to argue that a plane is faster than a car, we would agree and yet forget that a plane needs a landing strip to take off and land while a car doesn't.&lt;br /&gt;&lt;br /&gt;DataSet and DataReader are two different classes meant for different ways of handling data. One should not shun away from either completely under any circumstance. Both have their pros and cons. Use them &lt;a href="http://msdn.microsoft.com/msdnmag/issues/04/06/DataPoints/" target="_blank"&gt;appropriately&lt;/a&gt;. But, do not stop using DataSet just because the DataReader is faster. Because then we are guilty of premature preformance optimization. Two very important advice that Martin Fowler gave were:&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;Any fool can write code that a computer can understand. Good programmers write code that humans can understand. (Refactoring, 15)&lt;/b&gt;&lt;br /&gt;Using a DataReader does not exactly make code less readable than a DataSet. However, binding a DataReader to a control places the responsibility of closing the connection on the caller in some situations. &lt;a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnsqlmag03/html/The_NETConnectionPoolLifeguard.asp" target="_blank"&gt;DataGrid automatically closes the connection if CommandBehavior was set to CloseConnection&lt;/a&gt;. Bear in mind that when using a DataReader, the connection stays open while you are looping through the result.&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://www.artima.com/intv/tunable.html" target="_blank"&gt;If you ever do something for efficiency reasons and you aren't running the profiler, then basically you are speaking out of your rear end.&lt;/a&gt;&lt;br /&gt;If you think a DataReader is most suitable for a situation, do not assume that a DataSet would be better just because it is disconnected. Do some profiling after you got the code to work before optimizing. Likewise if you think a DataSet is more suited for the task at hand.&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9239626-111515949852495369?l=kuantanzai.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuantanzai.blogspot.com/feeds/111515949852495369/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9239626&amp;postID=111515949852495369' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9239626/posts/default/111515949852495369'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9239626/posts/default/111515949852495369'/><link rel='alternate' type='text/html' href='http://kuantanzai.blogspot.com/2005/05/dataset-vs-datareader.html' title='DataSet vs. DataReader'/><author><name>kuantanzai</name><uri>http://www.blogger.com/profile/07836860726508898603</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9239626.post-111513910702272032</id><published>2005-05-03T08:29:00.000-07:00</published><updated>2005-05-03T09:53:49.443-07:00</updated><title type='text'>Is Thor Still The God of RVs?</title><content type='html'>It's been a little over six months since I last recommended Thor Industries (NYSE: &lt;a href="http://finance.yahoo.com/q?s=THO" target="_blank"&gt;THO&lt;/a&gt;). Unfortunately, true to my "promise", the stock has plummeted more than 20% since my recommendation. Today I'll take a shot at guessing what went wrong and is Thor still good for my money.&lt;br /&gt;&lt;br /&gt;A lot has happened within the past six months for Thor. They &lt;a href="http://www.dailyitem.com/archive/2005/0416/local/stories/04local.htm" target="_blank"&gt;closed a plant&lt;/a&gt; in Middleburg, PA. Rising interest rates, &lt;a href="http://biz.yahoo.com/fool/050427/111463002220.html?.v=2" target="_blank"&gt;competitive pricing&lt;/a&gt; and &lt;a href="http://biz.yahoo.com/fool/050317/1111097400_1.html" target="_blank"&gt;oversaturated inventory&lt;/a&gt; have been linked to the overall decline of the RV industry.&lt;br /&gt;&lt;br /&gt;However, it's not all bad news for Thor. Early March, Thor &lt;a href="http://biz.yahoo.com/ap/050301/earns_thor_industries_1.html" target="_blank"&gt;announced second quarter earnings per share rose to 36 cents per share&lt;/a&gt;, missing Wall Street estimate of 41 cents per share. Nevertheless, the market reacted predictably - dumping shares - causing share price to drop 8 percent in a day. Since then, a string of bad news mentioned earlier convinced more shareholders to dump their shares. By end of April, the stock was trading at $26.87 per share.&lt;br /&gt;&lt;br /&gt;Hoping to boost investors' confidence, management recently released some good news. Thor still &lt;a href="http://biz.yahoo.com/prnews/050421/nyth126.html?.v=7" target="_blank"&gt;leads the RV industry&lt;/a&gt;, only now with a wider margin in market share compared to second competitor Winnebago (NYSE: &lt;a href="" target="_blank"&gt;WGO&lt;/a&gt;). Back in February, before the decline, Fortune ranked Thor as &lt;a href="http://biz.yahoo.com/prnews/050228/nym174_1.html" target="_blank"&gt;one of the most admired companies&lt;/a&gt;. Today, Thor announced &lt;a href="http://biz.yahoo.com/prnews/050503/nytu189.html?.v=7" target="_blank"&gt;preliminary sales for the third quarter were up 13%&lt;/a&gt;. Due to the significant drop in share price, management purchased 256,000 shares.&lt;br /&gt;&lt;br /&gt;So, is Thor still worth my money? Absolutely. Warren Buffet has always advised to buy a company with good management. I believe Thor's management is the best in the RV industry. Did I get in at a fairly high price? Absolutely. I should have anticipated the price drop considering the rising interest rates. Although rising interest rates did not actually affect Thor's sales, it did affect investors' confidence. And investors' confidence directly affects share pricing. In any case, Thor is a great company. The recent drop in share price presents a great buying opportunity.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9239626-111513910702272032?l=kuantanzai.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuantanzai.blogspot.com/feeds/111513910702272032/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9239626&amp;postID=111513910702272032' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9239626/posts/default/111513910702272032'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9239626/posts/default/111513910702272032'/><link rel='alternate' type='text/html' href='http://kuantanzai.blogspot.com/2005/05/is-thor-still-god-of-rvs.html' title='Is Thor Still The God of RVs?'/><author><name>kuantanzai</name><uri>http://www.blogger.com/profile/07836860726508898603</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9239626.post-111402353352271598</id><published>2005-04-20T11:13:00.000-07:00</published><updated>2005-04-20T11:58:53.526-07:00</updated><title type='text'>Select Query with Nolock Guideline</title><content type='html'>In my work, I noticed quite a few select queries with NOLOCK option specified. I am not a DBA so I did some research and consulted with my colleague about when to use or not to use the NOLOCK option. NOLOCK according to books online:&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Do not issue shared locks and do not honor exclusive locks. When this option is in effect, it is possible to read an uncommitted transaction or a set of pages that are rolled back in the middle of a read. Dirty reads are possible. Only applies to the SELECT statement.&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;Michael Balloni's &lt;a href="http://www.4guysfromrolla.com/webtech/101100-1.2.shtml" target="_blank"&gt;article&lt;/a&gt; wisely suggested that a NOLOCK hint can significantly improve performance when used &lt;b&gt;correctly&lt;/b&gt;. Nevertheless, a lot of "performance freaks" would jump in and start using NOLOCKs everywhere.&lt;br /&gt;&lt;br /&gt;When NOT to use NOLOCK:&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;The data you are retrieving will be re-written to the database.&lt;/b&gt; If you were reading dirty data into a form that would later persist the changes to the database you would run into a data concurrency issue.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;The accuracy of the data you are retrieving is crucial.&lt;/b&gt; If you were retrieving a list of financial transaction history, that would be considered crucial. Use your judgment when determining whether accuracy is crucial.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;One of the columns you are retrieving is part of a clustered index and can be modified.&lt;/b&gt; Using NOLOCK in the middle of a page split due to modification to a column in a clustered index will cause a &lt;a href="http://groups-beta.google.com/group/comp.databases.ms-sqlserver/browse_thread/thread/63ed0a9c27b0cf67/eee4d3e619cd3c50?q=nolock&amp;rnum=1&amp;hl=en#eee4d3e619cd3c50" target="_blank"&gt;"601 Could not continue scan with NOLOCK due to data movement"&lt;/a&gt; error. &lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;When to use NOLOCK:&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;The data you are retrieving is read-only or rarely changes.&lt;/b&gt; If you were retrieving a list of statuses to populate a dropdown, use NOLOCK and cache the list.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;The table you were trying to access is constantly being updated.&lt;/b&gt; If the table you were trying to retrieve records from has a table lock, use NOLOCK to bypass the lock.&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9239626-111402353352271598?l=kuantanzai.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuantanzai.blogspot.com/feeds/111402353352271598/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9239626&amp;postID=111402353352271598' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9239626/posts/default/111402353352271598'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9239626/posts/default/111402353352271598'/><link rel='alternate' type='text/html' href='http://kuantanzai.blogspot.com/2005/04/select-query-with-nolock-guideline.html' title='Select Query with Nolock Guideline'/><author><name>kuantanzai</name><uri>http://www.blogger.com/profile/07836860726508898603</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9239626.post-111376693006636373</id><published>2005-04-17T12:01:00.000-07:00</published><updated>2005-04-17T18:53:20.656-07:00</updated><title type='text'>RangeValidator for Currency Doesn't Work?</title><content type='html'>I've seen a lot of people posting questions about how to get the RangeValidator to work with currency. Most of the answers suggest working around the problem by using a RegularExpressionValidator or CustomValidator. But that involves quite a bit of work and I'm the lazy kind who likes to reuse whatever's available. Peter Blum, the  programmer who created the famous &lt;a href="http://www.peterblum.com/VAM/Home.aspx" target="_blank"&gt;Professional Validation and More (VAM)&lt;/a&gt;, suggested &lt;a href="http://groups-beta.google.com/group/microsoft.public.dotnet.framework.aspnet/browse_thread/thread/1cf55f8ff66be812/1dc9b44fa16dd5f0?q=rangevalidator+currency&amp;rnum=5#1dc9b44fa16dd5f0" target="_blank"&gt;setting the culture&lt;/a&gt; correctly for the current thread, page or application. I tried but couldn't get it to work. Now I'm left with two options: buy Peter Blum's excellent VAM or patch the RangeValidator myself. Because money's a little tight right now, I chose the second option.&lt;br /&gt;&lt;br /&gt;So I dug around the WebUIValidation.js script that .NET uses for client-side validation. Here's what I found out: RangeValidatorEvaluateIsValid calls ValidatorCompare which in turn calls ValidatorConvert. And in the ValidatorConvert function is where I find something interesting (an excerpt from the script):&lt;br /&gt;&lt;br /&gt;&lt;div class="snippet"&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    else if (dataType == "Currency") {&lt;br /&gt;        exp = new RegExp("^\\s*([-\\+])?(((\\d+)\\" + val.groupchar + ")*)(\\d+)"&lt;br /&gt;                        + ((val.digits &gt; 0) ? "(\\" + val.decimalchar + "(\\d{1," + val.digits + "}))?" : "")&lt;br /&gt;                        + "\\s*$");&lt;br /&gt;        m = op.match(exp);&lt;br /&gt;        if (m == null)&lt;br /&gt;            return null;&lt;br /&gt;        var intermed = m[2] + m[5] ;&lt;br /&gt;        cleanInput = m[1] + intermed.replace(new RegExp("(\\" + val.groupchar + ")", "g"), "") + ((val.digits &gt; 0) ? "." + m[7] : 0);&lt;br /&gt;        num = parseFloat(cleanInput);&lt;br /&gt;        return (isNaN(num) ? null : num);            &lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Notice the regular expression used in the line immediately below the data type  condition line. Now for those of you who are regular expression experts, you would be quick to realize that something's wrong. I am no expert, but from what I found out, \s is a character class for [ \f\n\r\t\v]. \d is a character class for digits. OK, so where's the currency symbol pattern?&lt;br /&gt;&lt;br /&gt;Without a match for the currency symbol in the pattern, expressions such as -$1,500 that is a perfectly legitimate currency format is considered invalid. So what's the remedy for this problem?&lt;br /&gt;&lt;br /&gt;I figured it'll be easiest to override the existing behavior of RangeValidator when it's dealing with currency. This will allow me to use the control exactly the same way as the built-in RangeValidator. The code below works pretty well for me. I have not tested against other currencies. I assume CompareValidator would have the same problem. I'll leave it up to the first person who comes across a need to do the same for CompareValidator.&lt;br /&gt;&lt;br /&gt;&lt;div class="snippet"&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;using System;&lt;br /&gt;using System.Web.UI;&lt;br /&gt;using System.Web.UI.WebControls;&lt;br /&gt;using System.Text.RegularExpressions;&lt;br /&gt;using System.Globalization;&lt;br /&gt;using System.ComponentModel;&lt;br /&gt;&lt;br /&gt;namespace Homer.WebControls.Validators&lt;br /&gt;{&lt;br /&gt; /// &lt;summary&gt;&lt;br /&gt; /// Summary description for RangeValidator.&lt;br /&gt; /// &lt;/summary&gt;&lt;br /&gt; [DefaultProperty("Text"),&lt;br /&gt; ToolboxData("&lt;{0}:RangeValidator runat=server&gt;&lt;/{0}:RangeValidator&gt;")]&lt;br /&gt; public class RangeValidator : System.Web.UI.WebControls.RangeValidator&lt;br /&gt; {&lt;br /&gt;  private const string EvaluationFunctionScriptKey = "RangeValidatorScript";&lt;br /&gt;  private const string EvaluationFunctionNamePrefix = "HomerRangeValidator";&lt;br /&gt;  private const string EvaluationFunctionName = "EvaluateIsValid";&lt;br /&gt;  private const string EvaluationFunctionScript = @"&lt;br /&gt;&amp;lt;script language=""javascript"" type=""text/javascript""&amp;gt;&lt;br /&gt;&amp;lt;!--&lt;br /&gt;&lt;br /&gt;function {0}{1}(val)&lt;br /&gt;{{&lt;br /&gt; var value = ValidatorGetValue(val.controltovalidate);&lt;br /&gt; if (ValidatorTrim(value).length == 0) &lt;br /&gt;  return true;&lt;br /&gt; return ({0}Compare(value, val.minimumvalue, ""GreaterThanEqual"", val) &amp;&amp;&lt;br /&gt;   {0}Compare(value, val.maximumvalue, ""LessThanEqual"", val));&lt;br /&gt;}}&lt;br /&gt;&lt;br /&gt;function {0}Compare(operand1, operand2, operator, val)&lt;br /&gt;{{&lt;br /&gt;    var dataType = val.type;&lt;br /&gt;    var op1, op2;&lt;br /&gt;    if ((op1 = {0}ParseCurrency(operand1, dataType, val)) == null)&lt;br /&gt;        return false;    &lt;br /&gt; if ((op2 = {0}ParseCurrency(operand2, dataType, val)) == null)&lt;br /&gt;  return true;&lt;br /&gt; switch (operator) &lt;br /&gt; {{&lt;br /&gt;  case ""NotEqual"":&lt;br /&gt;  return (op1 != op2);&lt;br /&gt;  case ""GreaterThan"":&lt;br /&gt;  return (op1 &gt; op2);&lt;br /&gt;  case ""GreaterThanEqual"":&lt;br /&gt;  return (op1 &gt;= op2);&lt;br /&gt;  case ""LessThan"":&lt;br /&gt;  return (op1 &lt; op2);&lt;br /&gt;  case ""LessThanEqual"":&lt;br /&gt;  return (op1 &lt;= op2);&lt;br /&gt;  default:&lt;br /&gt;  return (op1 == op2);            &lt;br /&gt; }}&lt;br /&gt;}}&lt;br /&gt;&lt;br /&gt;function {0}ParseCurrency(op, dataType, val)&lt;br /&gt;{{&lt;br /&gt;        exp = new RegExp(""^\\s*([-\\+])?\\"" + val.currencysymbol + ""*(((\\d+)\\"" + val.groupchar + "")*)(\\d+)""&lt;br /&gt;  + ((val.digits &gt; 0) ? ""(\\"" + val.decimalchar + ""(\\d{{1,"" + val.digits + ""}}))?"" : """")&lt;br /&gt;  + ""\\"" + val.currencysymbol + ""*\\s*$"");&lt;br /&gt;  m = op.match(exp);&lt;br /&gt;  if (m == null)&lt;br /&gt;  return null;&lt;br /&gt;  var intermed = m[2] + m[5];&lt;br /&gt;  cleanInput = m[1] + intermed.replace(new RegExp(""(\\"" + val.groupchar + "")"", ""g""), """") + ((val.digits &gt; 0) ? ""."" + m[7] : 0);&lt;br /&gt;  num = parseFloat(cleanInput);&lt;br /&gt;  return (isNaN(num) ? null : num); &lt;br /&gt;}}&lt;br /&gt;//--&amp;gt;&lt;br /&gt;&amp;lt;/script&amp;gt;&lt;br /&gt;  ";&lt;br /&gt;&lt;br /&gt;  public RangeValidator() : base()&lt;br /&gt;  {&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  protected override bool ControlPropertiesValid()&lt;br /&gt;  {&lt;br /&gt;   bool isValid = base.ControlPropertiesValid();&lt;br /&gt;   if ((!isValid) &amp;&amp; (this.Type == ValidationDataType.Currency))&lt;br /&gt;   {&lt;br /&gt;    try&lt;br /&gt;    {&lt;br /&gt;     decimal minCurrency = ParseCurrency(MinimumValue);&lt;br /&gt;     decimal maxCurrency = ParseCurrency(MaximumValue);&lt;br /&gt;     return true;&lt;br /&gt;    }&lt;br /&gt;    catch (FormatException)&lt;br /&gt;    {&lt;br /&gt;     return false;&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   return isValid;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;  protected override bool EvaluateIsValid()&lt;br /&gt;  {&lt;br /&gt;   bool isValid = base.EvaluateIsValid();&lt;br /&gt;   if (this.Type == ValidationDataType.Currency)&lt;br /&gt;   {&lt;br /&gt;    string controlValue = GetControlValidationValue(ControlToValidate);&lt;br /&gt;    try&lt;br /&gt;    {&lt;br /&gt;     decimal currency = ParseCurrency(controlValue);&lt;br /&gt;     decimal minCurrency = ParseCurrency(MinimumValue);&lt;br /&gt;     decimal maxCurrency = ParseCurrency(MaximumValue);&lt;br /&gt;     if ((currency &gt;= minCurrency) &amp;&amp; (currency &lt;= maxCurrency))&lt;br /&gt;     {&lt;br /&gt;      return true;&lt;br /&gt;     }&lt;br /&gt;    }&lt;br /&gt;    catch (FormatException)&lt;br /&gt;    {&lt;br /&gt;     return false;&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   return isValid;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  private decimal ParseCurrency(string controlValue)&lt;br /&gt;  {&lt;br /&gt;   NumberFormatInfo nfi = System.Threading.Thread.CurrentThread.CurrentCulture.NumberFormat;&lt;br /&gt;   Match m = Regex.Match(controlValue,&lt;br /&gt;    @"^\s*([-\+])?\"&lt;br /&gt;    + nfi.CurrencySymbol&lt;br /&gt;    + @"*(((\d+)\"&lt;br /&gt;    + nfi.CurrencyGroupSeparator&lt;br /&gt;    + @")*)(\d+)"&lt;br /&gt;    + (&lt;br /&gt;    (nfi.CurrencyDecimalDigits &gt; 0)&lt;br /&gt;    ?&lt;br /&gt;    @"(\" + nfi.CurrencyDecimalSeparator&lt;br /&gt;    + @"(\d{1," + nfi.CurrencyDecimalDigits&lt;br /&gt;    + @"}))?"&lt;br /&gt;    :&lt;br /&gt;    ""&lt;br /&gt;    )&lt;br /&gt;    + @"\"&lt;br /&gt;    + nfi.CurrencySymbol&lt;br /&gt;    + @"*\s*$");&lt;br /&gt;&lt;br /&gt;   if (!m.Success)&lt;br /&gt;   {&lt;br /&gt;    throw new FormatException("Invalid currency format.");&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   string intermediate = m.Groups[2].Value + m.Groups[5].Value;&lt;br /&gt;   string cleanInput =&lt;br /&gt;    m.Groups[1].Value&lt;br /&gt;    + Regex.Replace(intermediate, @"(\" + nfi.CurrencyGroupSeparator + @")", "")&lt;br /&gt;    + ((nfi.CurrencyDecimalDigits &gt; 0) ? "." + m.Groups[7].Value : "0");&lt;br /&gt;   &lt;br /&gt;   try&lt;br /&gt;   {&lt;br /&gt;    return Decimal.Parse(cleanInput);&lt;br /&gt;   }&lt;br /&gt;   catch (FormatException)&lt;br /&gt;   {&lt;br /&gt;    throw new FormatException("Invalid currency format.");&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  protected override void OnPreRender(EventArgs e)&lt;br /&gt;  {&lt;br /&gt;   base.OnPreRender(e);&lt;br /&gt;   RegisterValidatorClientScript();&lt;br /&gt;   Attributes.Add("currencysymbol", System.Threading.Thread.CurrentThread.CurrentCulture.NumberFormat.CurrencySymbol);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  protected virtual void RegisterValidatorClientScript()&lt;br /&gt;  {&lt;br /&gt;   Attributes["evaluationfunction"] = EvaluationFunctionNamePrefix + EvaluationFunctionName;&lt;br /&gt;   if (!Page.IsClientScriptBlockRegistered(EvaluationFunctionScriptKey))&lt;br /&gt;   {&lt;br /&gt;    string s = String.Format(EvaluationFunctionScript, EvaluationFunctionNamePrefix, EvaluationFunctionName);&lt;br /&gt;    Page.RegisterClientScriptBlock(EvaluationFunctionScriptKey, s);&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9239626-111376693006636373?l=kuantanzai.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuantanzai.blogspot.com/feeds/111376693006636373/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9239626&amp;postID=111376693006636373' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9239626/posts/default/111376693006636373'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9239626/posts/default/111376693006636373'/><link rel='alternate' type='text/html' href='http://kuantanzai.blogspot.com/2005/04/rangevalidator-for-currency-doesnt.html' title='RangeValidator for Currency Doesn&apos;t Work?'/><author><name>kuantanzai</name><uri>http://www.blogger.com/profile/07836860726508898603</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9239626.post-111229650276752750</id><published>2005-03-31T10:54:00.000-08:00</published><updated>2005-04-02T09:22:36.063-08:00</updated><title type='text'>The Misunderstood FormsAuthentication RedirectFromLoginPage</title><content type='html'>There are a lot of misconceptions about the function of FormsAuthentication.RedirectFromLoginPage function. People have been &lt;a href="http://www.derkeiler.com/Newsgroups/microsoft.public.dotnet.framework.aspnet.security/2004-02/0233.html" target="_blank"&gt;saying&lt;/a&gt; it's buggy. I hate to break the news to those who claimed so but the only thing wrong with FormsAuthentication.RedirectFromLoginPage is how one is using it.&lt;br /&gt;&lt;br /&gt;If you read the remarks for this method on MSDN library, you'll realize that it's working exactly as it should. To quote the docs:&lt;br /&gt;&lt;br /&gt;"The method issues an authentication ticket and does a SetForms with the ticket, using the appropriately configured cookie name for the application as part of the redirect response."&lt;br /&gt;&lt;br /&gt;Lesson learned:&lt;br /&gt;If you are creating your own FormsAuthenticationTicket, use FormsAuthentication.GetRedirectUrl. Using FormsAuthentication.RedirectFromLoginPage will actually create a new authentication ticket overriding the ticket you just created assuming you are using the FormsAuthentication.FormsCookieName for your cookie.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9239626-111229650276752750?l=kuantanzai.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuantanzai.blogspot.com/feeds/111229650276752750/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9239626&amp;postID=111229650276752750' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9239626/posts/default/111229650276752750'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9239626/posts/default/111229650276752750'/><link rel='alternate' type='text/html' href='http://kuantanzai.blogspot.com/2005/03/misunderstood-formsauthentication.html' title='The Misunderstood FormsAuthentication RedirectFromLoginPage'/><author><name>kuantanzai</name><uri>http://www.blogger.com/profile/07836860726508898603</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9239626.post-110940080102128075</id><published>2005-02-25T22:13:00.000-08:00</published><updated>2005-02-26T02:33:04.710-08:00</updated><title type='text'>H &amp; R - The Other Government</title><content type='html'>It has been a while since I last posted a message. I couldn't find a good buy and mostly I was too lazy. Today I finally found the energy to recommend a new stock I added to my portfolio.&lt;br /&gt;&lt;br /&gt;What is the first name that comes to mind when looking for help in filing your taxes? For most people, it is the company with the green square logo whose promise is come in and walk away with a check for your refund. Yeah, it almost made tax season fun and welcomed. Not for me, I had to send a check to IRS this year.&lt;br /&gt;&lt;br /&gt;H &amp; R (NYSE: &lt;a href="http://finance.yahoo.com/q?s=hrb" target="_blank"&gt;HRB&lt;/a&gt;)'s closest competitor Intuit (NASDAQ: &lt;a href="http://finance.yahoo.com/q?s=INTU" target="_blank"&gt;INTU&lt;/a&gt;) seems a little too richly valued. Jackson Hewitt (NYSE: &lt;a href="http://finance.yahoo.com/q?s=JTX" target="_blank"&gt;JTX&lt;/a&gt;) on the other hand, is much smaller in size despite beating earning estimates. And they have only been listed for 1 year.&lt;br /&gt;&lt;br /&gt;Interestingly, insiders at H &amp; R have been consistently accumulating shares at market prices for the past six months. With earnings growing at an average of 28.7% for the past 5 years, it is not surprising that H &amp; R has attracted some of the best funds and investors including Berkshire Hathaway and Oakmark Funds. EPS for the last fiscal year was $3.20 making it a bargain trading at $47.35 when I bought in.&lt;br /&gt;&lt;br /&gt;EV/FCF is 25.4 compared to Intuit's 17.0. However, H &amp; R has the biggest market share and the most recognized brand name in this industry. I am confident it will maintain its competitive edge for years to come. Growth in free cash flow has been consistent annually and they are aggressively paying down debt.&lt;br /&gt;&lt;br /&gt;Surprisingly, although third quarter results was not too impressive, the stock jumped about 9% today to $51. An increase of 11% in early tax season filing may have played a part in the jump.&lt;br /&gt;&lt;br /&gt;If earnings continue to grow steadily, I aim for a target price of $64 by middle of 2006. If you are getting a big refund this year, first adjust your W-2 so you withhold less taxes and then plough some dough into H &amp; R. In any case, file your taxes with H &amp; R.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9239626-110940080102128075?l=kuantanzai.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuantanzai.blogspot.com/feeds/110940080102128075/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9239626&amp;postID=110940080102128075' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9239626/posts/default/110940080102128075'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9239626/posts/default/110940080102128075'/><link rel='alternate' type='text/html' href='http://kuantanzai.blogspot.com/2005/02/h-r-other-government.html' title='H &amp; R - The Other Government'/><author><name>kuantanzai</name><uri>http://www.blogger.com/profile/07836860726508898603</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9239626.post-110089769754466432</id><published>2004-11-19T11:12:00.000-08:00</published><updated>2004-11-22T16:10:06.643-08:00</updated><title type='text'>Thor the Almighty God of RVs</title><content type='html'>Thor Industries (NYSE: &lt;a href="http://finance.yahoo.com/q?s=THO" target="_blank"&gt;THO&lt;/a&gt;) is the largest manufacturer of mobile homes (RVs) and small and mid-sized buses. In recent years, the company has experienced explosive growth of 24.59% annually for the past five years. With 71% of revenue coming from the sales of RVs, Thor leads the industry with 25% market share. Enterprise Value of $1.72B and Free Cash Flow of $56.28M gave Thor a pretty scary EV/FCF of 30.56 (EV/FCF/G = 1.24). Thor's closest competitor Winnebago (NYSE: &lt;a href="http://finance.yahoo.com/q?s=WGO" target="_blank"&gt;WGO&lt;/a&gt;) appears to be twice as cheap with an EV/FCF of 15.74 (EV/FCF/G = 0.79). However, a closer look reveals that in May 2004 Winnebago paid $5.3M to settle a lawsuit for Deferred Compensation Plan and it will take another hit of $2M for another 22 plan participants. This could just be a small bump on the road for Winnebago. But with competitors like Thor around, you can't afford to have that kind of luxury.&lt;br /&gt;&lt;br /&gt;Thor recently acquired CrossRoads for $27M in cash which is projected to increase EPS by 5 cents in 2005. Last year, Thor acquired Damon Corp for $46M in cash. Obviously Thor has plenty of greenbacks lying around. And if they are not buying companies, they are buying back shares - &lt;a href="http://biz.yahoo.com/prnews/041101/nym173_1.html" target="_blank"&gt;2 million of them&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Thor is debt free. However, they can definitely work on improving the net profit margin of a meager 4.8%. Industry average is 7.4% which is close to what Winnebago has. Thor's bus business, unfortunately, does not enjoy the growth of its peer. Bus sales has dropped 12% offsetting the sales of RVs. It'll be interesting to see how management will be handling this issue. Keep in mind Thor has government funding to build hybrid buses. This could turn the bus business around and spurt the growth of the company even further.&lt;br /&gt;&lt;br /&gt;Insiders hold 39% of shares while 52% of the shares are held by insitutions, which is good, not getting too much attention. But, shares are under heavy accumulation by financial institutions. So Thor may not stay in the dark for long.&lt;br /&gt;&lt;br /&gt;I am shooting for a target price of $43 by year end of 2005. At time of writing, I estimate its intrinsic value at $40.97 versus price of $32.88.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9239626-110089769754466432?l=kuantanzai.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://kuantanzai.blogspot.com/feeds/110089769754466432/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9239626&amp;postID=110089769754466432' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9239626/posts/default/110089769754466432'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9239626/posts/default/110089769754466432'/><link rel='alternate' type='text/html' href='http://kuantanzai.blogspot.com/2004/11/thor-almighty-god-of-rvs.html' title='Thor the Almighty God of RVs'/><author><name>kuantanzai</name><uri>http://www.blogger.com/profile/07836860726508898603</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9239626.post-110089072244494768</id><published>2004-11-19T10:52:00.000-08:00</published><updated>2005-02-07T10:17:08.766-08:00</updated><title type='text'>A New Career</title><content type='html'>Today I will take a baby step towards my new career as a stock picker. I am 100% confident that this career will not make me any money and 0% sure that it will make any one who follows my pick any money either. As always invest at your own risk. Investment is not FDIC insured and all the legal blah blah you can read at any financial site. And thus begins my career ...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9239626-110089072244494768?l=kuantanzai.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9239626/posts/default/110089072244494768'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9239626/posts/default/110089072244494768'/><link rel='alternate' type='text/html' href='http://kuantanzai.blogspot.com/2004/11/new-career.html' title='A New Career'/><author><name>kuantanzai</name><uri>http://www.blogger.com/profile/07836860726508898603</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry></feed>
