01/23/2010

a year of firsts

Category musings
For the past few years, I've posted some form of "annual review" on, or in the vicinity of, the last day of the calendar year. This year when the proverbial ball dropped I was simply far too busy to engage in such reflection.

Over the past week, while at Lotusphere 2010, I lost count of how many times some remark from a colleague prompted from me some variation upon, "this really has been an amazing year," prompting from them, in turn, a reminder that the year to which I was most likely referring was already over and a new year had begun. And finally it struck me: in many ways, my year now starts and ends at Lotusphere, just as believers of many faiths choose to treat a worship service as the close of one week and the beginning of another. Hence, I thought it fitting to spend some time over the course of the conference to reflect upon the experiences that I considered significant since the last time I'd wandered those halls, sat in sessions, and, yes, "serenaded" my fellow Yellowbleeders at Kimono's.

It took little time for a pattern to emerge: while the past year has, once again, been marked by personal growth and loss, professional development and challenges, this has been - more so than any other year I can recall - a year of many "firsts" for me. Here are just a few that came to mind:

  • I lost a parent. Before you ask, my mother and father are both, thankfully, still very much alive. My father-in-law, however, passed away in early November. I haven't spoken of this publicly before now for numerous reasons, but the experience of losing him had a profound impact on how I view my gratitude for the relationship I have with my own parents, my goals for the kind of person I hope to someday become, and how precious is each moment we have with the people who shape our lives, so I thought it fitting in this context to mention that experience - one that none of us can ever truly prepare for but must all eventually face.
  • I co-authored an article for THE VIEW with Nathan. (aside: I always feel a little silly capitalizing the entire name, since Internet etiquette considers that to be shouting... on the other hand, later in this list you'll see why, over time, I'm likely to become more accustomed to capitalizing a brand name in my blog posts.) Possibly because the article was about XPages, but more likely because of my co-author's reputation, the article received so much attention that we were asked to submit a follow-up article, which was published just a few weeks after the first.
  • At MWLUG in August, I had the chance to give a presentation about XPages. Not only was this the first time I'd presented a Domino-related session at a Lotus User Group, this was my first experience with public speaking since doing end user Lotus Notes training for new employees (typically 5 - 10 at a time in a small conference room) many years ago. My pacing was not what I'd hoped, so I blew through the last half of the content at approximately Mach 3 and still went over the allotted time by about 10 minutes... but it was a great experience nonetheless, and several people expressed to me afterwards that they found the session to be very informative.
  • I was given the honor of co-presenting (with Stephan Wissel) a session at Lotusphere. After all I'd heard about how much work goes into preparing these sessions, and judging by my experience at MWLUG, I had braced myself for a daunting task. With Stephan's help, however, the preparation was far easier than I'd anticipated, and - even better - the session itself was actually fun. I can't deny it was still a bit overwhelming presenting to several hundred attendees, but their response - both throughout the session and in the days since - made it clear that we accomplished the goals we'd established for the session despite being unable, in the short time we had, to demonstrate a couple of nuances we'd hoped to include.
  • I experienced an acquisition that actually proved mutually beneficial. As many of you are now aware, as of October 1, Lotus 911 officially became GROUP Experts, a division of GROUP Business Software. Unlike similar transitions I've experienced at previous employers, this acquisition truly was a merger: with practically no overlap in specialty but an identical vision and passion, our own branding has changed but our day-to-day operations have not. GROUP as a whole gained a services division, and we gained an incredible sales team with proven experience in successfully bringing products to market. In the past, we've written some pretty cool applications that weren't directly commissioned by a client, but until now, those have typically been proofs of concept to demonstrate our capabilities as a consulting services provider or to familiarize ourselves with (and, in many cases, push the boundaries of) some aspect of the Lotus Notes/Domino platform. But now that we're part of GROUP, those of you who would welcome such an occurrence will be excited to know that you will likely soon be able to actually purchase something that came out of our development lab. Which reminds me...
  • A patent application was recently filed listing me as an inventor. I'm sure you'll be seeing many mentions on Nathan's blog and elsewhere about our Innova framework, but here's the elevator pitch: it allows you to create entire XPage applications directly from the Notes client... no Designer, no Eclipse, no IDE of any kind. You simply create documents that define the structure of the database (such as the records that comprise it and the relationship between those records) and the application interface, and Innova handles the rest. </elevator> Other frameworks in Domino have used a similar conceptual approach, so why is this patent-worthy? Because - in addition to some crazy-cool interface rendering techniques - Innova uses a mutable 3NF data structure, providing not only atypical storage and performance efficiencies, but also allowing on-the-glass interaction with data relationships in a more intuitive fashion than is usually seen in "hard-coded" Domino applications... not to mention in a context where the application itself (be it CRM, help desk, ERP...) is defined entirely as data records with no modification to the underlying software at all. To our knowledge, not only has 3NF never been used this way in any XPage framework, this has never before been done in Domino, period. Again, I'm sure Nathan will tell you much more about this in the weeks to come, but I couldn't help bragging at least a little bit about how cool Innova is.
  • GROUP won the CTO Lotus Award for EMEA this year. This is the first time a project I was directly involved with resulted in a Lotus Award. However, something I wrote on my own (albeit with some CSS assistance from our graphic designer after the initial release) was also named as a finalist for a Lotus Award in another category.
  • On Tuesday, I had an opportunity to give a one-on-one demo of Innova for Alistair Rennie at his request. While this is not the first conversation I've had with a General Manager of Lotus, it certainly was the first time a GM walked straight across the Product Showcase floor to our booth specifically to ask me to show him something I'd helped to both architect and implement. Because Alistair is such a cool guy to begin with, after my short demonstration of what Innova currently looks like and description of our plans for what it will continue to become, he stuck around for a while and we just chatted about XPages and the state of Lotus in general. Then, after he wished me a good show and started to walk away, a "booth babe" from another vendor, who had been standing behind him throughout our conversation, asked him to come to their booth to watch a demo of one of their offerings... and he did. Alistair is (as jonvon would put it) unassailably cool. In fact, the best way I can think of to encapsulate how wonderful my week was is to stipulate that my conversation with Alistair was not the single biggest highlight of the conference... ANY other year (thus far), it absolutely would have been.
So... uh... sorry that turned into such a sales pitch. I really didn't mean for it to be. But after years of trying to keep this blog purely technical and not a blatant marketing channel for my employer, I can't honestly reflect on everything that's happened over the past year without expressing how excited I am about what we are now jointly capable of and the role I have the good fortune to play in that. It's thrilling and overwhelming and I can't wait to see what new opportunities this year will bring (if the latest code drop of 8.5.2 that was just delivered to design partners and managed beta participants is any indication, it's gonna be cuh-ray-zee... and y'all know I don't use that word lightly).

As I return to the "normal" life that connects one Lotusphere to another, and envision the possibilities for what this year might contain, I want to thank the people in my life that make it so fulfilling, exciting, and hopeful, especially my wife, my family, and the literally hundreds of folks who pleasantly blur the distinction between colleague and friend. I'm incredibly fortunate to have you in my life. May 2010 bring you countless blessings and opportunities to bless others... you've certainly blessed me.

01/06/2010

sessions I'll be attending at Lotusphere

Category lotusphere
Now that the full session agenda for Lotusphere 2010 has been posted, I've actually planned ahead this year and decided in advance what sessions I want to attend. While this may of course change, please consider this both my recommendation for each of the sessions listed and a roadmap to find me at the conference should you (for whatever reason) wish to do so... of course, outside of the timeslots listed below, I'll likely either be hurriedly cramming for my own session or holed up in Kimono's.

Sunday

8am-10am JMP101 IBM Lotus Domino Designer 101
1:30pm-3:30pm SHOW112 How to Build an XPages Application from Start to Finish
Monday

8am-10am OGS101 Lotusphere Opening General Session
11am-12pm AD101 IBM Lotus Domino Designer: Full Speed Ahead!
1pm-2pm AD206 Filthy Rich User Interfaces with IBM Lotus Notes 8.5
2:15pm-3:15pm AD109 XPages Performance and Scalability
3:45pm-4:45pm AD110 Unleash the Power of Xpages
5pm-6pm AD205 Developing and Deploying Secure Java Applications for the IBM Lotus Notes Client
6:15pm-7:15pm BOF201 Lotus Domino Designer
Tuesday

8:30am-9:30am BP103 "Top Chefs" Share Recipes for Avoiding Everyday Server Disasters
10am-11am KEY101 The Lotus Application Development Strategy
11:15am-12:15pm BP108 Worst Practices 4.0: "Orlando, We Have a Problem"
1:30pm-2:30pm BP207 Make Your XPage Apps "Pop!" with CSS and Themes
3:30pm-4:30pm AD108 XPages in the IBM Lotus Notes Client - A Deep Dive!
4:45pm-5:45pm AD107 Enhance Your Existing Applications with XPages
6pm-7:30pm GEEK101 SpeedGeeking!
Wednesday

7am-8am BOF204 Object-Orientated Programming in IBM Lotus Notes and Domino
8:30am-9:30am BP201 Meet "The Grumpies": Eschew Obfuscation!
10am-11am AD102 Extreme Makeover -- LotusScript and Java Editor Edition
11:15am-12:15pm AD106 XPages Just Keep Getting Better
1:30pm-2:30pm AD111* Harnessing the Power of Server-Side JavaScript and Other Advanced XPage Techniques
3pm-4pm AD104 DXL Re-invigorated
4:15pm-5:15pm INV107 An Oral History of IBM Lotus Notes: The First Twenty Years
5:45pm-6:45pm BOF203 Movin' On Up With IBM Lotus Xpages
Thursday

7am-8am BOF206 Application Architecture Based on Composite Application Technology
8:30am-9:30am AD105 Futures in IBM Lotus Domino APIs
10am-11am IDOL102 Lotusphere Idol Winner Session
11:30am-12:30pm GURU101 GURUpalooza!
1:45pm-245pm ASK101 Ask the Developers
3:15pm-4:30pm CSG101 Closing General Session

* While the rest of these may be subject to change, I'm quite certain I'll be attending this one.

12/24/2009

portable Lotus Symphony installation

Category symphony
As a followup to my last post, here's something that I somehow completely missed a few weeks ago (despite posts about if from Scott Treggiari, Mitch Cohen, Stuart McIntyre, and Arne Nielsen):

IBM, VMware and Keepod products family manufacturer NSEC, today announced that they have partnered to offer IBM's Lotus Symphony productivity software suite on Keepod devices for the first time.


What's a Keepod? Apparently, it's a roughly credit-card-sized portable hard drive with a built-in USB connector, designed to be the ultimate portable application/data container: plug it into any PC, Mac, or Linux machine and, not only can you access your portable data, but you can run a pretty comprehensive set of applications directly from the device, regardless of the host OS, without leaving a trace of those applications (or your data) on that host. Sounds pretty handy.

Since they range in price from 29 EUR ($42) for a 2GB card to 69 EUR ($101) for a 16GB (NOTE: these prices include shipping, so you might pay a bit less, especially if you live in Europe), I'm not quite intrigued enough yet to try one of these, given that I can snag a 16GB Micro SD card (with SD adapter) on NewEgg for about $40 or a 32GB thumb drive for about $65... then again, I've found both of those form factors prone to occasional disappearance, so I can certainly see the appeal of being able to store portable data in my wallet, not to mention the convenience of being able to access that data via their associated applications, even if the computer I'm currently at doesn't have those applications installed.

So I may pick one up at some point. If any of you have already tried one, I'd be curious to hear whether you would consider it a worthwhile purchase. In the meantime, the reason I finally noticed that they've packaged Symphony to run on these devices is that, motivated partly by the comment thread on my last post, I decided to refresh my memory regarding how many clicks it actually does take to download Symphony from its primary location (spoiler: despite already having an IBM ID, the total click count was 13), and noticed the Keepod press release. Intrigued, I checked out the site, and noticed that you don't actually have to own a Keepod to run their portable build of Symphony. After creating an account (which, admittedly, you do have to create in order to download Symphony from their site... but you also have to create one to even place an order for a Keepod, so by the time a device owner would come back to the site to download portable applications, they would already have an account), I went back to the Symphony page on their site, then started counting the clicks it took to actually download the software.

4.

Yes, you read that correctly: it takes more than 3 times as much clicking on a site IBM directly controls to get the software they're giving away for free as it does to obtain a custom distribution of the same free software on a partner's site. Naturally, there's still some teasing going on:

  1. The first click is a link labeled "Free Download"... sounds like that's a direct link to the ZIP file, yeah? Nope.
  2. Second click is the checkbox indicating acceptance of the license agreement (add an optional click to actually read the license agreement; I didn't... naughty user)
  3. Third click is a link labeled "Download". This even has a title attribute of "Download IBM Lotus Symphony 1.3". This will definitely be the ZIP file. Uh... nope.
  4. Fourth click is on a link labeled "Download IBM Lotus Symphony 1.3 Now!" (same hover text as the previous link). This must be the real download link. I mean, sure, the very first link had an animated down arrow graphic (this one, to be precise: ), but this one's even bigger, even though it's not animated. And they used an exclamation point in the link text... yeah, I'm sure they mean it this time.


And apparently they did, 'cause that's when the save prompt actually showed up and I finally got to download the ZIP. But even with all that teasing, it never stopped being immediately obvious how to proceed to the next step in the teasing. I didn't have to choose an operating system (maybe because on the Keepod it doesn't matter... or maybe because HTTP request headers tell the server what operating system you use, and some web servers actually pay attention to that), I didn't have to choose a language (maybe they only support English in this build, or maybe they checked the request headers and assume I speak English), and I didn't have to choose between downloading via HTTP or the vendor's proprietary download manager. In other words, despite spending perhaps more time than the average end user manning a laptop, when I got to the real download link, even I was nearing an "ugh, never mind" conclusion... despite the download process being both less tedious and more obvious than IBM's. Which reminded me that the only reason I persevered through the 13-click process when I originally downloaded Symphony was that I'm a Yellowbleeder, and Yellowbleeders know that, if we just push through this sort of thing, good software awaits us on the other side. And we're used to it. The "Facebook Generation", on the whole, is not. Just sayin'.

In any case, I followed their installation instructions (extract the ZIP file, then move the extracted folder to the Keepod), except instead of moving it to a real Keepod, I just moved the folder to an external USB drive. The first time I launched it from the external drive, it popped up a MessageBox saying something like, "Hey, that's not a Keepod!"... and then Symphony opened just fine. After playing for a bit, I closed it. After starting this blog entry, I'd forgotten the wording of that error, so I launched it again to transcribe it word for word... except this time, no error, only Symphony. YMMV, but I'm quite stoked... both that IBM decided to bundle Symphony this way, and that I can run it portably even though I don't own one of those devices. In fact, on second thought, maybe I will get a Keepod after all... it would be rather handy to have my slides in a form I can access from an alternate computer in case this one unexpectedly dies on me at the conference... as long as my co-presenter can still access all of our code demos, of course.

12/22/2009

suggest Lotus Symphony and Domino Designer to Ninite

Category grassroots
Earlier today, Carl Tyler posted about Ninite, a single site to go to for downloading a bunch of applications at once. For example, when setting up a brand new machine, you could check off Firefox, Pidgin, VLC, GIMP, OpenOffice, AVG, 7-Zip, and so on, and then click "Get Installer" and get a single executable that installs everything you need except your fringe applications... for example, for many of you, that includes Domino Designer.

But Designer is now free. And Lotus Symphony has been since its original release. At the bottom of Ninite's home page, they include a form to "Suggest an app". I've already suggested both Designer and Symphony, and am requesting that all of you do the same. Not only would it be quite convenient for us to include those apps in a bulk install the next time we need to outfit a new machine, wouldn't it be great if all of Ninite's visitors saw free Lotus products, treated as true peers to software they've already heard of, offered in a context where those visitors don't have to click through 37 pages on IBM's site just to download the software?

12/13/2009

how to implement the Konami easter egg in JavaScript

Category javascript
Here's a little weekend fun: a while back, word started spreading that Facebook had added an easter egg based on the old Konami code:



Specifically, they added a lens flare effect that only shows up while scrolling, and only after the above keystrokes have been entered in sequence, followed by the Enter key. For those who don't know (and haven't already clicked the link above to find out), this sequence was often used in old Konami video games to unlock something magical (extra lives, invincibility, etc.), and was so widely known that it's wormed its way into numerous other facets of popular culture.

Enabling this in JavaScript is actually easier than you might expect. Here's one possible implementation approach you could add to your own script:

var konami = (function(){
 var sequence = [38, 38, 40, 40, 37, 39, 37, 39, 66, 65, 13];
 var progressIndex = 0;
 var easterEgg = function(){
  /*
   * Replace with your own easter egg code:
   */

  alert("KONAMI'ED!!!");
 };
 return {
  interceptKey: function(thisEvent){
   var keyCode;
   if (window.event) {
    keyCode = thisEvent.keyCode;
   }
   else {
    if (thisEvent.which) {
     keyCode = thisEvent.which;
    }
   }
   if (keyCode == sequence[progressIndex]) {
    progressIndex++;
    if (progressIndex == sequence.length) {
     this.trigger();
    }
   }
   else {
    this.reset();
   }
  },
  reset: function(){
   progressIndex = 0;
  },
  trigger: function(){
   easterEgg();
   this.reset();
  }
 }
}());


Then just bind the interceptKey method to the onkeyup event of whatever element you want to contain the easter egg, e.g.:

<body onkeyup="konami.interceptKey(event)">


NOTE: In case it's not glaringly obvious, this only applies to client JavaScript. For all you XPagers out there, this wouldn't work as a server-side JavaScript function. Not only does SSJS not have a window object, the event model is entirely different from that of browser-based JavaScript.

12/04/2009

someday I will be this cool too

Category humor

11/17/2009

I will be speaking at Lotusphere

Category lotusphere
As most of you have by no doubt noticed, the official responses to all abstracts submitted for Lotusphere 2010 (except, I'm assuming, for the BOFs, if those will be selected based on attendee voting like last year) have been sent, and among them was confirmation that I will be co-presenting with Stephan Wissel the following session:

Harnessing the power of Server-side JavaScript and other advanced XPage techniques

XPages have ushered in a new era for application development on the IBM Lotus Domino platform. This session will take you beneath the surface of XPages and into the inner workings of server-side JavaScript, the language that allows you to easily add truly advanced features to your applications. By the end of this deep dive session, you will know how to use server-side JavaScript in the following ways: create events that dynamically manipulate interface components based on user interaction; use scope caching to improve performance and usability and leverage closures and other design patterns to create reusable object-oriented server-side JavaScript. You will also learn how to make your XPages more powerful with "managed beans" and other Java classes as well as create advanced reusable components by passing Java and server-side JavaScript objects to custom controls.

11/15/2009

inheritance in JavaScript

Category javascript
First, a brief historical recap:
  • Back in January, I admitted my man-crush on SSJS, and mentioned that IBM's implementation of it in XPages supports closures.
  • In February, I wrote a sprawling essay about what closures bring to JavaScript, and why I think they're so useful.
  • In June, I posited that SSJS isn't really JavaScript, explained what I felt to be the most pertinent differences, and promised to post my preferred approach to inheritance in JavaScript... but (until now) never did.
  • A few weeks ago, Peter Presnell sparked a fascinating discussion about closures and the prototype property, and what role each might serve in maintaining an object inheritance hierarchy in JavaScript. For some reason I can't seem to find that post on his blog at the moment, which is a shame, because it really was a great discussion. Nonetheless, it reminded me that I had been meaning to post something about inheritance, and ever since I've just been waiting to find enough time to do so.
With that out of the way, let's dive right in with a very basic example of a constructor function that demonstrates closure:

function Widget(widgetName){
 var privateWidgetId = 1;
 return {
  getName: function(){
   return widgetName;
  },
  getPrivate: function(){
   return privateWidgetId;
  }
 };
}


When called (either with or without the new keyword), this Widget function returns an object. This object has two methods (getName and getPrivate). Each method demonstrates its access to a private variable via closure: getName has access to widgetName because a function's arguments are bound by closure to an object returned from that function; similarly, getPrivate has access to privateWidgetId because variables defined within a function are bound by closure to an object returned from that function.

So this is one model available to us for creating classes within JavaScript (including SSJS): we create a function that behaves as the constructor for any class instances, and within the constructor we create an object that serves as the class instance and return it. Any properties or methods defined within that return object are public, any properties or methods defined outside of the return object but still inside the constructor are private: they can be accessed and modified by the object's public methods but not from "outside" the object. Very handy... but what if we want to "subclass" this class?

function Hoozit(hoozitName, hoozitType){
 var privateHoozitId = 2;
 // call parent class constructor:
 var thisHoozit = Widget(hoozitName);
 // add a new method:
 thisHoozit.getType = function(){
  return hoozitType;
 };
 // override a parent method:
 thisHoozit.getPrivate = function(){
  return privateHoozitId;
 };
 return thisHoozit;
}


This example illustrates one approach (though not quite my favorite) to inheritance: we create a constructor function that defines a return object by calling the constructor of a parent class. Optionally, we can also add new private members inside the constructor for the child class and extend the return object with new (or overridden) public members. This model is sometimes called "parasitic" inheritance. Perhaps worthy of note is that any private members introduced within this constructor are not accessible to methods defined within the parent class. This might already be obvious, but just to drive the point home: if I instantiate a Hoozit, its getName method cannot access privateHoozitId because that method is added to the return object within the Widget constructor function, so that variable does not exist within that scope. Similarly, the newly introduced getType method does not have access to privateWidgetId because it is defined within a different function scope. Nathan tells me that this means these variables are actually considered "protected", not private. I guess that's a good thing... just keep these scope issues in mind when you're defining your own classes.

Again, I like the above model, but it's not quite my favorite - my only criticism is that, when adding or overriding a large amount of public members to the descendant object, this just seems to require an excessive amount of duplicate object references. The good news is that we can add a handy helper function to streamline the descendant construction a bit but maintain the same result when each object is created:

var Inheritance = {
 extend: function(parentInstance, extensions){
  for (var thisExtension in extensions) {
   parentInstance[thisExtension] = extensions[thisExtension];
  }
  return parentInstance;
 }
};


There's not much going on here, but it packs a useful punch: simply put, we pass an instance of an object we want to extend (parentInstance) and an object consisting of what we want to add (extensions). This function loops through all the public members of the extension object and adds them to the parent instance, then returns the modified object. So here's an example almost identical to the Hoozit, but with less syntactical duplication:

function Whatzit(whatzitName, whatzitType){
 var privateWhatzitId = 3;
 return Inheritance.extend(Hoozit(whatzitName, whatzitType), {
  getType: function(){
   return whatzitType;
  },
  getPrivate: function(){
   return privateWhatzitId;
  }
 });
}


You may have noticed that it's the exact same number of lines of code, but there's one subtle difference: we don't have to name the return object just so that we can refer to it over and over again when we stamp it with each additional member (and, again, the advantage of this really only becomes obvious when defining a large amount of members in any given extension layer). We simply define a single object with all of the members we want to add, then pass the result of the parent constructor and the extension object to Inheritance.extend, and out pops an object that has all of the public members of the parent class (except those we've overridden), each with access to any protected members defined within that parent constructor, as well as any new methods we've defined in this constructor, each potentially with access to its own set of protected members.

I've concocted a fancier inheritance model that uses function memoization (not to be confused with memorization) and a few other tricks to provide support for basic object reflection, "super" methods, and such. But what we've definitely learned over the past year is that, in the context of XPages, an application runs best when its code is structured such that each language used is being asked to do what it's best at (imagine that). Hence, for basic inheritance models, the above pattern is perfectly sufficient... but if you're ninja enough to be maintaining deep inheritance hierarchies and ancestry-sensitive conditional processing in your server-side code, you might as well just use Java.

11/14/2009

Upcoming book review: Ext JS 3.0 Cookbook

Category books
Packt Publishing, the same publisher who approached Marie Scott about writing a book about Sametime, has also asked me to review one of their recently released titles, Ext JS 3.0 Cookbook. Long time readers of this blog know that I'm a huge fan of the Ext JavaScript framework, but until now I've never actually read a book specifically about the framework... everything I've learned about it has been gleaned from their API documentation and "the Google". But the reviewer copy arrived yesterday and, having only browsed the table of contents so far, I can't wait to dig in. In particular, I'm hoping to pick up some tricks for writing plugins and class extensions - after all, while the library on its own allows a developer to quickly crank out beautiful web applications, the true power of the framework lies in its inherent extensibility.

If, like me, you're a fan of the framework - or think you might become one if you had a good resource to bring you up to speed on getting the most out of it - let me know in the comments or via email if there are any specific items you'd like me to keep an eye out for in the book's content and report on.

11/11/2009

Another VIEW article about XPages is live

Category xpages
The sequel to our previous article has just been published. This one's massive... and yet is just the beginning of the story, so hopefully we'll be able to keep cranking out more sequels.

But I feel reasonably confident there won't be any prequels... at least, none featuring annoying CGI aliens.

Contact Me

Hire Me

What the Quote?

"Burn, my pretty, burn!!"

Myndi Birdsong

"We thought we'd be brilliant and just copy your code, and your code said, 'Yeah, you're funny'."

Monica Ferrante

"No, Doctor, it's Jesus spit... leave it there!"

Laura Tripcony

Elsewhere

Assorted Linkage

Apparel

Lotus Rocks

I write the code that makes the young girls cry

ClustrMap