Saturday, December 25, 2010

Off the Grid - Food, Diving, Love and Cholera in Paradise

Bula! We recently took a trip to the Fiji Islands, and I took this opportunity to go completely off the grid - no cellphone, no computer, no TV, no newspapers. I was afraid I wouldn't last the week and succumb to withdrawal symptoms, but I decided I would give it my best shot. Not happy with just being off the grid, I decided to bring a book with me. If I read it, it would be the first book I would read in almost 4 years. I had heard great things about Love in the Time of Cholera and it was a genre of books I hadn't ever attempted to read before, so I figured it would be ideal. As luck would have it, the King Country public library in Bellevue (a most excellent library, by the way) had the book available to check out.

Well, turns out that the world does continue to turn even if I don't read the news, write some code, catch up on blogs and scan Facebook.

It was a great week full of excellent scuba diving, phenomenal tropical foods and magnificent living in resorts bathed in luxury and relaxation. We spent a few days at Outrigger on the Lagoon near Sigatoka on the main island, Viti Levu. It was larger than I would've preferred, but our plan here was to primarily relax and explore some of the surrounding villages/towns and interact with local Fijians and experience Fijian culture. We did 2 dives at Beqa Lagoon, but saved the majority of our diving for the latter half of our trip, which was near the more renowned dive sites. Instead, we spent a day relaxing at the resort and enjoying a local bar and another exploring some not-so-touristed surrounding. We got invited to dinner at a local family's house (our taxi driver, who happened to be a 4th generation Fijian Indian), and it was a humbling experience. Our hosts were a warm-hearted family who lived in a modest home in a tiny village who served us lovingly prepared Fijian dishes. The fish lovo, made with a snapper that was caught the previous night, and vegetable curry made with Taro root and star fruit, were delicious.

At the end of this part of the trip, I was done reading about a third of my book, and I hadn't felt the urge to get wired in at all, although internet access was readily available. I read by the pool, on the beach and in the lounge - the book was more interesting than I had imagined, and written in a style that was very refreshing.

Our next stop was the Namale Resort & Spa, home to the Sexiest Villas in the South Pacific. I knew the resort was good and I had lofty expectations for the price I had paid, but it exceeded my every expectation. From the warm welcome to the teary send-off, I could not have imagined a better way celebrate love. We spent the mornings diving, after having devoured a scrumptious breakfast. We were lucky enough to dive Chimneys and Grand Central at the Namena Barrier Reef on our last day of diving. I cannot imagine a better dive site than Chimneys anywhere in the world. It was magical - I think I could dive Chimneys every day for a year and still be amazed after every dive. After diving, we would partake of the phenomenal 3-course lunch served at a location of our choice (beach, deck, bure... anywhere we felt like eating) and spend the afternoon lazing on the beach, in the spa or indulging in more water activites (snorkel, water ski, etc). After that, some more R&R and drinks to whet the appetite for the gastronomical extravaganza that was dinner, accompanied by island cocktails or wine pairings and the Kava Boys. Every day, we were made to feel special with several personal touches by the staff at the resort - they really treated us like family. If there is paradise, Namale was pretty darn close to what I'd like it to be.

My reading progressed surprisingly well, and I found myself carrying my book to the beach, to the spa and to the outdoor jacuzzi. Save for about a 100 pages in the middle, I was enthralled by the story and the writing. I completed the book at the airport lounge prior to departure, and it had lived up to it's hype. It was Gabriel Marquez' ode to the myriad forms of love - spurned love, unrequited love, illicit love, love at first sight, true love, love that grows and fades over time and love that cannot really be classified. I got lucky with this book - I couldn't have picked a more appropriate book for the occasion. The fact it was a literary masterpiece didn't hurt either.

Anyways, all good things end, and so did our week in paradise. I hope one day to return to this lap of luxury in the midst of the friendliest people in the world. Until then, it's back to regularly scheduled programming.

Tuesday, September 28, 2010

Fun facts about Pilots

On a recent flight, I sat alongside a pilot who was returning home to the Seattle area. A lot of interesting things came up in my chat with the pilot, stuff I didn't really know about:
  • Pilots fly free on all domestic routes, regardless of airlines. All airlines have an agreement to lets each others' pilots occupy empty seats.
  • If no seats are available, the travelling pilot can also occupy an extra seat in the cockpit that is usually empty.
  • Pilots retire at 65(!).
  • The pilot I spoke with had been flying for 30 years. His pay/benefits today are half what they were 20 years ago (adjusted for inflation).
  • Until ~10 years ago, most commercial pilots came from the military (air force/navy). Now, it's about 50% military and 50% from pilot schools.
  • For pilots, career all about seniority. If this guy (a captain currently) were to leave his airline and join another airline he would start as a flight engineer. That's one level below first officer, who is below captain. This is a result of unionization.
  • Flights longer than 8 hours require 3 pilots (1 captain and 2 first officers) to rotate flying duties. Flights longer than 12 hours require 4 pilots (1 captain and 3 first officers). They usually fly 3-4 hour shifts.
  • Pilots at traditional airlines (American, Continental) make more than pilots at startup airlines (Virgin America, Jet Blue).
  • Airlines like Horizon Air/American Eagle/etc serve as training grounds for pilots, who then move to the more lucrative airlines.
  • On average, pilots fly between 9 and 14 days a month.
  • Pilots train on simulators every 9 months, and typically work on hard scenarios like dual engine failures once every couple of years.
  • Boeing and MD planes have "steering wheel" type controls. Airbus has fly-by-wire joystick like controls.
  • A 737 needs about 6000 feet to land comfortably. Headwind makes a huge difference in amount of runway needed.
  • Airbus planes felt more "robust" to this pilot.
  • Bird hits are more common than passengers realize.
  • Tegucigalpa, Honduras, is the hardest airport that he's landed in. Overshooting the 5000 foot runway drops you off a cliff and on to a strip mall. There have been 2 crashes in the last few years, with one dead pilot.

Thursday, September 9, 2010

Google Instant's biggest win? Teaching users to search better

The biggest impact of Google Instant is that it teaches users how to search more effectively. Based on these dynamic results, users can tweak their query such that it helps them find the exact answer they were looking for, even if they started with a bad query. Teaching users how to improve their search query is huge. The immediate impact is that it improves users' perception of Google's search results. Same with speed - users will perceive Google as being much faster than anything else out there because of the instant results.

Perception is reality. Speed and relevance are a search engines bread & butter. I think Google just upped the ante by a lot.

Credit to Microsoft though - this probably wouldn't have happened if it weren't for Bing's recent gains. Proof that competition provides the best kick-in-the-pants needed to innovate.

Friday, September 3, 2010

Integrating Core Data into your Three20 App

The Three20 library for iPhone app development is pretty cool - it take care of a lot of the 'mundane' code that you might need to write to accomplish basic tasks. Apple really should include some of the functionality of Three20 in the iPhone SDK itself, specially the Network stack.

As I built a more advanced app using Three20, I realized that it would be great to be able to persist to a database to make loading quicker when state on the server is unchanged. I needed some pretty basic Core Data integration in my app, and it was a little annoying that Three20 didn't have a Core Data-backed implementation of TTModel built into it. It seems like a lot of people want this, but there isn't any good post about it I could find. Jeff Verkoey, who currently maintains Three20, started writing a Core Data based tutorial but didn't get around to actually doing much with it. And there are a few threads in the Three20 Google group but nothing too comprehensive.

Having built this for my app, I figured I would post some more concrete information here for others who might find it helpful. I really want to just contribute a clean cut of this code as a TTCoreDataModel with unit tests to the Three20 source, but I don't have the luxury to spend time on it currently.

So here's a quick step-by-step on how to modify your existing Three20 based app to add basic Core Data support. The specific example I use fetches data from a server using TTURLRequestModel and writes it out to SQLite using Core Data entities. When displaying data to the user, it fetches data from Core Data using a NSFetchedResultsController, which efficiently interacts with Core Data.

Step 1:
Add CoreData.framework to your list of dependencies. Then, add the Core Data stack. Usually, you would do this in the AppDelegate. Jeff's AppDelegate files have this implemented, if you want an example. In my case, I wanted different databases for different Models, so I had these in my TTModel implementations.

Step 2:
Create your model. Add a new file of type "Data Model" in XCode (available in the 'Resource' section). Make sure the .xcdatamodel file gets added to your Target so that it's compiled into your binary. Look here for information on using the modeling tool. Once you've created your model, import the code for the Entity objects into your project. Now you're ready to start using Core Data.

Step 3:
Update your TTURLRequestModel implementation. Let's assume you're simply downloading some JSON and want to write it out to Core Data as an Entity you created in Step 2. Update your (void)requestDidFinishLoad:(TTURLRequest*)request implementation of TTURLRequestDelegate as follows:




A few notes about the code above:
  • DBResource is my simple entity containing an "id" and a "name".
  • The NSAutoreleasePool helps clean up memory allocated in this tight loop here
  • -reseting the context makes sure that the data faults in as needed instead of staying in memory when you read it using the NSFetchedResultsController
Step 4:
In your TTTableViewDataSource implementation, create a NSFetchedResultsController and configure it to fetch your Entities as needed. Then, override the (id)tableView:(UITableView*)tableView objectForRowAtIndexPath:(NSIndexPath*)indexPath method to return a TTTableItem built using the Entity you fetched from Core Data




Now, your TTTableViewDataSource is configured to read results from the DB you created and display them in your view. Remember that tableViewDidLoadModel can be called multiple times, so clean up any previously allocated NSFetchedResultsController if you need to.

Step 5:
Implement your logic to delete/update your Core Data entities. Depending on how your app uses data persistence, you want to ensure that you update/delete your entities from Core Data. For instance, if you always want to fetch data from the server and load them into Core Data on first run, then update Step 3 to first delete all entities. If you wish to show local data when available but fetch when not, then update Step 3 to not hit the server before checking locally. When deleting entities, remember that Core Data only lets you delete a single entity at a time (so you must fetch all and loop through each to delete it). If you want DROP TABLE ... semantics you're going to need to maintain different NSManagedObjectContent and NSPersistentStoreCoordinator for each such table, and then simply delete the database file.

So, this isn't too hard really. Assuming you're familiar enough with building your Three20 app, my notes above should help you get started with integrating Core Data into your app. My app uses a TTSectionedDataSource, and with a few tweaks to NSFetchedResultsController it's simple to do that too.

Saturday, August 7, 2010

An Apple A Day Keeps the Bugs Away

I've been working from home for the last year or so, and it has tremendously increased my productivity as a developer. Besides the obvious reasons (lack of distractions, no meetings, etc) there is one other advantage that I haven't often heard people mention before.

I have been able to find solutions to some really hard technical problems much quicker, and I attribute it to the fact that I work from home instead of in a more office-like environment.

Most software engineers will tell you that some of their best ideas have come to them when doing the most mundane of things - taking a shower, strolling across lawns, eating a snack and so on. I don't know the psychology behind this, but I know it's true for me. Something about being alone and thinking about your problem subconsciously, and yet being physically distracted with an unrelated task seems to work wonders. I have solved more tough problems while in the shower, when sitting down to eat an apple, when watering the plants or simply doing the dishes than I have sitting at a desk and just thinking. So obviously, being at home makes it much easier for me to find one of these mundane tasks that let me 'detach' and yet perform an activity that is beneficial to me both personally and professionally.

It's become a pretty important weapon in my software development arsenal - if I'm stuck on something for more than 30 minutes, I get out of my workspace and go do something mundane around the house or eat a quick snack. More often than not a solution magically appears in my head.

Back when I worked in an office, if I was stuck trying to solve a hard problem my best option was to find someone familiar enough with the problem space and bounce ideas off of them. This worked pretty well initially but didn't scale too well with problems that became narrower and deeper in scope - I often found myself spending a lot of time trying to catch others up to where it would be valuable to bounce ideas off them. And heading off into the office kitchen to reorganize stuff or watering random plants around the office would just make me look weird.

So, the next time you're having a hard time finding a solution to a problem in the office think about taking the next day off and working from home. Oh, and make sure the dishes aren't done. ;)

Wednesday, June 2, 2010

Slow onclick Javascript handling in Google Chrome 5.0.375.55

Google's Chrome has been a pretty slick browser, and it's speed was the primary reason it became my default browser. The V8 Javascript engine supposedly blows other engines out of the water, and it did feel noticeably faster than others - until this latest version.

Sometime last week, my version of Chrome was auto updated to 5.0.375.55, and I noticed that the website I've been developing became terribly slow. Because of a previous Chrome bug, I hadn't migrated my site from jQuery 1.3 to 1.4, so one of the first things I did was verify the fix and then update my site to use the newer, faster jQuery. As I began testing, I noticed a significant reduction in performance of some features on my site.

Initially I suspected it had something to do with jQuery upgrade. After a few hours of changing things around, I was pretty convinced the problem wasn't jQuery 1.4. I noticed that Firefox and even IE 8 didn't exhibit this latency, so now I started to suspect the new version of Chrome. I hacked up a couple of test pages, and I think I found the culprit. It looks like the onclick handler's performance degrades with the size of the HTML page, resulting in a very noticeable (1-2 second) lag once you have a table with 3000/4000 rows. Firefox and IE 8 don't seem to have any trouble handling this.

I've logged Issues 45620 in the Chromium project with details and a repro to track this problem. This definitely isn't good news for me - with growing adoption of Chrome and general perception that Chrome is the fastest browser, problems like these make website look faulty even when they might not be.

As an aside, I have noticed that Chrome feels like it has regressions more often than other browsers (perhaps a function of shipping more often?) I've run into several issues developing for Chrome - the silver lining is that bug fixes are quicker than rival browsers, but better QA before release would certainly help developers.

Other Chromium issues that I've had trouble with and have had to work around are Issue 7771, Issue 30693, Issue 33411 and Issue 36559.

Update: An comment on Issue 45620 identifies one of the reasons for the slowdown and a workaround.

Wednesday, May 19, 2010

Showing the Cancel button in UIActionSheets on iPad

In it's iPad Human Interface Guidelines Apple provides several guidelines about how user experience should differ between iPhones and iPads. One such element that should behave differently is the UIActionSheet.

Apple recommends that cancel buttons not be shown for action sheets on iPad:
Because taps outside the popover dismiss the action sheet without selecting an item, this results in a default way to cancel the sheet. Including a cancel button would therefore only cause confusion.
But the guidelines do allow exceptions to this rule:
However, if you have an existing popover and are displaying an action sheet on top of other content using an animation, a cancel button is still appropriate.
So you would think following this guideline is as simple as not specifying a cancel button in most cases, and only specifying a cancel button when you have carefully considered the exception case, right? Wrong. Even when you do specify a cancel button, it looks like the iPad runtime automatically always hides it.

So how do you provide a cancel button in the exception case? You could just provide another normal button that has cancel behavior, but that's not ideal because you really want the difference in appearance that UIActionSheets on iPhone provide by default. After some tinkering, the best solution I could find was to set the actionSheetStyle property of the UIActionSheet to UIActionSheetStyleBlackOpaque.

I can speculate why this works, but I don't know for certain so I'd rather not. If you know of a better/more correct way to do this, please do leave a comment.

I am surprised that Apple was mindful enough to list the exception case, and yet careless enough to not provide documentation on how to handle this case - they even seem to have forgotten to document that the iPad automatically overrides user-provided cancel buttons by default.

Saturday, May 8, 2010

Notes from a Week in Paris

I just got back from a lovely week in Paris with my wife - it was a rejuvenating and much required holiday for both of us. Work has been crazy, for her especially. Paris was wonderful and more than lived up to my expectations. We rented a tiny little studio in Le Marais (~120 sq ft, fully equipped including a washer - extremely efficient use of space!) Spending the entire week gave us enough time to explore sights, sounds and tastes of Paris without too much of a rush.

There are lots of books and blogs about visiting Paris and things to do/see. We used a bunch of resources - advice from friends, Lonely Planet's excellent iPhone app, the very insightful Secrets of Paris website with lots of tips on what to see and do and Paris By Train for everything related to using Paris' excellent rail network. However, there were some things I hadn't read about or was surprised by:
  1. Cafés, brasseries and boulangeries/patisseries often have different prices for take away (emporter), eating inside (sallé) and terrace dining. Most cafés/brasseries offer set menus (Formulae) and chef's recommendations which change daily (menu and price). They are almost always much more cost effective than ordering a-la-carté.

  2. The French are very proper. You greet business owners when you enter their store/café and thank/greet them before you leave. Sprawling across lush, green lawns in the several Parisian parks (Jardins) or putting your feet up on empty train seats is frowned upon. You sit in chairs provided in the Jardins and sit up straight in trains.

  3. Freshly squeezed orange juice (jus d'orange pressé) is extremely popular. You'll find orange juice machines pretty much everywhere (including Starbucks), but it's best to find a green-grocer/fruit vendor and get a half-litre jar of fresh squeezed juice each morning. Fruit vendors are everywhere.

  4. By default, coffee means espresso. A cappuccino/café-au-lait at most Cafés is expensive (between 4-8 EUR) and isn't that great (for a Seattlelite, at least). A mocha at Starbucks is EUR 4.50. Little créperies or street cafés have better deals for coffee that tastes identical (1-2 EUR). Or, hit up a McCafé. A 'mocha' is called a 'chocochino' and isn't commonly available.

  5. Sit-down créperies are totally not worth the price/experience. Get a crépe from a street vendor/corner shop and sit in a park or a street bench. The more exotic crépes (savory & sweet) are only available in nicer establishments, obviously.

  6. Wine/liquor is available in grocery stores (supermachés). Supermachés open around 8:30am and shut around 8:30pm. You are expected to bag your own groceries. For late-night grocery runs, there's usually a little-store-that-sells-most-things every few blocks which stays open late (2am)

  7. Bars have later Happy Hours than in the US. Happy Hour generally runs from 6pm to 9-10pm. Folks eat dinner late (most restaurants stay open until 11pm).

  8. Time is specified using 24 hour notation. "What time do you shut?" is answered with "23 hours". Most people don't understand am/pm.

  9. Take binoculars to fully appreciate the amazing art/architecture details.

  10. Use the 'Vélib' shared bike service. Vélib stations are everywhere, the roads and drivers are biker-friendly and it is the cheapest and most convenient way to get between locations in the city. Buy a map-book that has streets sorted by districts (arrondissements) and also has metro/Vélib stations marked.

  11. It is not common for crémeries/gelaterias to offer tastes.

  12. On the metro, or just walking around, you'll see a significant number of people going home (presumably) with one or more fresh baguettes from the bakery. For some reason almost all of these people are men. Join the crowd - fresh baguettes are the only way to go. Bread is baked fresh twice a day (if not continuously).

  13. When you pay cash, check the change you get back. Parisians are either terrible at math or prone to short-changing customers. I routinely got back incorrect change, on average twice a day.

  14. Before you go to a specific place (café, boulangerie, museum, grocery store, monument) on a Saturday/Sunday/Monday check before you head out. Almost every establishment is closed on at least one or more of these days.

  15. Strange, English speaking 'beggar women' holding hand-written post cards will randomly approach you at monuments/museums and ask "Speak English?". I didn't quite figure out what the scam was (I never answered "Yes") but this is most certainly some sort of scam.

  16. Comté tastes awesome, and is the most popular cheese. Camembert is a close second. Get cheese fresh from your neighborhood fromagerie.

  17. If you decide to cook at home, chances are good that you'll find an Italian store nearby selling freshly prepared pastas and raviolis that you can cook at home, in addition to a large number of other Italian foods (including freshly prepared Tiramisu).

Monday, April 19, 2010

Why I use Google Voice for SMS

I have an iPhone with AT&T service, but I recently started using Google Voice for most of my SMS needs. SMSes sent to my Google Voice number are forwarded to my email. It works great for me:
  • I don't want to pay AT&T any more than I have to. I use AT&T out of necessity, not choice.

  • GMail has support for Exchange ActiveSync, and is the default Exchange account on my iPhone. So I get notified of the SMS in email on the phone as soon as it is received (assuming I have data coverage, which is pretty much all of the time) giving me the equivalent of the native SMS experience.

  • The Google Voice extension for Chrome. Chrome is my default browser, I'm in front of a computer for a large part of the day, and my browser is usually open. I can read, send and reply to SMSes using a full keyboard and without exiting the browser, instead of pulling out my iPhone and fiddling with the tiny keyboard to type out a message. So much more convenient!

  • All my SMSes are archived and made searchable not only by Google Voice, but also by GMail. Win.

Sunday, April 18, 2010

Living Every Developer's Wet Dream

Back in June I wrote about new technologies I had started working with and my experiences learning those technologies. Having continued working with most of those since then, I've become incredibly comfortable and proficient with them. It's pretty normal to have a week where I write a bunch of PHP, a few SQL queries, some fancy Javascript, learn new CSS tricks, hack up features in client software using Win32/Cocoa, debug some corner-case issue using logs and a mini-dump and also write some straight up C/C++ server side code with unit tests. Initially, jumping languages and client/server boundaries came with large context-switch penalties but it's become almost second nature now, which feels very empowering and has had a dramatic impact on productivity.

Then, there are the one-offs - new technologies that need to be learnt, integrated into the product/service (in a way that's easy to understand/change in the future) and then 'forgotten' before moving on to the next one. These tend to be client-side technologies usually - server side stuff is much more on-going and stable. Some of the 'bigger' things I've done in the last 6-9 months that would fall into this one-off category are:
  • Fiddling around with HTML5 <audio> and <video> before giving up
    For all the promise of new HTML5 multimedia standards, the sad reality is that we are far from any consensus. Different stakeholders have different motivations, which leaves the issue of a single standard codec up in the air. The proprietary nature of Flash, licensing issues surrounding H.264, Apple's opposition to including Ogg Theora in Quicktime and Microsoft's silence have resulted in the current stalemate. And this shows when you experiment with the different browsers. Development for web/mobile-web is poised to become radically easier once/if this is sorted out, but there is a lot of pain to be endured before then - pain that wasn't worth it to me presently.

  • Flash audio support
    Having decided to not want to build a flash media plugin myself, I first played around with this MP3 Player and later switched to the much more powerful SoundManager2. With a few tweaks, I was able to make it do exactly what I wanted.

  • Facebook Connect
    For single sign-on and any sort of social media integration, Facebook Connect is a must have. Having implemented Facebook Connect for my website and for iPhone, I must say that I am very pleased with the facilities offered by the platform and the consistency across client/server applications. XFBML and FQL are nice touches. My only gripe is that, for a mature and widely adopted platform, documentation seems disproportionately poor as compared to the richness of the Platform APIs.

  • Online Payments
    There's a lot you learn about how safe online transactions really are (they're not) when you dive into how payments really work. I developed a quick payment portal using Payflow Pro by PayPal. Can't really vouch for Payflow Pro yet, though. A few weeks after I finished working with Payflow Pro, PayPal X was announced. I'll eventually get around to migrating to it, I guess.

  • Identicons
    Simple, cute, pretty. I use the ones provided by Gravatar.

  • Javascript Craziness
    One of the crazier things I've done, among others, with Javascript is to try and emulate a desktop-software like user experience in a website. Dragging, dropping, keyboard navigation, sorting, multi-select with Ctrl/Shift/Meta... oh boy! It look a while, but with some help from jQuery UI (the different components work great individually, but try to also make a sortable a draggable and a selectable, and you're in a world of hurt) and a lot of long nights, I finally got a lot of this done. For Forefox. Then I spent some time making it work for Chrome. And then a whole bunch of more time making it work for IE8. It sorta works in IE7, and not at all in IE6, but honestly if you're still using those browsers you should be chastised. Someday, I want to wrap all this into my own little plugin that others can use. If you told me a year ago that I would be some kind of JS nut, I would have laughed. Ha!

  • iPhone web-app (before I ditched it for a full blown iPhone app)
    Not wanting to deal with Apple and their strange AppStore practices, I decided to try and be really creative with native Quicktime support built into Safari on the iPhone. I almost got all the way to hacking up really neat functionality, complete with callback from Quicktime into my website, background playback and an rich interactive experience. With Flash coming soon to other mobile platforms, this would have been ideal for providing mobile experiences without needing multiple platform-specific applications. However, the iPhone web app felt clumsy and not polished, so I ditched it and went the iPhone app route. I reserve comment on my experiences with development for the iPhone - my app hasn't been submitted/approved yet. ;)
    I also foresee myself playing with other mobile development platforms in the very near future. I think a blog post comparing my experiences with the different platforms might make for good reading.
In addition to these one-offs, there are the perpetual how-will-it-scale and how-will-it-perform issues that need to be considered for every little feature. One of the harder lessons for me was to change my default mindset about how I approached design. While I wouldn't say I optimized prematurely, my design was heavily influenced by potential for optimization. (segue: This, I now realize, is a result of my years at Microsoft. At companies like Microsoft, you do things once and you do them right - rapid iteration is often not possible or not practical. So, while this approach has its merits it's not ideal for agility in startup environments). The new approach that's imbued into me now is to build it quick and make it simple first. Simplicity is the bedrock of agility. Simpler code is easier to revisit and to scale. Complexity is technical debt that you don't really need or want.

What's the biggest thing I've learnt in the last year? I learnt how to learn efficiently.
You can spend hours studying/learning about something and barely scratch the surface. Or you can study to retain information for very short periods of time. Exceptional developers are able to identify when they need to deep-dive vs when they need to simply skim the surface. They can instinctively answer the "How do you learn enough to make well informed decisions and yet don't spend a disproportionate amount of time learning without producing?" question. Transient learning is only good when you never need to revisit a decision - such cases are rare. Otherwise, learning to execute quickly and leaving a trail behind you is a crucial skill. I believe I'm well on my way down this path.

So yes, in many ways I am living a developer's wet-dream. And fortunately, I am able to do a little more than just development stuff. But I know this probably won't last forever. This series of blog posts is my attempt to capture my memories for when I'm not doing this everyday anymore.

Friday, March 5, 2010

SecureCRT and Windows 7 - The Case of the Magically Disappearing Window

Ever since I upgraded my copy of SecureCRT to 6.5.1 on Windows 7 x64, I started noticing really strange behavior. Occasionally, my SecureCRT window would just 'disappear'. What do I mean by 'disappear'? The SecureCRT icon on the Windows 7 taskbar, which appears like this when a window is open:

would suddenly change to this, which is what an icon looks like when the process has no windows open:


I could still see SecureCRT.exe running, but didn't find any windows for it, even in the Alt+Tab menu. Nothing appeared in my system tray either except for the network connection, volume and system time information.

Once this happened even after a system restart (isn't that the usual fix for everything?! ;)) I got curious. Eventually, I realized that this happened each time I minimized the window. Somehow, minimizing the window is muscle memory for me now and I don't really notice I'm doing it until I consciously try.

Minimizing a windows shouldn't ever cause the window to just 'disappear' so I started looking at the different SecureCRT settings and noticed a curious "Minimize to Activator" setting that was checked, and seemed suspicious. Unchecking it was all it took to restore sanity.

What is this 'Activator'? It's an icon that sits in your system tray. But Windows 7, by default, hides away all my system tray icons so I had no idea this even existed until I started investigation this issue. This is the 'Activator':


SecureCRT removes the window and sticks it into this Activator. Without any sort of visual cue or notification bubble. By default. Ugh! To restore it, you need right click the icon and select the "Restore" option. Umm... isn't that the whole point of Windows' built-in minimize implementation?

This seems like a poorly designed feature, especially given how Windows 7 treats tray icons (and I know SecureCRT v6.5.1 has some Windows 7 specific features so this isn't accidental). Making the process 'appear' like it's not running and sticking into into the 'Activator' (why isn't it called 'system tray icon'?) instead seems like bad design. If anything, this should definitely not be on by default.

Ed: Title inspired by Mark Russinovich's blog posts

Update: Just got an email response back from SecureCRT's support staff to a query about I this I had sent last night. She clarified "... the global option is **not** enabled by default. The session option by default is a "tri-state" option, meaning it uses the setting found in the global option."

Wednesday, March 3, 2010

Discouraging Tardiness in Social Circles

What's the best way, if any, to incentivize punctuality in a social setting i.e. among friends and family? Alternately, is there any way to dis-incentivize/discourage impunctuality?

In professional environments this isn't usually a problem. There is generally one party which has some form of authority (the boss running a meeting, the doctor who is so booked up that you got an appointment only after months, the VP who can only meet for 20 minutes before he has to run to another meeting, the senior engineer whose time really is more precious than yours) who sets an example that is expected to be followed, and can dangle a carrot or wield a stick to manage tardiness. There is also a personal incentive to be on time - you want to make a good first impression, not appear to be the one holding the team behind, or not miss out on something important if you're late.

The social dynamics that exist among friends and family are vastly different though. Often times, no one person holds a position of authority over another, the 'meetings' aren't usually that important and outcasting the offenders as a group isn't really an option either. Hence the repercussions of tardiness are pretty mild, if any. In many ways, the punctual folk have more to lose.

The ideal solution wouldn't make any individual or sub-group appear to be 'jerks' and yet make each member of the group want to be punctual. Poking fun at the offenders or just generally being prudish isn't going to help, I think.

So, any ideas? I'm drawing blanks...

Friday, February 19, 2010

Coupon etiquette?

What's the 'correct' social etiquette about using restaurant coupons purchased online (such as those from restaurant.com or groupon.com) when you go out in a group and split the check?

For coupons you get in the mail, or basically free stuff, I assume common etiquette is to offer the coupon to the group to use and split the remainder. Does it work the same way when you've bought, say, a $50 coupon for $25 (i.e. you use the coupon to cover $25 off your tab and offer the discount value to the group)? Do you use the entire $50 to cover just your tab? Or maybe simply not using the coupon when dining as a group is the right way to go?

Tuesday, February 9, 2010

Hey Google Buzz, I don't email my friends anymore

I just saw the presentation from Google about Google Buzz. The quick summary of the announcements - creating a social network using my email contacts and emailing history as the source of my social graph, and using rich geo-tagged status messages (buzz-es) to keep it updated. It comes with rich mobile apps to help this take off. They also promised enterprise versions of Buzz.

Buzz sounds pretty exciting. Some of the geo-tagged updates sounds really slick. My first reaction - this is going to be 'public follow' thing more than a 'follow my friends' things. Because emailing my friends is like, so 2007!

Fact is, I don't email my closest friends/family anymore. I either IM them, post on their Facebook page, send them SMS or call them. Email is more for work type, non-personal stuff. Having said that, it's important to note that Google does have GTalk and Google Voice so I hope they weigh activity from GTalk and Google Voice higher than GMail activity when figuring out who I must 'auto-follow'.

Time for someone to write a Buzz-to-Facebook cross poster. :)

Monday, January 25, 2010

Test cases for basic Facebook Connect integration

I recently spent a couple of days adding some basic Facebook Connect functionality to an existing site with it's own Login/Auth system. The process isn't too hard and there are several examples and even a nice tutorial which shows you exactly how to hook this up.

The tutorials and the Facebook developer wiki to a good job of getting you on the right path and putting up a quick working implementation. However, there are several subtleties that need to be addressed, depending on the requirements of the existing site. I didn't really find a good compilation of these 'test cases' anywhere. Facebook's own documentation probably references several of these but they're scattered all over their wiki page and are very easy to miss if your primary focus is getting this stuff up and running quickly.

This blog post is my attempt to compile of list of things you need to think about when adding Facebook Connect support for sign up/login or linked accounts on an existing site with its own authorization system. Here are a few characteristics (pretty standard) of the existing site:
  • Authentication using a username/email address and password
  • Cookies to help keep users logged in
  • Accounts keyed by email address (i.e. 1:1 mapping between accounts and email address)
  • Ability to reset accounts by having a temp password send to user's email address
  • Fully ajax navigation (which complicates matter a little bit)
  • Logout functionality which clears up cookies
The goals for the Facebook Connect integration are pretty simple:
  • New User Sign Up without needing to create a site-specific password (email address needed)
  • Ability for existing users to link with their Facebook account
  • 1:1 mapping between accounts on your site and Facebook accounts
  • Single sign-on experience
Let's call the existing site YouFace. I won't go into how to get stuff hooked up or explaining how to implement this - it's not too complicated and there's a bunch of information online for it. Instead, treat this as a list of a few things you probably want to test when you're done. There shouldn't really be anything ground breaking here - this was just a convenient way for me to build a list of things to test on my own site. Some of these are handled by the Facebook Connect client library, while some need your application to do the right thing. Please comment if I've missed any cases or if some just appear wrong - I would love to fix my site!
  • Sign Up should fail if the user denies permission to the app (category: sign up)

  • Since we need access to an email address, Sign Up should fail if the user provides publish permission but denies email permission (category: sign up)

  • If the user provides an email address that already exists in your system, fail Sign Up. Make sure no YouFace backend tables are modified (category: sign up, 1:1 mapping) PS - when this happens, I didn't find a way for you to de-authorize YouFace on the Facebook user's behalf. The user must manually do this if they wish you use the same account but provide a different email address.

  • Accounts created using Facebook Connect should not be able to login using YouFace's default email/password login system (category: sign in, account security). PS: Since YouFace accounts require a password and those created using Facebook Connect don't, make sure to insert a random password hash into your table to avoid silly errors

  • Accounts created using YouFace should be able to sign in without requiring to be signed into Facebook, even if when a link to a Facebook accounts exists (category: sign in)

  • Attempting to Sign Up with a Facebook account that's already linked and authorized should result in the Sign In experience (category: sign up, sign in)

  • Attempting to Sign In with a Facebook account that YouFace hasn't seen before should lead to the Sign Up experience, i.e. create a new account (category: sign up, sign in)

  • Linking a Facebook account with an existing YouFace account should add the email address from Facebook to the list of 'used addresses' in the YouFace system (category: linking, 1:1 mapping)

  • When a Facebook linked account de-authorizes YouFace, your system should record this action and change state as needed (category: authorization)

  • Linking a YouFace account with a Facebook account that's already been linked to some other account in your system should be denied (category: linking, 1:1 mapping, authorization)

  • Users with linked accounts can continue to sign in using their YouFace credentials even after they de-authorize YouFace from their Facebook account (category: linking, authorization)

  • Linking a Facebook account different from one you originally linked to an existing YouFace account should be denied. This can happen when an existing user links a YouFace account to Foo@FB, de-authorizes the app and then tries to link Bar@FB to that account (category: linking, 1:1 mapping, authorization)

  • A user who Signed Up using Facebook Connect should not be allowed to link their account to another Facebook account (category: sign up, 1:1 mapping)

  • Attempting to Sign In or Sign Up with a Facebook account that YouFace has seen, but one that has currently not authorized the YouFace app (maybe the user revoked authorization) should ask to re-authorize before proceeding. It must log the user into the original YouFace account linked with that Facebook account, i.e. no new account should be created. If that account was originally created using Facebook Connect, the primary email address should be updated to reflect the user's current Facebook contact email address else the primary email address should not be changed from what the user provided YouFace when they signed up (category: sign in, 1:1 mapping, reauthorization)

  • If a user with a linked account is logged into Facebook, they should be signed into YouFace directly (category: single sign in)

  • Users who signed into YouFace with Facebook Connect should be logged out of Facebook if they log out of YouFace (category: single sign out)

  • Users who signed into YouFace with Facebook Connect should be logged out of YouFace if they log out of Facebook (category: single sign out)

  • Users with linked accounts who signed into YouFace directly (not using Facebook Connect) should continue to remain signed in, regardless of whether they're were originally signed into Facebook, when they sign out of Facebook (category: sign in, sign out)

  • Users who created their account using Facebook Connect should not be allowed to reset passwords or have temporary passwords sent to their email addresses (category: account security)

  • This is a kinda convoluted. Users Foo and Bar both have YouFace accounts linked to Facebook. Bar is logged into YouFace with his YouFace account, and Foo is logged into Facebook, in the same instance of the browser (Bar logged in first). When Bar logs out of YouFace, Foo should not get logged out of Facebook (category: single sign out, multiple accounts ). PS: YouFace's Connect library is going to pick up a valid Facebook ID but it's that of Foo, not Bar. So you need to do more than just client-side detection of whether someone's logged into Facebook.