Saturnboy
 3.24

Adobe killed this blog when they killed Flex. Well I guess Flex is not all the way killed, more like stumbing around with a knife in the neck. So what’s next? I’m breaking out my crystal ball to see what the future holds…

Flex’s Future

With a singular, spectacular blog post Adobe eviscerated Flex and any hope of ever using Flex again. Thanks Adobe! Flex is dead dead, not just a little dead. No one in their right mind should ever start development on a Flex app. Period. I’ll make a nice pull quote just so you understand my opinion on the matter:

“Flex is dead!” — Justin

Spoon is not dead, nor is the open sourcing of Flex on Apache (as Apache Flex), but alas, no amount of open sourcing or intense community love will change things in this particular case. But since everyone is in love with zombies nowadays, I’m sure there are plenty of people and companies out there that see the simulacrum of life in Flex and call it life.

Flex Mobile’s Future (aka Adobe AIR)

Flex Mobile, aka Adobe’s cross-compiler that take AS3 code as input and outputs iOS binaries (as a fully formed IPA) and also Android binaries (APK), is decidedly not dead yet. But the most unfortunate part is that it certainly should be. In general, cross-platform mobile solutions (particularly Flex Mobile) really suffer when the make contact with reality (a real app with real functionality and real users). Sure they can be made to look pretty, sure they make great demos, and sure they lower the initial bar for developer. But you will never ever have as much control as you will when you go native. It is still the case today (March 24 2013) that the only good answer is go native, and write it twice. Twice being once for iOS and once for Android.

Flash Player’s Future

Banned from iOS, unsupported by Chrome Mobile, officially discontinued on all interesting mobile platforms, click to install on desktop Safari, no 64-bit flash player on linux, and now click to run on Firefox. Hmmm…I wonder what is next? How about dead. Yep, my crystal ball says that the flash player itself is on the gallows.

Let’s think about how Adobe makes money from Flash (this is coming from a developer that actually knows nothing)… From what I can tell, they sell really nice authoring tools, they sell some server-side tools (mainly Flash MediaServer, aka FMS), and they have a bunch of game stuff. I love games and I love watching video online, but does the flash-related parts of any of those mostly desktop businesses look healthy and awesome to you, especially with the rapid growth of mobile and tablets?

Adobe’s Future

Adobe will be certainly fine (but again, I’m just some guy who writes code). I always think of them as a tools company. Photoshop is a sweet piece of software (Photoshop 1.0 source is now available in the Computer History Museum). But the whole platform play that is Flash and Flex? I always felt that was Macromedia’s idea, and Adobe just went along for the ride after the aquistion. That ride isn’t much fun anymore.

And who just left? Kevin Lynch is moving to Apple (aka they guy who was chief software architect at Macromedia).

My Future

I know I’ll be doing some more speaking. And I’m trying hard to figure out how to create more teaching opportunities. I love teaching, but for whatever reason it’s hard to find the right teaching opportunity with the right students. I’ll always be learning new feameworks and languages, because that is a constant. But writing more content for this blog, probably not so much…

I’m working on couple of app ideas right now, and still writing lots of code, but only a little bit of that work will make it all the way to this blog. Watch my my github for some new projects.


 8.16

Sometimes I feel like I write more test code than real code. For unit tests on iOS our stack is OCHamcrest, OCMock, and GHUnit. For functional tests, there’s nothing better than FoneMonkey. For this post, I’m going to focus on OCHamcrest.

Hamcrest was born in the Java world as the matcher framework in jMock. It was quickly extracted into its own framework and has become somewhat of a monster in the testing world. It’s now included directly in JUnit (since v4.4), and has been ported to many languages (OCHamcrest in Objective-C, Hamcrest-AS3 in Actionscript, PyHamcrest in Python, etc.). Additionally, the matcher concept is generally useful, and Hamcrest is used is lots of different places (my favorite is collection filtering with Hamcrest in LambdaJ).

When writing unit tests, OCHamcrest offers lots of advantages over the vanilla SenTest assertions. First, there’s a ton of matchers that really make life easy, especially when testing collections like NSArray. Second, OCHamcrest matchers are very readable in code, almost self-documenting. Lastly, OCHamcrest automatically provides excellent failure messages when actual is not equal to expected.

Matching Strings

Some string matching examples:

  • is – match the complete string
  • startsWith – match the beginning of a string
  • endsWith – match the end of a string
  • containsString – match part of the string
  • equalTo – match the complete string
  • equalToIgnoringCase – match the complete string but ignore case
  • equalToIgnoringWhiteSpace – match the complete string but ignore extra whitespace (new line, tab, or double spaces)
NSString *s = @"FooBar";
 
assertThat(s, is(@"FooBar"));
 
assertThat(s, startsWith(@"Foo"));
assertThat(s, endsWith(@"Bar"));
assertThat(s, containsString(@"oo"));
assertThat(s, equalToIgnoringCase(@"foobar"));
 
assertThat(@" X \n  Y \t\t  Z \n", equalToIgnoringWhiteSpace(@"X Y Z"));

NOTE: Technically, is isn’t really a matcher, it’s a matcher decorator that implicity converts to the equalTo matcher. [thanks Jon!]

Combining Matchers

You can combine multiple matchers with:

  • allOf – AND together all matchers
  • anyOf – OR togehter all matches
NSString *s = @"FooBar";
 
assertThat(s, allOf(startsWith(@"Foo"), endsWith(@"Bar"), nil));
assertThat(s, anyOf(startsWith(@"Foo"), startsWith(@"Bar"), nil));
assertThat(s, anyOf(endsWith(@"Foo"), endsWith(@"Bar"), nil));

NOTE: The list of matchers must be nil terminated.

You can invert a matcher, or multiple matchers, with:

  • isNot – negate the matcher
NSString *s = @"FooBar";
 
assertThat(s, isNot(@"foo"));
assertThat(s, isNot(endsWith(@"Baz")));
assertThat(s, isNot(allOf(startsWith(@"Baz"), endsWith(@"Baz"), nil)));
assertThat(s, isNot(anyOf(startsWith(@"Baz"), startsWith(@"Baz"), nil)));

Matching nil

You can match nil with:

  • nilValue() – stands in for nil
  • notNilValue() – stands in for !nil
NSObject *o = nil;
assertThat(o, nilValue());
 
NSString *s = @"FooBar";
assertThat(s, notNilValue());

Matching Classes

You can match an instance’s class with:

  • instanceOf – match the class
NSString *s = @"FooBar";
assertThat(s, instanceOf([NSString class]));

Matching Numbers

One of the great pains of Objective-C is typing numbers from primitive types to objects and back again. OCHamcrest has a variety of matchers the help make life easy.

  • assertThatInt – typed assert that expects an int (other types too: assertThatFloat, assertThatDouble, etc.)
  • equalToInt – typed equals that takes an int (other types too: equalToFloat, equalToDouble, equalToBool, etc.)
  • closeTo – match a number with a target number plus or minus a delta (both params are double)
  • lessThan – match a number less than the given number (param is NSNumber), also lessThanOrEqualTo
  • greaterThan – match a number greater than the given number (param is NSNumber), also greaterThanOrEqualTo
assertThatInt(5, equalToInt(5));
assertThatFloat(3.14, equalToFloat(3.14f));
assertThatBool( false, equalToBool(NO) );
 
NSNumber *i = [NSNumber numberWithInt:5];
assertThat(i, equalToInt(5));
assertThat(i, is([NSNumber numberWithInt:5]));
 
NSNumber *f = [NSNumber numberWithFloat:3.14f];
assertThat(f, equalToFloat(3.14f));
assertThat(f, is([NSNumber numberWithFloat:3.14f]));

The easiest cleanest approach is to use assertThatInt with equalToInt, the next best option is to use the vanilla assertThat with equalToInt, the most verbose option is to use NSNumber everywhere.

It’s easy to make rough number comparisons too:

NSNumber *f = [NSNumber numberWithFloat:3.14f];
assertThat(f, closeTo(3.0f, 0.25f));
 
assertThat(f, lessThan([NSNumber numberWithInt:4]));
assertThat(f, greaterThan([NSNumber numberWithInt:3]));

NOTE: It is a little weird, but closeTo takes double params, but everything else expects NSNumber params.

Numeric comparisons also work great on dates too:

NSDate *now = [NSDate date];
 
//now minus 1000 seconds
NSDate *beforeNow = [NSDate dateWithTimeIntervalSinceNow:-1000]; 
 
assertThat(now, greaterThan(beforeNow));

Matching Arrays

Easily the best part of OCHamcrest is its ability to match lists of objects. Array matchers are every powerful, but don’t forget to add the terminating nil to all lists.

  • hasItem – match if given item appears in the list
  • hasItems – match if all given items appear in the list (in any order)
  • contains – match exactly the entire array
  • containsInAnyOrder – match entire array, but in any order
  • hasCountOf – match the size of the array
  • empty – match an empty array

Here some basic array examples:

NSArray *a = [NSArray array];
 
assertThat(a, is(empty()));
assertThat(a, hasCountOf(0));

Here some hasItem examples:

NSArray *a = [NSArray arrayWithObjects:@"a", @"b", @"c", nil];
 
assertThat(a, hasItem(@"a"));
assertThat(a, isNot(hasItem(@"X")));
assertThat(a, hasItem(equalToIgnoringCase(@"A")));

The last matcher may look a little weird, but remember matchers expect a matcher as their input param, and only default to equalTo if none is given. Thus, the first matcher hasItem(@"a") can be rewritten as hasItem(equalTo(@"a")).

We repeat the above example, but this time using numbers in our NSArray. As you can see below, all the number matchers require us to explicitly use equalToInt everywhere:

NSArray *a = [NSArray arrayWithObjects:
    [NSNumber numberWithInt:2],
    [NSNumber numberWithInt:3],
    [NSNumber numberWithInt:5],
    nil];
 
assertThat(a, hasItem(equalToInt(2)));
assertThat(a, isNot(hasItem(equalToInt(13))));
 
assertThat(a, contains(equalToInt(2), equalToInt(3), equalToInt(5), nil));

Here are some more complex array matchers:

NSArray *a = [NSArray arrayWithObjects:@"a", @"b", @"c", nil];
 
assertThat(a, hasItems(@"b", @"a", nil));
 
assertThat(a, contains(@"a", @"b", @"c", nil));
assertThat(a, containsInAnyOrder(@"c", @"b", @"a", nil));
 
assertThat([a componentsJoinedByString:@","], is(@"a,b,c"));

And as I show in the last matcher, you can always dump to a string, and just match strings.

Matching Dictionaries

The dictionary matchers build on the array matchers:

  • hasKey – match a key
  • hasValue – match a value
  • hasEntry – match a key-value pair
  • hasEntries – match a list of k-v pairs
NSDictionary *d = [NSDictionary dictionaryWithObjectsAndKeys:
    @"valA", @"keyA", @"valB", @"keyB", @"valC", @"keyC", nil];
 
assertThat(d, hasCountOf(3));
assertThat(d, isNot(empty()));
 
assertThat(d, hasKey(@"keyA"));
assertThat(d, isNot(hasKey(@"keyX")));
 
assertThat(d, hasValue(@"valA"));
 
assertThat(d, hasEntry(@"keyA", @"valA"));
 
assertThat(d, hasEntries(@"keyA", @"valA", @"keyC", @"valC", nil));

Matcher Error Messages

When a matcher fails, you get a standardize error message of: Expected "foo", but was "bar". This default message is easy to modify by using the describedAs() matcher in place of the typical is() matcher.

NSString *s = @"bar";
 
assertThat(s, is(@"foo"));
//Expected "foo", but was "bar"
 
assertThat(s, describedAs(@"doh! this should be 'foo'", equalTo(@"foo"), nil));
//Expected doh! this should be 'foo', but was "bar"
 
assertThat(s, describedAs(@"doh! this should be foo, %0, %1", equalTo(@"foo"), @"baz", [NSNumber numberWithInt:42], nil));
//Expected doh! this should be foo, "baz", <42>, but was "bar"

NOTE: The argument list for describedAs() MUST end with nil or your tests will crash instantly with no useful error message.

Building a Custom Matcher

Writing your own custom matchers is relatively easy. Here’s an example of a matcher that matches the value of some property on an object:

#import <OCHamcrestIOS/OCHamcrestIOS.h>
#import <objc/objc-api.h>
 
@interface HasProperty : HCBaseMatcher {
    NSString *property;
    id<HCMatcher> valueMatcher;
}
 
+ (id) hasProperty:(NSString *)aProperty value:(id<HCMatcher>)aValueMatcher;
- (id) initWithProperty:(NSString *)aProperty value:(id<HCMatcher>)aValueMatcher;
 
@end
 
OBJC_EXPORT id<HCMatcher> hasProperty(NSString *property, id valueMatcher);

We extends HCBaseMatcher with our custom HasProperty class. We store the name of the property and a value matcher.

And the implementation:

#import "HasProperty.h"
#import <OCHamcrestIOS/HCDescription.h>
#import <OCHamcrestIOS/HCWrapInMatcher.h>
 
@implementation HasProperty
 
+ (id) hasProperty:(NSString *)aProperty value:(id<HCMatcher>)aValueMatcher {
    return [[[self alloc] initWithProperty:aProperty value:aValueMatcher] autorelease];
}
 
- (id) initWithProperty:(NSString *)aProperty value:(id<HCMatcher>)aValueMatcher {
    self = [super init];
    if (self != nil) {
        property = [aProperty copy];
        valueMatcher = [aValueMatcher retain];
    }
    return self;
}
 
- (void) dealloc {
    [property release];
    [valueMatcher release];
    [super dealloc];
}
 
- (BOOL)matches:(id)item {
    SEL propertyGetter = NSSelectorFromString(property);
 
    if ([item respondsToSelector:propertyGetter]) {
        if ([valueMatcher matches:[item performSelector:propertyGetter]])
            return YES;
    }
    return NO;
}
 
- (void) describeTo:(id<HCDescription>)description {
    [[[description appendText:[NSString stringWithFormat:@"an object with a property named '%@' with a value of {", property]]
        appendDescriptionOf:valueMatcher]
        appendText:@"}"];
}
@end
 
OBJC_EXPORT id<HCMatcher> hasProperty(NSString *property, id valueMatcher) {
    return [HasProperty hasProperty:property value:HCWrapInMatcher(valueMatcher)];
}

When we write a custom matcher, we must implement two methods, matches: to do the matching and describeTo: to provide feedback in case of match failure. In the above code, we first construct a selector from the given property name, then call the selector to get the actual property value, and finally check if actual matches the expected value (given by the valueMatcher).

Usage looks like this:

Person *p = [Person personWithFirstName:@"Joe" andLastname:@"Doe"];
 
assertThat(p, hasProperty(@"firstName", @"Joe"));

Or more importantly, we can now use our custom hasProperty matcher to match an arrays of objects:

NSArray *a = [NSArray arrayWithObjects:
    [Person personWithFirstName:@"Joe" andLastname:@"Doe"],
    [Person personWithFirstName:@"Joe" andLastname:@"Smith"],
    [Person personWithFirstName:@"Jane" andLastname:@"Allen"],
    nil];
 
assertThat(a, contains(
    hasProperty(@"firstName", @"Joe"),
    hasProperty(@"firstName", @"Joe"),
    hasProperty(@"firstName", @"Jane"),
    nil));

That’s it. Go forth and match.

UPDATE: I put the above hasProperty matcher into a pull request, and Jon Reid accepted it into OCHamcrest v1.6. He even wrote a nice post about it. Get the lastest OCHamcrest from github.

Links


 11.10
Life, Work
off

Back in December 2009, I had the amazing opportunity to teach for a whole week at Boulder Digital Works. BDW had just started that fall and the first class of 12 students1 was just getting started on a 60-week program.

Teaching was a ton of fun! We wrote some PHP code together and everyone was able to experience a little slice of the joy/hell that is development. We also did a lot of talking, which was fun, too. Since most of the class was non-technical, we didn’t geek out technology stuff, instead we spent a lot of time talking about meta-development. We talked about process, how developers think, how development fits into digital, what it means to actually build something real, etc. I found the whole thing to be very cathartic, having 12 smart people listening to me and participating in a fun discussion was just awesome.

To Be Great

One of the more interesting topics I discussed during class was how to be a great developer. I spend a lot of my time and brainpower thinking about what I can do to be better. That’s the mountaintop, but it’s always a long way away.

Some options for improvement are external but many are internal. For example, I’m always asking myself: Who knows something that I want to know? Who can I model? What can I borrow from those around me? Who can I mentor? Who should I ask to mentor me? What should I learn next? What should I forget? What can I read? Do I need to change my approach? What is today’s definition of value?

I always think my kung-fu is strong, but I’m not a master. So here’s a quick summary of the components of greatness as I see them today. Please feel free to ignore everything as you see fit.

Undying Hunger

If you have the hunger to be better then you’re always on the upslope. And this isn’t the vanilla-level hunger of “Hey, there’s more I could learn.” This is the full on, I’m-starving-more-is-never-enough hunger.

Once upon a time, back when I was a young lad of 10 or so, there was a series of TV commercials asking “Do you see the glass half full or half empty? Blah blah blah” After the hundredth viewing, I turned to my dad and said, “Dad, I see it as not enough! What does that make me?” And in his infinite wisdom, my dad replied, “That makes you a hog!” – which is an apt description without a doubt.

To be great, you need to be a hog.

Effort

Where hunger is internal, effort is external. Where hunger is mostly mental, effort is mostly physical. Effort is all about doing the work, expending the energy, getting really tired, waking up the next day, and doing it all over again. There is an obvious correlation between hunger and effort, but it’s definitely not one of causation. I fully control my decision to turn off the TV and pick up a book or sling some code.

The best thing about effort is that it’s just a switch, and I truly believe that. You can decide for yourself to turn it on any time you want. And with the right carrot, you can even flip the switch on for someone else. In my many years of coaching frisbee, I have repeatedly witnessed the switch being flipped. People can be taught to work hard, but it’s a heck of a lot easier when they are on a team of hard workers.

Unfortunately for me, the effort switch is more of a dimmer switch then an on-off switch. Burning bright and bringing a high level of effort for an extended period of time is hard. Motivation naturally ebbs and flows, and it’s challenging to maintain it. The best solution I’ve found to keep motivation high is to find a partner in crime. When there’s someone else to work with and compete against, it’s much easier to work hard. When I’m on my own, the only trick I have is to maximize variety, some days I work hard by doing some extra reading, some days it’s writing, some days it’s hacking, etc.

Effort in athletics is pretty easy to recognize. It’s spending time in the weight room during the off-season and out-hustling the other guy during the game. Effort in development is a little harder to figure out. Some of my typical outside-of-work adventures include reading (books, blogs), writing (this blog, the occasional article), speaking (local user group), teaching (BDW), and lots of side projects (contribute to open source, silly iPhone apps). There are also quite a few at-work hustle opportunities: you can give a brown bag talk, you can mentor others, and you can be a good mentee (basically you need find someone who knows something you want to know and beg them to teach you). But my favorite thing to do at work is find a like-minded peer and inspire them to work hard. You can do this by co-reading, co-writing, co-speaking, pair programming, etc.

To be great, you must work hard, constantly.

Creative & Pedantic

Unfortunately, no matter how hungry you are or how hard you work, you must also be creative to be a great programmer. Without creativity, you will forever be a cog in the machine. You might be the best cog ever, but you are a cog none the less. In Hackers & Painters2, Paul Graham talks a lot about the creative nature of software construction. Hackers, painters, architects, and writers are all makers in Paul’s world, and he spends quite a bit of prose comparing them.

I really like Paul’s take on the process of construction, and the different disciplines that practice it, but I try to take a more practical view. To me, creativity is nothing more than a measure of one’s ability to escape the box. I see it as just another skill that can be learned, practiced, and improved. Over time, you can learn to recognize the box faster and more fully. The more boxes you see, and solve, the more solution patterns you will remember. Eventually, you will develop a toolbox of escape tactics.

Again, it’s still not enough. No amount of creativity will help you actually implement a solution to any given box. There are a lot of details that are critically important to the construction process. Paul Graham writes, “In both painting and hacking there are some tasks that are terrifyingly ambitious, and others that are comfortingly routine.” Much like Superman has Bizarro, the creativity has pedanticalness (yes that’s a word). Many of the routine tasks in programming are routine to the point of being tedious.

I often liken the pedantic aspect of development to licking envelopes. It’s easy, but yuck! Who actually likes the taste of envelopes? If you don’t do it right, then don’t be surprised if you mail explodes while in transit. In my experience, there are a multitude of development tasks that taste a heck of a lot like envelope glue, but you don’t really have much choice. You can either do them the right way, or watch them explode.

To be great, you must embrace both the creative side of development and the pedantic side.

Ego

The number one, most important, aspect of greatness is ego. The truth of development is that not everything goes your way. And even when it does, it can be exceedingly difficult. For all those times when you are tumbling down the mountain, you need a certain amount of ego to arrest your fall and start upwards again. I’ve actually spent time and effort into stroking my own ego for this simple reason: you learn more when you lose than when you win.

I heard a pretty amazing football stat the other day. In 9 of the last 10 seasons in the NFL, at least one last place division team fought their way to first place in their division the following year. Most recently, the Saints finished 8-8 in 2008 and were in last place in the NFC South. In 2009, they won their division at 13-3 and went on to win the Superbowl. Why? Because you learn more when you lose than when you win. Fact.

The mud is a great teacher. But you must have a certain level of mental fortitude to climb out and get clean again. To me ego is just that – confidence in yourself and your ability, lack of fear, and a basic mental toughness. I’ll go willingly into the mud to take on a difficult problem or learn a new language (or maybe a new API instead of another language). And if I get thrown into the mud unexpectedly, which happens all the time in consulting, there won’t be any fear or panic.

Do I get dirty? Hell yes. Does it stink? Of course. Having confidence in yourself and believing in your abilities doesn’t magically transform the metaphoric mud into flowers, it’s still mud.

To be great, you must be confident and fearless.

Conclusion

It’s hard to know what you don’t know. In fact, it’s really damn hard. But I don’t find it discouraging in the least to know the code I write today is going to suck compared to the code I write tomorrow. That’s called progress.

I feel good when I focus on what I do know. I know I want to be the best dad I can be, the best husband I can be, and the best developer I can be. And today, that means this:

  • undying hunger — more is never enough
  • effort — constantly work hard
  • creative & pedantic — embrace both sides, writing elegant algorithms and head-pounding debugging
  • ego — be confident in yourself

So stay hungry, work your ass off, lick your envelopes, and welcome the mud at every opportunity. Good luck.

“And whenever men and women straighten their backs up, they are going somewhere, because a man can’t ride your back unless it is bent.” — Martin Luther King, Jr 3

Footnotes
  1. I’d like to thank BDW’s First 12 for listening to a crazy opinionated developer. @botvinick, @kygalle, @dasn101, @tetonmarketing, @jefferyjake, @justinmccammon, @rosErin, @JadedSkipping, @hseal, @tegoenfuego, @dviens, @ndubsglobal
  2. Hackers & Painters by Paul Graham
  3. I’ve Been to the Mountaintop by Martin Luther King, Jr. This speech was given on April 3, 1968.

 5.19

The AIR Install Badge is a very handy little flash application for delivering AIR applications to your users via the web. The badge allows your users to download and install both your application and the Adobe AIR runtime. Additionally, the install badge will automatically prompt users to upgrade if a previously installed version is detected. At Gorilla Logic, we use the AIR Install Badge on the FlexMonkey download page (free registration required).

Alas, flash is opaque to analytics. We have no idea what our users are doing inside the AIR Install Badge application. Are they installing? Or upgrading? No problem, we just need to write some code…

The Code

Using flash’s ExternalInterface, we can manually push the data out of flash and into javascript. Once we have the data in javascript, we have total control. One option is to use google analytics to store our badge data. In the case of FlexMonkey, we send the badge data along with the user’s credentials to our CRM platform, SalesForce.com.

Step 1: First, open AIRInstallBadge.as and add this to the top:

import flash.external.ExternalInterface;

Step 2: Next, add the ExternalInterface call to the top of the handleActinClick() function in AIRInstallBadge.as:

protected function handleActionClick(evt:MouseEvent):void {
    if (action == 'install' || action == 'upgrade') {
        //send data to js
        ExternalInterface.call('badgeJS',action);
    }
    ...
}

Since I only care about the install or upgrade actions, I’ll only send those out to javascript. Re-compile the badge and deploy.

Step 3: Last, add the badgeJS() javascript callback to the page containing the badge and do whatever you want with the incoming badge data:

function badgeJS(action) {
    //do metrics here...
    alert('badge action=' + action);
}

Conclusion

With an hour of effort, and a very small amount of code, we’ve managed to get the useful metrics of installs and upgrades out of the AIR Install Badge and into our analytics engine of choice. A job well done.


 5.5
Life, Work
off

This post is a follow-up to my previous post, The Schizophrenic Programmer. I must have struck a chord, because I got a ton of fantastic feedback not just on this blog, but also on Reddit and DZone.

After further reflection, I still agree with what I said and my conclusions. Moving forward, I plan to learn fewer languages and less syntax. My daily pain is that too much of my knowledge is too tightly bound to the implementation details, and when I switch languages I can’t bring my solutions to bear. It sucks, and it makes me feel stupid. So the simple fix is to put my energy into learning more unbound concepts (like my soft skills of speaking and writing) and more easily transferable concepts (like APIs).

Analytics

google-analytics

I had a huge bump in traffic mostly due to Reddit and DZone that basically crushed my regular traffic down into the noise. My poor slice at Slicehost was taking a beating, so I quickly installed the Hyper Cache plugin. It’s dead simple and it really works. On Sunday morning, my load went from 0.2 to 0.02 instantly.

I also tracked my twitter analytics via bit.ly. As a side note, you can just put a plus “+” at the end of any bit.ly url to see it’s analytics. So, since the original post was shortened to http://bit.ly/cR6cnY, the analytics are available here: http://bit.ly/cR6cnY+.

Commentary

Lots of stuff was said, but I just wanted to touch on some of the key points.

  1. I feel your pain – thanks. I find it comforting to know I’m not alone.
  2. definition of schizophrenia – a couple of comments mentioned that my post had nothing to do with schizophrenia. First, I suggest you look it up: “contradictory or antagonistic qualities or attitudes”. My Erland knowledge is definitely in opposition with my Java, which is in opposition with my Actionscript, etc. If you don’t have this problem, good on you. Second, there’s thing called artistic license and if you are a writer you get to use it. I picked a sensational word for a reason. I welcome criticism of the choice, but it’s silly to take umbrage with its validity.
  3. short sighted – my plan was called out as short sighted a couple of times. I tend to think of life and plans like this… There are three choices when it comes to plans. First, you can have no plan, and just drift along. Sometimes you get smashed into the rocks, and other times you wash up on a tropical paradise with beer, virgins, and fiber broadband. Second, you can have a short term plan that gets revisited and revised as things change. Third, you can have a short term plan, but be so unbelievably naive that you think it is actually some awesome long term plan. Life is agile. All plans are short term.
  4. embrace and extend – a few people said don’t fight it and embrace the insanity. I really lust after new stuff, so it was easy to try to the embrace-the-insanity method first. Alas, it didn’t work out and I think it actually made matters worse. Thus, I decided to go with a new plan to fight against the insanity.
  5. python – a couple people mentioned I should switch to python. Yes I’ve written many thousands of lines of python, but none in 2010 so far.

Thanks for reading. As always, I love feedback.


© 2014 saturnboy.com