Oct 09

Silverlight is to RIA as Giuliani is to the Republican party?

Adobe, Microsoft, Tech No Comments »

I was speaking to a friend, who is an open Web kinda guy, and we had a giggle when we looked over at the Adobe and Microsoft Silverlight booths and he said “maybe Flash and Silverlight will split the Republican vote”.

Now, the idea of splitting the Republican vote is one I like to think about, but I hadn’t thought about it with the RIA context. By adding Silverlight as a platform we will see more flash rectangular boxes right? Maybe, maybe not.

Will the fight for good RIA people have some move from Adobe to Microsoft? Or will the entire pie grow by a large amount?

In the fight itself, will Silverlight win as it is a “new” look. This is where the Giuliani comparison really fails. Adobe has actually done a really good job. It can’t be linked to George Bush (who hasn’t). But maybe the social conservatives who don’t like Rudy because of his views on abortion, or the odd picture of him in a dress, can be linked to certain Adobe fans?

Watching the huge booths of both Adobe and Microsoft one thing is for sure, the primaries are here, and the general election is coming.

The Open Web needs to be ready.

Oct 09

Managing calendars is still driving me nuts

Tech 2 Comments »

We have great calendar clients such as GCal, iCal, 30boxes, etc etc, and we have simple standards such as ics, and hcal but I still have a nightmare with calendars.

The problem is that I need to contort my calendar data in a bunch of use cases:

  • I want to be able to read my calendar from all of my devices
  • I want my wife to be able to read non-work events
  • My trip to London last week for FOWA…. that was work, but my wife still wants to know I am flying to London and when!
  • I have people at work looking at my calendar trying to schedule time
  • I have grouped calendars. For example, my team shares a calendar that holds the conferences that are going / we are at
  • I want Dopplr to read my trips from my calendar (so it needs to know what a trip is.

The problem is that I constantly run into issues. My main hard rule is:

I never want to entry a calendar event more than once!

This causes problems. People at work only see my main calendar, so if I have something in my “Trips” calendar they don’t see it, and schedule time that I can’t make. Often my wife doesn’t see items that she should be able to see because they are on the wrong calendar.

I think I could be happy if I just had the feature to add an event to MULTIPLE calendars at once. With that alone, I selectively choose the ACL model based on the calendar. I need this feature now :)

Oct 08

Comparing the HEAD from Facebook and Google

Facebook, Tech 1 Comment »

When I built the Facebook Auto Expand Greasemonkey script I had to find out the function that kicks off the expansion, and I wanted it to be the right one.

Firebug can help out there, but as I looked through I was surprised by something:

Facebook doesn’t seem to minify/compress their JavaScript!

Since they push ~60 billion pages a month, I was a touch surprised. With this nugget I decided to grab the <head> of Facebook and iGoogle to compare.

The obvious differences as you look at them side by side (below):

  • iGoogle inlines a lot more: CSS and some JavaScript
  • Facebook has ~26 separate external CSS link tags for my profile
  • Facebook has a lot of external JavaScript files

Maybe it is time to run some YSlow?

My Facebook HEAD


<head>
<title>Facebook | Home</title>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<meta name="robots" content="noodp" />
<meta name="description" content="Facebook is a social utility that connects people with friends and others who work, study and live around them. People use Facebook to keep up with friends, upload an unlimited number of photos, share links and videos, and learn more about the people they meet." />
<script type="text/javascript">var cc=new Image();cc.onload=function(){cc.hit=(typeof(Env)=="undefined");};cc.src="http://static.ak.facebook.com/images/global_menu_space.gif?12:37897";Env={method:"GET",dev:0,start:(new Date( )).getTime(),cache:(((typeof(cc)!="undefined")&&cc.hit)||0),ps_limit:5,ps_ratio:4,pkgv:1};</script><link rel="stylesheet" href="http://static.ak.facebook.com/css/base.css?12:62201" type="text/css"/>
<link rel="stylesheet" href="http://static.ak.facebook.com/css/apps_menu.css?12:43718" type="text/css"/>
<link rel="stylesheet" href="http://static.ak.facebook.com/css/typeahead.css?12:19905" type="text/css"/>
<link rel="stylesheet" href="http://static.ak.facebook.com/css/wallpro.css?12:57950" type="text/css"/>
<link rel="stylesheet" href="http://static.ak.facebook.com/css/dialog.css?12:39930" type="text/css"/>
<link rel="stylesheet" href="http://static.ak.facebook.com/css/attachments.css?12:59060" type="text/css"/>
<link rel="stylesheet" href="http://static.ak.facebook.com/css/captcha/captcha.css?12:60350" type="text/css"/>
<link rel="stylesheet" href="http://static.ak.facebook.com/css/dialogpro.css?12:61009" type="text/css"/>
<link rel="stylesheet" href="http://static.ak.facebook.com/css/editor.css?12:52799" type="text/css"/>
<link rel="stylesheet" href="http://static.ak.facebook.com/css/components.css?12:53705" type="text/css"/>
<link rel="stylesheet" href="http://static.ak.facebook.com/css/sharer.css?12:55903" type="text/css"/>
<link rel="stylesheet" href="http://static.ak.facebook.com/css/share_media.css?12:60984" type="text/css"/>
<link rel="stylesheet" href="http://static.ak.facebook.com/css/typeaheadpro.css?12:58664" type="text/css"/>
<link rel="stylesheet" href="http://static.ak.facebook.com/inbox/css/inbox.css?12:62108" type="text/css"/>
<link rel="stylesheet" href="http://static.ak.facebook.com/css/homeslice.css?12:58781" type="text/css"/>
<link rel="stylesheet" href="http://static.ak.facebook.com/css/info.css?12:56012" type="text/css"/>
<link rel="stylesheet" href="http://static.ak.facebook.com/css/profile.css?12:60600" type="text/css"/>
<link rel="stylesheet" href="http://static.ak.facebook.com/css/feed.css?12:61849" type="text/css"/>
<link rel="stylesheet" href="http://static.ak.facebook.com/css/feed_ad.css?12:43135" type="text/css"/>
<link rel="stylesheet" href="http://static.ak.facebook.com/css/status.css?12:39712" type="text/css"/>
<link rel="stylesheet" href="http://static.ak.facebook.com/css/gifts/gift.css?12:36833" type="text/css"/>
<link rel="stylesheet" href="http://static.ak.facebook.com/css/mobilepro.css?12:60047" type="text/css"/>
<link rel="stylesheet" href="http://static.ak.facebook.com/css/sms.css?12:59681" type="text/css"/>
<link rel="stylesheet" href="http://static.ak.facebook.com/css/sprite/icons.css?12:56618" type="text/css"/><!--[if lte IE 6]><style type="text/css" media="screen">/* <![CDATA[ */ @import url(http://static.ak.facebook.com/css/ie6.css?12:62634); /* ]]> */</style><![endif]-->
<!--[if gte IE 7]><style type="text/css" media="screen">/* <![CDATA[ */ @import url(http://static.ak.facebook.com/css/ie7.css?12:62093); /* ]]> */</style><![endif]-->
<script type="text/javascript" src="http://static.ak.facebook.com/js/base.js?12:62634"></script>

<script type="text/javascript" src="http://static.ak.facebook.com/js/extended.js?12:59503"></script>
<script type="text/javascript" src="http://static.ak.facebook.com/js/string.js?12:59850"></script>
<script type="text/javascript" src="http://static.ak.facebook.com/js/async.js?12:61542"></script>
<script type="text/javascript" src="http://static.ak.facebook.com/js/deprecated.js?12:58595"></script>
<script type="text/javascript" src="http://static.ak.facebook.com/js/apps_menu.js?12:55974"></script>
<script type="text/javascript" src="http://static.ak.facebook.com/js/typeahead_ns.js?12:60552"></script>
<script type="text/javascript" src="http://static.ak.facebook.com/js/suggest.js?12:52414"></script>
<script type="text/javascript" src="http://static.ak.facebook.com/js/dynamic_dialog.js?12:51503"></script>
<script type="text/javascript" src="http://static.ak.facebook.com/js/captcha.js?12:52414"></script>

<script type="text/javascript" src="http://static.ak.facebook.com/js/recaptcha_ajax.js?12:60350"></script>
<script type="text/javascript" src="http://static.ak.facebook.com/js/typeaheadpro.js?12:61630"></script>
<script type="text/javascript" src="http://static.ak.facebook.com/js/dialogpro.js?12:62226"></script>
<script type="text/javascript" src="http://static.ak.facebook.com/js/fbml.js?12:62093"></script>
<script type="text/javascript" src="http://static.ak.facebook.com/js/swfobject.js?12:55357"></script>
<script type="text/javascript" src="http://static.ak.facebook.com/js/editor.js?12:58199"></script>
<script type="text/javascript" src="http://static.ak.facebook.com/js/timezone.js?12:52149"></script>
<script type="text/javascript" src="http://static.ak.facebook.com/js/share.js?12:61979"></script>
<script type="text/javascript" src="http://static.ak.facebook.com/js/home.js?12:47671"></script>

<script type="text/javascript" src="http://static.ak.facebook.com/js/polls/polls.js?12:48801"></script>
<script type="text/javascript" src="http://static.ak.facebook.com/js/pools.js?12:36733"></script>
<script type="text/javascript" src="http://static.ak.facebook.com/js/editregion.js?12:56446"></script>
<script type="text/javascript" src="http://static.ak.facebook.com/js/statuspro.js?12:52414"></script>
<script type="text/javascript" src="http://static.ak.facebook.com/js/profile.js?12:62148"></script>
<script type="text/javascript" src="http://static.ak.facebook.com/js/rooster.js?12:40867"></script>
<script type="text/javascript" src="http://static.ak.facebook.com/js/poke.js?12:62143"></script>
<script type="text/javascript" src="http://static.ak.facebook.com/js/privacy.js?12:57099"></script>
<script type="text/javascript" src="http://static.ak.facebook.com/js/addfriend.js?12:62209"></script>

<script type="text/javascript" src="http://static.ak.facebook.com/js/mobile.js?12:61909"></script>
<script type="text/javascript" src="http://static.ak.facebook.com/js/search_typeaheadpro.js?12:59982"></script>
<script type="text/javascript" src="http://static.ak.facebook.com/js/animation.js?12:62119"></script><link rel="search" type="application/opensearchdescription+xml" href="http://static.ak.facebook.com/opensearch_desc.xml?12:27839" title="Facebook" />
<link rel="shortcut icon" href="http://static.ak.facebook.com/favicon.ico" />
</head>

My iGoogle HEAD

<head><meta http-equiv="content-type" content="text/html; charset=UTF-8"><meta name="description" content="iGoogle is your personalized Google page. Add news, photos, weather, and stuff from across the web to your page.">
<link rel="shortcut icon" href=" http://www.google.com/favicon.ico" type="image/x-icon"/>
<link rel="icon" href="http://www.google.com/favicon.ico" type="image/x-icon"/>
<title>iGoogle</title>
<script>var _IG_time_epoch = (new Date()).getTime();var IGoogleTranslation = {};</script>
<script src="/ig/extern_js/f/CgJlbhICdXMrMAE4ACw/93yMfLuYD4A.js"></script>
<script>var domain = document.location.hostname;var google_pos = domain.indexOf('.google.');if (google_pos > -1) {domain = domain.substring(google_pos);document.cookie = "TZ=" + (new Date()).getTimezoneOffset()+ ';path=/;domain=' + domain;}_et="Tr8WCSVW";_source="";_setp_url="/ig/setp";_gmail_json_url="/ig/gmailjson";_magic_tabs_url="/ig/filltab";_prefid="";_uli=true;_pnlo=false;_mpnlo=false;_pl=false;_mod=true;_pid="";_old_html = false;_use_old_feed_styles = false;_cbp=true;_authpath="";_upc();var _pl_data = {};</script>
<script><!--
function save_chkbox_value(hidden_elem_name, checked) {var hidden_elem = document.getElementById(hidden_elem_name);hidden_elem.value = checked ? "1" : "0";}function module_loader_closure(el, rm) {return function() {_IG_RemoveModuleEventHandler(rm.id,'unzip',arguments.callee);el.src=rm.base_iframe_url;};}function RemoteModule(spec_url, id, render_inline, base_iframe_url,caching_disabled, is_zipped) {this.spec_url = spec_url;this.id = id;this.render_inline = render_inline;this.base_iframe_url = base_iframe_url;this.caching_disabled = caching_disabled;this.old_width = 0;this.wants_scaling = false;      this.is_inlined = function() { return this.base_iframe_url == ""; };this.is_zipped = is_zipped;};var remote_modules = [];_IG_RegisterOnloadHandler(function() {for (var i=0;i<remote_modules.length;i++){var rm=remote_modules[i];var el=_gel("remote_iframe_"+rm.id);if(el){if (rm.is_zipped && true) {_IG_AddModuleEventHandler(rm.id, 'unzip',module_loader_closure(el, rm));} else {el.src=rm.base_iframe_url;}}}});function ifpc_resizeIframe(iframe_id, height) {var el = document.getElementById(iframe_id);if (el) {el.style.height = height + "px";}}_IFPC.registerService('resize_iframe', ifpc_resizeIframe);function _ig_gmid_(iframe_id) {return(iframe_id.split("_")[2]);}function _setModTitle(title, module_id) {var title_element = _gel("m_" + module_id + "_title");if (title_element) {title_element.innerHTML = _hesc(title);}}function ifpc_setTitle(iframe_id, title) {if (typeof(iframe_id) == undefined|| !iframe_id || iframe_id == "undefined") {return;}_setModTitle(title, _ig_gmid_(iframe_id));}_IFPC.registerService('set_title', ifpc_setTitle);function _IG_SetTitle(title, specified_module_id) {if (typeof(specified_module_id) == "undefined"|| !specified_module_id || specified_module_id == "undefined") {throw new Error("Inline modules must specify their "+ "__MODULE_ID__ when using _IG_SetTitle");} else {_setModTitle(title, specified_module_id);}}function ifpc_setPref(iframe_id, var_args) {var module_id = _ig_gmid_(iframe_id);var prefs = new _IG_Prefs(module_id);var keyValues = new Array();for (var n = 1; n < arguments.length; n += 2) {keyValues.push(arguments[n], arguments[n + 1]);}prefs.set.apply(prefs, keyValues);}_IFPC.registerService('set_pref', ifpc_setPref);function share_gadget_safe(msg, url, width, mod_id, send_label,cancel_label) {_share_gadget(_hesc(msg), _hesc(url), _hesc(width), _hesc(mod_id),_hesc(send_label), _hesc(cancel_label));}_IFPC.registerService('share_gadget', share_gadget_safe);var isPubSubEventRouterLoaded = false;function setupPubSubEventRouter() {if (!isPubSubEventRouterLoaded) {document.write("\x3cdiv id\x3d\x22remote_pubsub_event_router\x22 name\x3d\x22remote_pubsub_event_router\x22 style\x3d\x22border:0;padding:0;margin:0;width:100%\x22\x3e\x3ciframe id\x3d\x22remote_iframe_pubsub_event_router\x22 name\x3d\x22remote_iframe_pubsub_event_router\x22 style\x3d\x22border:0;padding:0;margin:0;width:0;height:0;overflow:hidden\x22 frameborder\x3d0 scrolling\x3dfalse src\x3d\x22http://pubsub.gmodules.com/ig/modules/pubsub_event_router.html\x22\x3e\x3c/iframe\x3e\x3c/div\x3e");isPubSubEventRouterLoaded = true;}}// -->
</script>
<script src="/ig/f/CSyPjYmOhjg/lib/libminimessage.js"></script>
<style>.modbox .el {display:;}.modbox .csl, .modbox .es {display:none;}.modbox_e .el {display:none;}.modbox_e .csl, .modbox .es {display:;}.dm {position:relative;width:1px;height:1px;}.fres {width:expression(_gel("ffresults").offsetWidth+"px");overflow:hidden;}.panelo {}.panelc {}.mod {}.unmod {}form {display:inline;}.c {clear:both;}.fbox {margin-top:1px;margin-right:6px;display:block;overflow:hidden;width:12px;height:12px;background-image: url('/ig/images/max_dark_blue.gif');background-repeat: no-repeat;cursor:hand;cursor:pointer;}.fmaxbox, .fmaxbox_reverse_directionality {background-image:url('/ig/images/max_dark_blue.gif');}.fminbox, .fminbox_reverse_directionality {background-image:url('/ig/images/min_dark_blue.gif');}a.fmaxbox:hover, a.fmaxbox_reverse_directionality:hover {background-image:url('/ig/images/max_dark_blue_highlight.gif');}a.fminbox:hover, a.fminbox_reverse_directionality:hover {background-image:url('/ig/images/min_dark_blue_highlight.gif');}a.ddbox {background-image:url(/ig/images/arrow_blue.gif);}a.ddbox:hover {background-image:url(/ig/images/arrow_blue_highlight.gif);}a.delbox {background-image:url(/ig/images/x_blue.gif);}a.delbox:hover {background-image:url(/ig/images/x_blue_highlight.gif);}a.minbox {background-image:url(/ig/images/min_blue.gif);}a.minbox:hover {background-image:url(/ig/images/min_blue_highlight.gif);}a.maxbox {background-image:url(/ig/images/max_blue.gif);}a.maxbox:hover {background-image:url(/ig/images/max_blue_highlight.gif);}.f_tbl {font-size:12px;margin-right:2px;margin-left:2px;padding-top:4px;padding-bottom:4px;}.fr_tbl {margin-right:2px;margin-left:2px;padding-top:4px;padding-bottom:4px;}.fb {font-size:12px;padding:2px;padding-top:4px;border: 1px solid #d1d3d4;border-top:none;overflow:auto;}.sftl {border: 1px solid #d1d3d4;border-bottom:none;}.sftl_reverse_directionality {border: 1px solid #d1d3d4;border-bottom:none;clear: right;text-align: right;}.uftl {border:1px solid white;border-bottom:none;}.uftl_reverse_directionality {border:1px solid white;border-bottom:none;clear: right;text-align: right;}.remote_module_textbox {box-sizing:border-box;-moz-box-sizing:border-box;padding:1px;padding-left:4px;margin:2px;margin-left:3px;width: 95%;}* html .remote_module_textbox {width: 91%;}.remote_module_select {box-sizing:border-box;-moz-box-sizing:border-box;padding:0px;width:95%;margin:2px;margin-left:3px;}</style><link rel="stylesheet" type="text/css" href="/ig/f/hDk4Fm4iv7I/ig.css"/><style></style><style>#gbar{float:left;height:22px}
#gbarl{border-top:1px solid #c9d7f1;font-size:0;height:1px;position:absolute;right:0;top:24px;width:110%}
#gbar a{color:#00c}
#gbar .gbard{background:#fff;border:1px solid;border-color:#c9d7f1 #36c #36c #a2bae7;display:none;font-size:13px;position:absolute;top:24px;z-index:1000}
#gbar .gbard a{display:block;padding:.2em .5em;text-decoration:none;white-space:nowrap}
#gbar .gbard a:hover{background:#36c;color:#fff}
#gbar td{font-size:13px;padding-right:1em}
#guser{font-size:13px;padding-bottom:7px !important;padding-top:0}
#gbarc{font-size:0;height:1px}
#guser,#gbarc{background-color:#fff}#guser{color:#000;margin-bottom:0px}#guser a:link,#guser a:visited,#guser a:hover{color:#00c}#gbarl{border-top:0;border-bottom:1px solid #c9d7f1}#guser{padding-right:10px ! important;}</style>
<script>window.gbar={};(function(){function o(a,b,c){var e="on"+b;if(a.addEventListener){a.addEventListener(b,c,false)}else if(a.attachEvent){a.attachEvent(e,c)}else{var d=a[e];a[e]=function(){var f=d.apply(this,arguments),g=c.apply(this,arguments);return f==undefined?g:(g==undefined?f:g&&f)}}};var h=window.gbar,m=["affdom","channel","client","hl","hs","ie","lr","ned","oe","og","rls","rlz"];function i(a){return escape(unescape(a.replace(/\+/g," "))).replace(/\+/g,"%2B")}function j(a){return a=="c"||a=="o"||a=="m"}h.getHtml=function(a){var b;for(var c=0;c<a.length;c++){if(a[c][2]==""){b=a[c][0]}}var e=j(b)?" target=_blank":"",d="<td nowrap>",f="<table border=0 cellpadding=0 cellspacing=0 style=margin-left:"+h.getPad(true)+"px><tr>"+d;for(var c=0;c<a.length;c++){if(a[c][0]==b){f+=a[c][1].bold()+d}else{f+="<a href=";if(a[c][3]==3){f+='# onclick="this.blur();return gbar.toggle(event)" style=text-decoration:none'+e+"><u>"+a[c][1]+"</u> <span style=font-size:11px>▼</span></a><tr><td colspan="+c+"><td><iframe class=gbard id=gbarif style=border:0;z-index:999></iframe><div class=gbard id=gbardd onclick=gbar.stopB(event)>";d=""}else{f+=n(b,a[c][0],a[c][2])+e+" onclick=gbar.close(event)>"+a[c][1]+"</a>"+d}}}f+="</div></table>";return f};h.getPad=function(a){var b=-1,c=a?10:4,e=document.body.currentStyle,d=document.defaultView;if(e){b=a?e.marginLeft:e.marginTop}else if(d){b=a?d.getComputedStyle(document.body,"").marginLeft:d.getComputedStyle(document.body,"").marginTop}b=parseInt(b,10);return b>=0&&b<c?c-b:1};function n(a,b,c){var e=window.location.search.substring(1),d=e.match("q=([^&]*)"),f=e.match("near=([^&]*)"),g=c+(c.match("[?]")?"&":"?");g+="tab="+a+b;if(j(b)&&window.location.protocol=="https:"){g=g.replace("http:","https:")}if(!j(b)&&!j(a)){for(var k=0;k<m.length;k++){var l=e.match("("+m[k]+")=([^&]*)");if(l){g+="&"+l[1]+"="+i(l[2])}}if(d&&f&&a=="l"&&b!="l"){g+="&q="+i(d[1])+"+"+i(f[0])}else if(d){g+="&q="+i(d[1])}}return g}h.toggle=function(a){h.stopB(a);var b=document.getElementById("gbardd"),c=document.getElementById("gbarif");if(b&&c){b.style.display=b.style.display=="block"?"none":"block";c.width=b.offsetWidth;c.height=b.offsetHeight;c.style.display=b.style.display}return false};h.close=function(a){var b=document.getElementById("gbardd");if(b&&b.style.display=="block"){h.toggle(a)}};h.stopB=function(a){if(!a){a=window.event}a.cancelBubble=true};o(document,"click",h.close);})();</script>
</head>
Oct 08

Choosing !(Rails || Java)

Tech No Comments »

Leah Culver gave a talk on lessons learned from Pownce at FOWA last week. She just uploaded her slides, and an interesting part is on her choice of Django.

  • Leah was a Java programmer. Although she knew that inside and out she didn’t want to do that again this time
  • Kevin is a PHP hacker (but not a programmer ;), and Digg is PHP, but Leah didn’t want to go that route either
  • Rails is sexy and Web 2.0-y, yet Leah didn’t choose that either

Why did Leah choose Django? Ignoring the fact that Django is a very nice framework, it was much more social:

  • It is cool to be a top Django app (too late to get the same gains with Rails. Twitter was already that)
  • Python “feels” nicer to her.

That is again the key. So much of this stuff is based on how you feel. Python and Ruby are great, but they feel different. Java has a certain feeling too ;)

Oct 07

Greasemonkey script to expand Facebook Leftbar

Facebook, Tech 1 Comment »

For some reason it has always bugged me to only have a few applications show in the left bar, with the “more >>” link that you have to hover/click on to get the full list.

So, I wrote the simplest of Greasemonkey scripts to automatically expand the more link whenever I go to a Facebook page.

Now I have all of my gems (Pet Monkeys and co…) in plain sight. I am glad I am doing the important things while I wait for my flight from London back home to the US. Yup, “back home” to the US :)

facebookexpandedmore.png

Oct 05

The relationship between the platform vendor and the third party developers: Google, Facebook, Apple, Microsoft

Facebook, Tech 2 Comments »

At the end of Dave Morin’s Facebook talk at he kindly did the usual, and accepted questions from the audience. For all of the other talks that I participated in, the questions were fired off by fellow developers and were generally technical.

This time around, it was time for the reporters. They popped out of nowhere to ask the usual silly questions that you KNOW the guy can’t answer. I get them all the time for Google. “When is the Google Deathstar coming out”. What are they thinking? Do they think that I will slip and think “hmm, I guess we released the Deathstar so I can start talking about it up on stage here!”

Anyway, one of the rants hidden in a question was about how the platform competes with third party applications. It is as though it is so novel that Facebook has some apps and features like the Wall, Videos, Photos, …. and that they may overlap with applications that others have developed. This is not new guys:

  • Microsoft: They are ruthless right? They keep expanding out with applications on Windows. From MS Paint to an office suite to a TCP/IP stack to a Media Player to a web browser … fast forward … anti-virus and firewall software.
  • Apple: iLife + iWork. How dare they.
  • Google: Various Google Gadgets (e.g. gmail viewer)
  • Facebook: As said, a better Wall. Friend grouping. Videos. Photos.

So, everyone does it. I think the key points from the platform vendors side are:

  1. Make your applications true showcases. Apple sets the standard on its platform, and developers often compare their work to these showcases. If you are going to do apps on your own platform, make they ridiculously good
  2. Share knowledge on how you produce your fantastic applications
  3. Be transparent. I love how the Google Gears team was allowed to follow their vision on getting the code out when it was a baby. You could imagine that some of the Google teams, that could maybe be interested in taking their apps offline, would like to have worked on a private Gears platform…. and only announce it when all of the apps are ready. Instead we did the opposite, and the side effect is that Zoho has an opportunity to be the first online word processor to work offline wth Gears (not Google Docs). This is actually a good thing for Google Gears itself, as it isn’t meant to be about Google. We wanted to make sure there was a level playing field.
  4. Try to be good to the community by letting people know when you stomp on them. Microsoft at least told Norton and co. that they were getting into the security business natively. This allows your developers to choose whether to directly compete, or head off to a niche

The “niche” is key. You KNOW that Facebook is going to make its profile page better, which will include a better wall (sorry SuperWall) or better friend grouping (sorry TopFriends). As a third party developer you know the risk.

If you go into something generic, and it becomes popular, expect the platform vendor to do something in that space.

If you go into something niche, then you are probably safer. If you make a killer application for lawyers, then you will probably be safe.

If you decide that you want to target everyone and build a generic app, you just have to know that either:

  • a) If successful you will get competition from others, potentially including the platform
  • b) At least have a bit of time to make money on top

In general, make a product rather than a feature.

But wait, isn’t Facebook a little different? The virality model is such that the big guys like RockMe have an advantage in that they can watch out for anything popular, and quickly jump in and use their overall platform to promote their quickly-hacked-up solution.

You come up with the next cool mini-app, and then they clone it and advertise the hell out of their clone on their most popular applications.

That is life. It isn’t new. Big players are able to do that on the other platforms too.

Again, you have to assume competition and use your unique assets to counter it… or sell out to the big guy! :)

Oct 04

Identity Matcher: Import social graphes

Facebook, Ruby, Tech No Comments »

My favourite talk at the FOWA was Matt Biddulph talking about his experience with Dopplr which is a fantastic little service (and I was shown today that the colors change EVEN on the favicon.ico when you are in a different location!!!).

Matt’s talk was high level, but he managed to keep it interesting and it wasn’t about pimping Dopplr.

He also released identity matcher, a new open source project that allows you to import social graphs! Now we can all work together on this instead of reinventing the wheel:

This code, extracted from the Rails codebase of dopplr.com, extends your User model with methods to pull in social network information from sites such as GMail, Twitter, Flickr, Facebook and any site supporting appropriate Microformats.

This is an alpha-quality plugin. It was extracted from our codebase at the start of October 2007 and may still contain dopplr-specific code (although we tried to avoid that).

The code is just one file with a few includes and you can now matches_identities in your Rails model. Of course, you can abstract out the Rails stuff and just have a library to do the matching. Very nice indeed.

Now I just want the option to say “if someone shares their stuff with me on Dopplr, and they are friends with me via another service (e.g. Facebook, Twitter) just go ahead and share back automatically!”

.

Oct 03

Steve Souders on Web Performance

Tech No Comments »

Steve gave an interesting talk that walked through the 14 rules that he has come up with, and documented in his new book.

The importance of Frontend Performance

Steve showed a set of test results that ran against the Yahoo! front page.

For an empty cache, the backend work took only 5% of the time, and thus the front end was taking 95%. This shocked them.

Well, what about a primed cache? It was still 88%.

Hmm, so maybe this was just for Yahoo! :/ No, he looked at the top ten URLs

Steve looked at the top ten urls and they were all high. The only one that was below 80% was Google, with 64% on a primed cache.

Steve’s Golden Rules became:

80-90% is spent on the front end part

Since the cache matters, Steve then did a browser cache experiment. He added an image to a page and calculated the % of users with an empty cache, and the % of page views too.

Over time ~50% users, ~20% page views. Wow, so you thought that caching was great…. but you get a lot of hits with an empty cache!

14 Rules

  1. Make fewer HTTP requests
  2. Use a CDN
  3. Add an Expires header
  4. Gzip components
  5. Put stylesheets at the top
  6. Move scripts to the bottom
  7. avoid CSS expressions (evaluated up to 10 thousand times!)
  8. Make JS and CSS external (Downloaded / evaluated multiple times)
  9. Reduce DNS lookups
  10. Minify JS
  11. Avoid redirects
  12. Remove duplicate scripts
  13. Configure ETags
  14. Make Ajax cacheable (XHR to download email addresses for autocomplete)

He also said to:

  • Split static content across multiple domains
  • Reduce the size of cookies
  • Host static content on a different domain (no cookie payload as well as split)
  • Minify CSS
  • Avoid iframes (blank iframe 40/50ms) onload won’t fire until iframe returns!

Case Studies

Steve’s team was able to make Yahoo! Search 50% faster by simply:

  • Move js to onload
  • Remove bottom tabs
  • Avoid redirects
  • Image sprites
  • Host js on cdn
  • Combine js files

Finally, Steve demonstrated YSlow and showed how the net panel in Firebug is tricky and has people thinking that requests keep going out even though it is just getting the data from the cache.

The new version of YSlow that is coming out at the end of the week will have a fix for this. To test, fire up Live Http headers and see what is happening.

Oct 03

Future of Web Apps: Taking your web application offline

Tech No Comments »

I just got done with my presentation on Google Gears at the Future of Web Apps. It has been a great show so far, and fun to be in London to talk to my homies.

I put my slides up on slideshare, so you can flick through them… although you don’t get all of the fun inline movies…. especially the DHH talking face!

Oct 02

Madison startup does good: Jellyfish acquired by Microsoft

Tech 1 Comment »

I have some friends who work at Jellyfish, an online ad startup in Madison. Well, although they are bad poker players, they must be good at negotiating as they just sold JellyFish to Microsoft:

We want to welcome some new folks to the Live Search family – we recently purchased a company called Jellyfish.com, based in Madison, Wisconsin.  Jellyfish has done some really innovative work in comparative shopping engines. We think the technology has some interesting potential applications as we continue to invest heavily in shopping and commerce as a key component of Live Search.  Stay tuned for more great stuff from our new colleagues in Madison!

Interesting to see that announcement from the Live Search team… hmmm :)

The other WSJ (the WISCONSIN State Journal) has more details.

Congrats guys. Great to see a Rails + Madison startup do so well!