Tuesday, December 25

Wait for the third one-off to build the framework

I have quite a talented and ambitious young crew working for me, and they're always chomping at the bit to show off their skills at using and abusing design patterns. I used to be the same way... a decade ago, now that I think of it. Wow. Give me a second to catch my breath; that knocked the wind out of me.

Now that I'm the stuffy old boss, I have to see the big picture, look out for the greater good, consider long-term goals, and all those other enterprise-y cliches. With that in mind, there's one mole that keeps popping up no matter how many times I whack it: the "let's build a framework" mentality.

We are an ASP. We host a centralized service that multiple clients integrate into their web sites. All of the clients reference the same code on the same servers. Whenever a client calls us up and wants to alter the way a feature looks or works, we have to create a branch in the code to accommodate them. Since all clients call the same code, that branch has to be tested across all clients. These "one-offs" have a high risk factor which increases as we accept more clients.

The way I run my shop, I hand these projects off to my developers to think up a solution and pitch it back to me (before they write a line of code). Rather than acting as the architectural overload, handing down designs like stone tablets, I give them a chance to show me what they're made of.

In most cases, the young whippersnappers use every one-off as an excuse to abstract the discrepant functionality into a full-blown framework. They come into my office and start drawing boxes and lines on my board. Factories here, Inversion of Control there, a sprinkle of Singletons and a dash of Decorators, throw in a Composite for good measure. I sit there quietly and hear them out. Then I ask the questions they dread to hear:

"Is this the first one-off for this feature?"

Yes.

"Can this be handled by a simple little if-else clause?"

Yes.

"Do you understand how the introduction of a new framework will complicate testing now, maintenance later, and the learning curve for new hires?"

Yes.

I remember the countless times I was on the other end of that inquisition. It's tough to swallow. But business and business, and we're not in the business of building cool new frameworks, we're in the business of delivering solid software on schedule. However, there is a light at the end of the tunnel.

Were they to answer the first question, "no, this isn't the first one-off for this feature," then it's a whole other story. Three is the magic number. When two clients want two different behaviors, an if-else branch is sufficient. But when the third one comes along, it's time to start talking abstraction.

Three is the threshold where you need to start asking if these are different variations on the same theme, or perhaps a new theme altogether? How flexible does this feature need to be, or can it be, before you have to break it into different features for the sake of sanity, clarity, testing, and maintenance? There are features in our system which are incredibly abstract, so much so that they differ for every single client. And, there are features in our system where the client's request was so far from the mark we had to create a completely separate version, essentially giving us sibling features that live side by side.

There's a time and place for abstraction and frameworks, but when a simple if-else clause will do the trick, be done with it and move on.

Monday, December 24

Book Review: Maverick

Maverick: The Success Story Behind the World's Most Unusual Workplace is the autobiographical braggadocio of Ricardo Semler and his Brazilian company Semco. Long story short: rebel son inherits company from traditional father and turns it into Willy Wonka's Chocolate Factory where the employees set their own hours and their own salaries and there's no org charts and no secretaries and everybody rides a unicorn to work. OK, that might be a bit of a hyperbole, but you get the gist of it.

After suffering a perspective-on-life-changing physical breakdown, Semler decides to break all the rules, think outside of the box, try new things, and manages to create a rather unique workplace where employee morale is high, turnover is low, changes are quick and painless, and tough financial times don't require drastic downsizing... as long as all the employees vote on it.

It's a rather impressive tale, but I took it with a grain of salt. I know Semler's type. Or, to be fair, I should say that I know guys that talk the way Semler writes. They are successful self-made men and to listen to them talk you'd believe they can do no wrong and everything good in their lives and their company's history has been the direct result of their foresight and perseverance, but they seem to gloss over the bad stuff and completely omit the really embarrassing screw-ups.

But all cynicism aside, the book is an entertaining read, and I do recommend it. Semler is a solid writer and weaves a good tale. The chapters are very short easily-consumed parables. Who knows, after reading it, you might be able to convince your boss to let you set your own salary and work hours.

As an aside, Semco is very much a blue-collar business, responsible for manufacturing industrial machinery for shipyards and biscuit factories -- I wonder how Semler's ideologies would fare in a large software company.

Friday, December 21

My New Year's Resolution: Heal My Department

There's a bit of a schism in my department. Earlier in the year a silver tongued consultant slithered into my organization under the covers of an unrelated project and in the blink of an eye convinced the president the the board that the B2C side of my operation could be rebuilt from scratch on an antiquated framework by an off-shore team. The next thing I knew I was running half a department (the remaining B2B side).

The tale is as old as time and history has repeated itself a million times over. For some inexplicable reason, the powers that be always trust an outsider more than an insider. I was the harbinger of doom. I recounted to horror stories of off-shore teams. I demonstrated the deficiencies of the framework. But it was all for naught. The checks were signed and the gears were put into motion.

Fast forward to tonight. The project is months behind schedule. The code is a steaming pile of fecal matter. Half of my remaining in-house department is working themselves to exhaustion into the wee hours of the night, including weekends, trying desperately to save it. They're likely to work through X-mas and New Years. For the sake of "delivering on time" they've cut all "non-essential" steps out of the process, like formal requirements, unit testing, peer code review, etc. It's the infamous IT death march.

Come the turn of the year, the contracts are up. The consultant and the off-shore team will be sent on their merry way, and I'll be left to clean up their mess. Lucky me. But I'm up to the task. I've demonstrated with the B2B team (the half of the department that remained under my control) that I can deliver projects successfully without sacrificing the process and people. I can fix the B2C side, and make the department whole again.

Let the healing begin.

Wednesday, December 12

Programmers aren't the only people that need requirements

I'm a proud member of the Starbucks army. I find myself procuring their fine beverages pretty much every day of the week.

Starbucks has this pretty crude but normally effective "requirements" policy where drink order specifications are written right on the cup into which the libation will be prepared and served.

Now I'm not one of those riffraffs that consumes my coffee cocktails out of plastic or paper vessels -- I provide my own thermos (Starbucks branded, of course). They even give me a discount for using it. That's just how I roll.

However, my eccentricities come at a cost. Starbucks is wise enough to not scrawl my order on the side of my thermos, and thus the barista is left to his or her own wits to remember my order... and they get it wrong... every... single... time...

Monday, November 19

1 is a Beautiful Number

1 Machine
1 Person
1 Day
1 Document

That's all it takes to deploy a large commercial web-based application I designed seven and a half years ago which today is still running and serving millions of pages a day for a dozen of clients so large and well-known I'm contractually prohibited from disclosing their names.

Every time I hire a new developer, their first day on the job is spent building the system from scratch on their workstation: 1 machine, 1 person, 1 day, 1 document. The most recent hire set a new record and had his system operational by lunchtime.

I attribute this accomplishment to mainly two mantras: "no fringe tools", and "the build document must stand alone".

No fringe tools means we don't use the latest and greatest one-offs or widgets just because we can. The old cliche of "the best tool for the job" breaks down when you have thirty jobs and thirty tools to master and maintain. If one tool does five jobs "good enough" then you've saved yourself a headache in staffing and training and documentation and licensing and on and on and on.

The build document is the bible in my organization. My colleagues know that I'm not one for formal documentation in most cases, but the build document is the exception. That sucker is kept up to date (the responsibility of each new hire that has to follow it) and it's kept simple. It doesn't gloss over any details and it doesn't defer to other documents. When it starts getting too long, too verbose, including too many diagrams and appendices, it's time to simplify.

So what inspired this burst of braggadocio?

By contrast, a friend's organization is currently deploying, or attempting to deploy, a web-based application with a nearly identical feature set which thus far requires nineteen separate physical servers for operation. Configuring this beast has required the consultation of three separate agencies (including the vendor). And, the grunt work of actually installing it, setting it up, and getting all the pieces to communicate and play well together has required the coordination of a half-dozen people from five different departments.

As I type up this rant, it's late into the evening, and the e-mails are still flying, and the system is not yet churning, and all I can think of in the back of my mind is, "1 is a beautiful number."

Heroku: Rails development in a web browser, with hosting.

Heroku is too cool for school. I just got my beta invite this morning and I'm having a blast playing with it. It's a Rails development interface (basically a webified text editor with some shell wrappers) that runs inside your browser with AJAXy goodness, letting you create and edit Rails applications in real time. It's rather impractical from a real-world point of view, lacking features like version control, and IDE niceties like code completion, and I haven't figured out how to use gems without "vendoring" them, but it's still insanely fun to dabble with and show off to your colleagues. The migration management is rather clever, and the drill-down logging is nice. They are still in beta, but perhaps by the time they go public it might be a decent little platform for very simple deployments like Grandma's Flickr and Twitter mash-up. But putting all practicality and business potential aside, it's an impressive feat of programming. Kudos to them.

Wednesday, November 14

Partitioning, Shards, or Federating with ActiveRecord?

Dear Lazyweb,

My Google-fu has proven to be weak sauce and I've not been able to find a nice clean solution to the following dilemma.

From Wikipedia's database partitioning definition...

"Horizontal partitioning involves putting different rows into different tables."

"Range partitioning selects a partition by determining if the partitioning key is inside a certain range."

It seems that acts_as_partitioned might be able to do this for a single table across multiple databases, but I need to partition data across multiple tables in a single database, and it gets worse...

For my particular needs, I want to get even a little bit crazier than the standard partitioning strategies. I want new records to be inserted into tables specific to the date on which they are created. For example, if I create a new Address record on November 12th, 2007, I want that record inserted into a table named ADDRESSES_20071112. OK, that's admittedly a bit screwy, but there's a big gotcha there: that table might not exist, which means I need the ActiveRecord to create tables on an as-needed basis!

Am I completely nuts here? Wait, don't answer that, but do answer me this: can it be done? Based on the amazing things I've seen done with Ruby thus far, I'm pretty confident this is possible, but my Ruby-fu hasn't yet reached black belt status. I'll try building it myself if I must, but I'm hoping to avoid reinventing any wheels.

Nested ActiveRecord Relationships and Validation Conundrum

Originally posted to another blog.

I ran into a bit of a pickle earlier this week with some models and their relationships.

The Situation

* A partner has an account.

* An account has an address.

* Accounts do not require an address; it's optional.

* Partners do require an address.

The Problem

So how do I enforce the presence of an address for the account assigned to the partner?

My First Attempt

My first thought was to break the encapsulation and create a direct partner-to-address relationship. Then I came up with the clever hack of keeping the partner-address in sync with the account-address during assignment, so when you set the address for the partner, it would automatically set the account to the same address. But it just didn't sit right with me; it wasn't the typical Ruby on Rails elegance I had come to expect. So I reached out to my partner in crime (Jack Danger) and asked for his opinion.

A More Elegant Idea

Jack suggested a more elegant simplification of just validating the presence of the address from the partner level like so:
validates_presence_of :address

def address
account && account.address
end
Now that's the simplicity and elegance I've come to expect from a Ruby on Rails application.

A Possible Third

But Jack also took it a step further and suggested subclassing account and making address required, so we'd have two types of accounts, one with optional addresses and one with required addresses. That might be even less code and arguably more elegant, but my gut just tells me it's a little less of a hack than my original attempt and not quite as pure as his first suggestion.

Wrapping Up

I went with Jack's first suggestion and I'm satisfied with the results, but I'm curious to hear if anybody else has tackled the same, or similar, problem, and how they solved it.

Monday, November 12

Mail.app and Safari... sadly not up to snuff for my needs

Mail

When I saw the IMAP feature enabled in my GMail account last week, I thought I'd hit the jackpot. I set it up immediately and started using the built-in OS X mail client I'd heard so many raves about. I gave it a good solid week of faithful use but in the end I just had to go back to GMail's web interface. There were two features I just couldn't live without:

1. Labels. I'm the kind of guy that puts multiple labels on some messages. This message might be an order status from Amazon for my daughter's X-mas gift, so it gets the "expenses" label and the "daughter" label. You just can't do that in OS X Mail; it translates labels to folders and flattens it to a one-to-one relationship; you can only put the message into one folder.

2. Searching. I didn't realize, until after my switch, how often I search my massive archive of e-mail for absolutely ancient pieces of correspondence. Just this morning somebody asked me when I had purchased my first Mac. I typed the model name into GMail's search field and in less than a second up came the order confirmation e-mail.

Safari

Even though Mail.app didn't float my boat, I decided to continue the experiment this week and switch cold-turkey to Safari from Firefox. It's been two hours now and I'm already feeling the pain.

1. Tabs. Praise be to all things holy that Firefox eventually got tabs right. I want every pop-up link from every site and every embedded link in every application to open up in a new tab, never a new window. One window to rule them all! This simply isn't possible with Safari. Granted, there is a commercial plug-in that supposedly resolves this, but I'm not paying a dime to make a built-in application work as well as the free market leader.

2. Plug-ins. I try to keep my plugs-ins to a minimum. I hate bloat and waste of memory and CPU cycles. It's my OCD nature. But there's a few plug-ins that I just can't live without.

- Firebug simply can't be lived without when you need to debug Javascript and CSS debacles.

- Linky let's me select a group of links on a page and open them all in tabs. This is priceless for doing change-set code reviews in Trac.

- AdBlock Plus blocks ads. 'Nuff said.

- Flashblock blocks Flash applets, presenting them with a nice little "play" button so that if I really (for some insane reason) want to punch the monkey I have the option.

I don't think I'm going to make it the entire week on Safari. I'm certainly going to have to pull up Firefox for some tasks I simply can't do in Safari (re: Firebug and Linky) and it doesn't make much sense to switch between two browsers when one of them already satisfies all your needs. But I'm going to try...

Update #1: I do have one huge gripe with Firefox (on the Mac). When you open a new tab, the page up/down and arrow keys do not work until you click somewhere on the page. Safari doesn't have this annoyance.

Update #2: Game over! I just tried to load up Google Spreadsheets in Safari and it doesn't work and it's not supported. That's one too many strikes. Sorry Safari.

Update #3: I gotta admit that the eye candy in Safari (native OS X form widgets, buttons, fields, font smoothing, etc.) looks a lot better than in Firefox, but looks don't compensate for lack of functionality, usability, and productivity.

Sunday, November 11

Worthy Technology and Business Podcasts

(Update: I finally got around to hyperlinking the podcasts.)

An old colleague just wrote me...
"I'm driving to work now and need listening material for the trip. What podcasts do you get/recommend?"
So I figured I'd respond here for posterity...

With a 2-hour daily commute, I've got a veritable butt-load of podcasts in my iTunes. Here's about half of my favorites (I'm omitting the video podcasts):

Technology:

- Drunk and Retired Podcast (two grizzled veterans rant about programming)

- FLOSS Weekly (really comes out quarterly, interviews with big OSS guys)

- Java Posse (if you're still into Java, good news and discussions with occasional interviews)

- Google Code Blog (mostly same guys as Java Posse, they all work for Google, discussing Google projects, tools, APIs, processes, etc.)

- Rails Envy (two local [Orlando] guys' weekly news blurbs about Rails; the same guys that do the Rails vs. Framework X spoof videos)

- Ruby on Rails (the PeepCode guy does occasional interviews with Rails pundits, usually at conferences)

Business:

- Entrepreneurial Thought Leaders (interviews with successful business people)

- Startup Studio (interviews with successful business people)

- Venture Voice (interviews with successful business people; long defunct but the back episodes are worth cherry picking)

- Web 2.0 Show (occasional interviews with the people/teams of popular/rising Web 2.0 sites)

- Vitamin (short interviews with successful people)

Humor:

[These come out pretty rarely, like once a month if you're lucky, but since you're just getting into it you'll have a gold mine of back-episodes to devour...]

- Downloadable Content (the Penny Arcade guys record some of their comic brainstorming sessions)

- Maniacal Rage (f.k.a. Garret Murray Podcast, just short comedic skits)

News, Reviews, Debates:

[When I've exhausted all of my favorites, these are decent time fillers...]

- This Week in Tech (Leo Laporte and a constantly rotating panel of industry journalists discuss current events in the tech world)

- MacBreak Weekly (Leo Laporte and a somewhat regular crew of Apple fanboys discuss current events in the Apple world)

- Security Now (in-depth discussions on network/software security issues. This used to be a really good podcast, but eventually it became one-half advertising and every other episode is just listener e-mail now. It's my bottom of the barrel podcast that I listen to after I've caught up on everything else)

- LUG Radio (a half-dozen thick-accented foul-mouthed guys discuss Linux and OSS and occasionally interview some interesting people)

Sunday, November 4

The Circle of Life

I do not work at the same place today as I did eight years ago... or do I?


Eight years ago I had my own office with a beautiful view.

Today I have my own office with a beautiful view.


Eight years ago that office view included the corner of the building where the smokers hung out.

Today my office view includes the corner of the building where the smokers congregate.


Eight years ago I was the last remaining founder in the company.

Today I am the last remaining founder in the company (which is now a mere "division" of a bigger company due to acquisition).


Eight years ago I had a prestigious title (Chief Architect) that was more a symbol of tenure than any real authority.

Today I have a prestigious title (Vice President of Development) that is more a symbol of tenure than any real authority.


Eight years ago I was considered by my peers to be the most reliable, productive, and knowledgeable technical resource in the organization, due mainly to the fact that I had been there the longest and understood the system more thoroughly than anyone else.

Ditto today.


Eight years ago I spent all day every day in back to back meetings.

Guess what I do today? Same.


Eight years ago I was the guy everybody came to for answers... but oddly enough those answers were disregarded because they "didn't have time to do it right", and so I sat there helplessly watching the ship sink.

Today? You guessed it.


Eight years ago I decided that the salary and stock simply weren't worth the frustration of being a disconnected cog in a broken machine, so I resigned and helped found a new company.

Today... I'm seeing the circle of life come back around.

Friday, November 2

Stupid Dot Net Tricks

(In homage to my colleague's series of the same name... although in this case it might be me who's stupid, not Dot Net.)

Disclaimer: I am not a real Dot Net developer (at least not a good one).

Long story short: I was young and needed the money, so about six years ago I ported a very large Java web application to Visual Basic Dot Net for one special client, and to this day I still maintain it, only touching it two or three times a year.

As part of this port, I created a little logging utility. It's a teeny tiny class whose only job is to accept a string and write it to a file.

No big deal, right? That's what I thought.

My first attempt simply opened a file handle, kept it open, and wrote strings to the file as they came in. What's the problem with that? Windows locks the file and you can't open or read it from another application. So I couldn't load a web page then open the file with a text editor to read the logged messages... without killing the web server. Screw that.

My second pass opened and closed the file for every logged message. Gah! Yeah, I'm not proud of it. But you know what? It fricken' worked... for a few years. Note that this is only for development; this mechanism doesn't log in production, so don't soil yourselves in disgust... yet.

I don't know if it's Windows itself or the file system or faster processors or what is to blame, but now for some reason the operating system (or file system) can't seem to free the lock on the file in time for the next request to open it, resulting in an IO exception when you try to log a bunch of messages consecutively. Even though the code closes the file before attempting to re-open it for the next message, Windows says "nope, sorry, somebody is still using that file." Even more perplexing is that the exception doesn't occur when you open the file, it fails on the first write attempt. It smells like a classic race condition, and it stinks.

For some silly reason I thought I could solve this by synchronizing access to the logging utility... but that didn't work, and then it dawned on me that the application was essentially single threaded. I'm not loading multiple web pages simultaneously when I'm testing, so a race condition in the code was a red herring.

So here's my dilemma: I can't keep the file handle open because that prevents me from reading the log messages as they are being written, and I can't open and close the file in rapid succession as that causes Windows to asplode.

What's my solution? I hope you're sitting down...

A queue.

This is how it works:
  1. You send a message to the utility to be logged.
  2. It puts the message in a queue (at the back).
  3. It opens the file (this always succeeds).
  4. It pulls the first message off the queue.
  5. It attempts to write the message to the file (this is where Windows barfs).
  6. It the write fails, it puts the message back on the queue (at the front).
  7. It the write succeeds, it keeps looping until the queue is empty.
  8. It closes the file.
What's the method to this madness? If I pass a message to the logger and it fails to write it to the file, the message stays on the queue and the next time I write another message to the log, if it succeeds, it writes all of the failed-so-far messages to the log, clearing out the queue.

This whole block is synchronized on the queue just in case I decide to get crazy and load two web pages at the same time.

Since I have to put things back on to the front of the queue, I can't use a real Queue object, so I have to settle for an ArrayList. Why not a LinkedList? Oh yeah, did I forget to mention that I'm stuck in version 1.1!?

Here's the code (please don't burn me at the stake):
Private Shared ReadOnly queue As ArrayList = New ArrayList

Public Shared Sub WriteLine(ByVal msg As String)
If LoggingEnabled Then
SyncLock (queue)
queue.Add(msg)
Dim out As StreamWriter
Try
out = New StreamWriter(File.Open(LogFile, FileMode.Append))
While (queue.Count > 0)
msg = queue.Item(0)
out.WriteLine(msg)
out.Flush()
queue.RemoveAt(0)
End While
Catch e As IOException
' put it back on the queue
queue.Insert(0, msg)
Finally
If out Is Nothing Then
' noop
Else
out.Close()
End If
End Try
End SyncLock
End If
End Sub

Thursday, November 1

Where are the design agencies that "get" usability?

My day job is looking to do some site redesign and we've been running the gamut of RFPs from so-called "design" agencies and it's got me on the brink of tears... and not tears of joy, we're talking tears of sheer agony. One after another I sit there and look at these proposals that are simply glorified brochures with inane and convoluted interfaces.

Our site is a service. Our customers need to be productive! The tools need to be intuitive and helpful. Clever and "innovative" are all fine and good but two things I really don't need are a) customers that leave because they can't figure it out, and b) customers that drive up support center costs because they can't figure it out.

Seriously, how hard is it to understand that a hover-scrolling list with no scroll-bar doesn't convey to the user that there's more items on the list than they can currently see? How hard is it to understand that when the entire set of content is embedded in a flash applet you can't use the browser's "find" feature to search the page, and you can't bookmark certain articles to access later or send to your friends, and it wont get indexed or cached in a search engine, and it's much harder to brand or use a translation proxy?

It pains me to read some of the design discussions on the 37signals blog, because those guys get it, and I want them and their talent working on my project. There's got to be some other agencies out there that "get" it. Where are they? Seriously. If you know of a good one, please leave me a comment. Thanks!

Monday, October 29

The new Ruby logo: Name those fonts?


I just saw the new Ruby logo (to the left of this text, in case you're reading this from an RSS feed) on Ruby Inside and I'm curious if anybody can identify those fonts?

Saturday, October 20

Coping with Doing Nothing

A little over a year ago, my little start-up was acquired by a big fish, and through a rapid and chaotic series of political implosions, I found myself running the entire technology department of the conglomerate... and I haven't written a single line of code since. I'm essentially a cat herder now. It's frustrating and depressing to spend my time approving personal time-off request forms, attending meetings about why we're always in meetings and how we can cut down on the number of meetings, dealing with the hiring and firing of a constantly fluctuating team, and watching projects, processes, techniques, and technologies that I spent nearly a decade developing and perfecting die by inches as I lose my grip on them. I miss the small company start-up atmosphere.

In the Kübler-Ross model of dealing with tragedy, I've recently graduated from bargaining to depression. But thanks to a book I'm currently reading, I'm quickly moving on to the acceptance phase. I've barely cracked the book and there's already been two key phrases that have stuck with me. The author describes managers as people who "organize and delegate." That's me to a tee right now. I'm too busy with the standard bureaucratic B. S. to get anything done myself, so I have to figure out who might be best suited to get it done for me, and pass the buck. The author also describes consultants as people who have "impact without control." Again, hitting the nail on the head regarding my situation. All I can do in my endless chain of meetings is tell people how I've seen it fail in the past, how it should be done going forward in order to succeed, then hope and pray that they take my words to heart.

Although the author tries to draw a distinction between a manager and an internal consultant, I think the line is pretty blurry, and depending on the day of the week or the attendees of a given meeting or the catastrophe du jour, I have to play both roles. It's hard being a team player when you're sitting on the bench for every game... but I guess that's what the coach does, right? Perhaps I just prefer to be the quarterback, taking the hits, getting my uniform dirty, and relishing the touchdowns from the end-zone rather than the sideline.

Saturday, October 13

Ruby Revelations: An Introspection

I was code-reviewing a cohort's contribution to adPickles the other day and I came across a line of Ruby that had me completely perplexed:
Hash[*Advertisement.aspects.map {|aspect| [aspect,advertisements.send("count_#{aspect}")]}.flatten]

That's quite a mouthful. If you know what that does, go ahead and stop reading now, 'cause the rest of this post is just self flagellation.

I'm no Ruby expert, but this guy is (in case you couldn't discern that from this one line masterpiece). I had to ask him to break it down for me, and after a few back-and-forths I think I've got it. Here's the skinny, for those of you that are still reading and, like me, want to see the secret unravelled.

My gut instinct is to start from the deepest nested block and work my way out, but you first need a little nugget of context.
Hash[*Advertisement.aspects.map {|aspect| [aspect,advertisements.send("count_#{aspect}")]}.flatten]

An Advertisement has several aspects, such as "new", "approved", "rejected", etc. Knowing that, now we can work from the bottom up.
Hash[*Advertisement.aspects.map {|aspect| [aspect,advertisements.send("count_#{aspect}")]}.flatten]

Here he's referencing a variable named advertisements which we can safely assume is an Array of objects, each being an instance of Advertisement.

He wants to call a method on the array (which in turn calls the method on each contained element) but at runtime he doesn't know the name of the method, so he constructs it on the fly.
Hash[*Advertisement.aspects.map {|aspect| [aspect,advertisements.send("count_#{aspect}")]}.flatten]

In Ruby, if you want the value of a variable to be evaluated in a string, you wrap the reference in #{}, so in this case if the value of aspect is "rejected", the method name being constructed from "count_#{aspect}" is going to be count_rejected.

So each member of the Array named advertisments is going to have the method count_rejected called.
Hash[*Advertisement.aspects.map {|aspect| [aspect,advertisements.send("count_#{aspect}")]}.flatten]

And for each aspect, a two-element array is created, where the first element is the value of aspect and the second element is the results returned by the on-the-fly-generated-method-name.

For example, one of these arrays might look like ['rejected',23].
Hash[*Advertisement.aspects.map {|aspect| [aspect,advertisements.send("count_#{aspect}")]}.flatten]

Each of these arrays are collected into an outer containing array by the map method, which will leave us with something like [['new',14],['live',51],['rejected',23]].
Hash[*Advertisement.aspects.map {|aspect| [aspect,advertisements.send("count_#{aspect}")]}.flatten]

The the whole thing gets flattened into a one-level (flat) array, like ['new',14,'live',51,'rejected',23].
Hash[*Advertisement.aspects.map {|aspect| [aspect,advertisements.send("count_#{aspect}")]}.flatten]

As my cohort explains the next part, "Hash doesn't take an array as an argument, so the asterisk breaks the array into multiple arguments. Hash[*[1,2,3,4]] is the same as Hash[1,2,3,4]."
Hash[*Advertisement.aspects.map {|aspect| [aspect,advertisements.send("count_#{aspect}")]}.flatten]

Which bring us to the crusty outer shell, leaving us with a simple hash which looks like {'new' => 14,'live' => 51,'rejected' => 23}, where as you can see, each aspect is now mapped to its respective count.

Clear as mud? Excellent!

Thursday, October 11

Clients don't always say what they mean

When I wrote my last post, I worried [rightfully so, it turns out] about readers reading too much into the example client requirements. My intent was to simply explain how to breakdown a chunk of verbiage into a checklist of manageable requirements. Unfortunately, I planted a few too many language bombs in my contrived example and a former colleague (Craig Walker) ran with it and posted a follow-up over on his blog.

Craig focuses on the first and last sentences of the example requirement verbiage. My intent was to use those sentences as examples of non-requirements, since they merely expressed opinion and speculation from the client. But, as Craig astutely pointed out, they are good warning flags that what the client expects doesn't match up with what the client requested. The client expects fewer mistyped passwords and better security, but the client requested changes that might not necessarily deliver those results. Craig outlines some guidelines for digging deeper and finding the root cause of the problem rather than taking the client's proposed solution for granted.

As a side note, if you're not subscribed to Craig's blog feed, I highly recommend it. He's one sharp guy. He was my right-hand man in the Java world for a great many years and he recently set off on his own to dabble with .NET, which he rants about quite frequently in his "Stupid .NET Tricks" posts.

Wednesday, October 10

Digestible Requirements

I commonly get "requirements" documents from clients that read like a college book report, just pages and paragraphs of disorganized stream-of-consciousness ramblings. When this happens, I use a little trick to restore some order to the chaos. I break it down into one long checklist of digestible tasks.

Why a big checklist? Several reasons:

  • It's easier to digest smaller tasks in comparison to big paragraphs of verbiage.
  • It's easier to organize and group those smaller tasks.
  • It translates directly into a test plan; every checkbox is a test that has to be written.
  • It gives me a pretty decent status report and evidence of progress and I work through the list checking things off.
  • Once everything is checked off, I know I'm finished. I know that I've satisfied the client's request.

Here's how I typically go about it. Let's use this as an example "requirement" from a client:

"We're tired of users mistyping their passwords and not being able to access their new accounts. First of all, we want to add a password confirmation field to the sign-up form. Secondly, we want to force them to pick a password between eight and twelve characters. This should make their accounts a little more secure."

For the moment, set aside the issues of usability and obscurity over security; those topics are outside the scope of this discussion. The first thing I do is break the document into a checklist where every sentence is an item on the list. You can do this pretty quick and dirty with a simple regular expression find and replace of every period with a carriage return and a checkbox. This gives me something like:
[_] We're tired of users mistyping their passwords and not being able to access their new accounts.  
[_] First of all, we want to add a password confirmation field to the sign-up form.
[_] Secondly, we want to force them to pick a password between eight and twelve characters.
[_] This should make their accounts a little more secure.

Now you'll probably notice that the first and last items aren't really requirements, they're just an opinion and a speculation respectively. They can be removed, leaving us with:
[_] First of all, we want to add a password confirmation field to the sign-up form.  
[_] Secondly, we want to force them to pick a password between eight and twelve characters.

Is the order important here? I don't think so. Let's get rid of that "first" and "second" nonsense:
[_] we want to add a password confirmation field to the sign-up form.  
[_] we want to force them to pick a password between eight and twelve characters.

Then a little clean-up of the grammar to make it more declarative:
[_] add a password confirmation field to the sign-up form
[_] passwords must be between eight and twelve characters

Now comes a tricky issue. Whenever you see a conjunction like "and" or "or" or in this case the word "between" you're probably looking at multiple requirements in a single sentence, and you're better off breaking them up:
[_] add a password confirmation field to the sign-up form
[_] passwords must be more than eight characters
[_] passwords must be no more than twelve characters

By breaking it up into two separate items, we can writer better isolated tests.

As a side note here, try to keep in mind that most people don't think "logically" like programmers. When they say "between eight and twelve" they don't consider the literal interpretation of "greater than eight and less than twelve." In most cases they probably mean inclusive of eight and twelve. Make sure you clarify with them before taking their words for granted.

Now, depending on the situation and the particular client, I will probably pass this list back to them, asking for them to review it and confirm that I have accurately interpreted their prose. And in some extreme cases, I'll ask them to sign it.

Once I've got my checklist, I'm golden. I can write a test case for every item on the checklist. I can use the list to gauge my progress with the project. And when everything on the list has been checked off (i.e. all the tests pass), I know I'm done.

Monday, October 8

acts_as_conference (is Orlando the new Silicon Marsh?)

Ah ha ha, get it? Valley. Marsh. Oh I kill myself. Anyways...

I'm still coming down from the high that was BarCamp Orlando (you rock, Gregg) and I've just discovered that one of my favorite presenters (Robert Dempsey) is trying to organize a full-fledged Rails conference right here in my backyard! I'll take the three-hour drive over a ten-hour flight any day of the week. Rails For All (Dempsey's organization) just announced their call for proposals today. I'm praying he gets flooded with submissions.

Sunday, October 7

Ted Driven Development

Earlier this year, I was helping an old friend weed-out candidates for his latest Ruby on Rails venture, adPickles, and in the interest of cutting to the chase and letting the potential hires know what they were getting themselves in to, I sent them the following manifesto:

I just wanted to drop you a note to give you a little heads-up on the way they run development teams, so you can comment, inquire, or run screaming for the hills :-)

* TDD

First of all, they're a huge proponent of Test Driven Development (TDD). They aren't always faithful, but they enforce it as much as they can, and when you see the code that has been written so far, you'll see that there's a veritable butt-load of tests.

They haven't yet incorporated any sort of test coverage metric, but they do rely on autotest, which re-runs all of the appropriate tests every time they change a line of code. This tool is invaluable for catching all those "oops I didn't expect this to break that" moments.

If you commit code to the repository that fails a test, your humiliation will be merciless :-)

* DDT

Likewise they are adamant about Defect Driven Testing (DDT). Whenever the Big Boss Man shoots a developer an e-mail saying "hey I think I found a bug" the first thing the developer does is write a test to reproduce the issue. That test becomes a permanent part of the application test suite so that particular bug will never again sneak back into the application without unnoticed. They will expect you to do the same every time you are asked to fix or change something.

* Branching

If you don't know how to handle branching in Subversion, now is the time to figure it out :-)

Every time they take on a new developer, they insist that the new hire perform all of his development in branches rather than directly in trunk, until such time as they trust them to interact directly with trunk, or until the team grows so big that working directly with trunk is simply taboo and they all work in branches. This ensures the purity of the code base.

* Commits

One commit per change. If you have three items on your TODO list, you do one then commit, then another and commit, then the last and commit. You don't do all three at once and commit one big chunk of changes. Why? Code reviews...

* Code Reviews

They use Trac for code reviews. If you've every poked around the Rails core site you've seen this tool. The changeset feature gives them a nice purty color-coded "diff" of every change you made for each commit. This is why they insist on one change per commit. This allows them to review your work on a clean per-ticket basis. Your work will not be merged back into trunk until they have code reviewed and approved it.

* Referential Integrity

Just a side note here. I believe in foreign keys. If you've dug deep enough in my blog you'll see that DHH and I disagree on this issue, and if you saw/heard the keynotes from RailsConf last year [ED: referring to RailsConf 2006 in Chicago] you'll know that I'm not alone in my disagreement. This project has referential integrity in the database, which they achieve through a plug-in. You'll see this if/when you come on board. [ED: I later changed my stripes on the matter, during RailsConf 2007 in Portland as a matter of fact.]

* Deployment

They use Capistrano for deployment, and for now only the lead developer performs deployments.

When you finish up some work and it needs to be reviewed by the president, you'll let the lead developer know (through Trac). He will then code review it and (if he approves it) merge it into trunk. He will then deploy from trunk.


That's the basic gist of it. If you have any questions, comments, or concerns, please let me know. Thanks for your time.

Wednesday, October 3

Book Review: Secrets of Consulting

Secrets of Consulting is like Sun Tzu's The Art of War for consulting. Gerald Weinberg has organized the book into a rapid-fire succession of juicy tidbits of sage advice, packaging each in a quaint but often esoteric mnemonic, and reinforcing each nugget with a story. That's all good. The stories are relevant and they get the point across. However, he gives each anecdote a silly name (like "Prescott's Pickle Principle"), then uses that nomenclature when referring to them later on in the book. I found that annoying and confusing. Since I didn't memorize each goofy alias, I had to read the book with my pinky finger lodged in the glossary, so I could continuously flip back and forth to reference the pseudonyms. Thankfully, he lists them all out by name, alphabetically, right before the index at the back of the book.

It wasn't the "fix problem X with solution Y" book I had expected. It's much more of a "get to know yourself" and "be able to step outside yourself and observe your own actions without bias" sort of tome. The lessons learned from the book are basically methods for keeping an objective view on a situation, being able to see things from different angles, being cognizant of and compassionate to your client's personal and emotional investment in a project as well as your own, realizing when you're falling into ruts, discerning when you've become part of the problem rather than the solution, etc., ad infinitum. It wraps up with a few short and sweet chapters on pricing and trust.

In short, it's worth reading. You'll be a better consultant for having read it. You'll better deal with other consultants for having read it. I've heard there's a sequel, but I think my next endeavor is going to be the book Weinberg praises in his own writing: Flawless Consulting: A Guide to Getting Your Expertise Used.

Friday, September 14

Previews, Test Accounts, and Screencasts

I was over at RailsRumble this morning, reviewing entries and casting my votes, and I noticed a few things that set some of the sites head and shoulders above the rest: previews, test accounts, and screencasts.

Many of the sites simply had splash pages that said "log in or register" to see what the site did. In most cases, I didn't bother. There's nearly one-hundred entries in the competition; I'm not going to bother creating that many bogus accounts. Kudos to the sites that support OpenID, but Dr. Nic already covered that.

Some of the sites with really slick designs or very clear and concise explanatory text coaxed me in to signing up. But in most cases it was a crap shoot, especially the ones that expected me to read paragraphs of exposition.

Sites with screencasts were immediate favorites. It doesn't get any simpler! A screencast shows me what the site has to offer in a quick and easily digestible format. The creator of the site knows best how to use it to its full potential, and they can explain it all in full motion video while talking me through it. If I were President of the Interwebs, I would make it a law that all sites have screencasts.

Previews in the form of screenshots with annotations are nice, and I highly recommend them as second-best to a screencast, but they require me to read, interpret, and study the presentation. That's work. The less work I have to do to learn what a site can offer me, the quicker I'm going to get to using it.

Test accounts are a double-edged sword. The cons are that once I'm in I have to explore; I have to wander around aimlessly playing with the site, trying to figure out what it has to offer. It also seems that with at least one of the sites, they didn't disable the "change your password" for the test accounts, as I was unable to get into them using the credentials advertised on the front page. However, for those of us, and those particular services, where you really need to "try before you buy", test accounts are golden.

Monday, September 10

Book Review: Founders at Work

Founders at Work: Stories of Startups' Early Days is a very large collection of mostly fantastic interviews with people that founded (or were closely related to the founding of) companies that made them ridiculously wealthy, or at the very least dot-com celebrities. They are not necessarily successful companies; in some cases rather miserable failures that burned bright then burned out, and in some cases names I've never heard, and I did my tour of duty pretty deep in the trenches. But they are companies that, for the most part, changed the face of business for the better.

It took me forever to get through this book, and it was worth nearly every page. I could have done without the Woz interview; who hasn't heard his story fifty bazillion times now? I expected the Spolsky interview to be more of the same, having followed his popular blog since its inception, but surprisingly it wasn't a regurgitation of his regular rants, which I appreciated.

The author and interviewer, being female, was very keen to ask each of the female founders about their gender-related challenges, which I found rather awkward and borderline insulting, but they all handled it well. They are probably used to it. I guess I can't relate, since I'm male and haven't made a mint off any of my start-ups.

One thing this book wont teach you is the secret to success. It will reinforce the virtues of perseverance, faith in your ideas, following your dreams, and acting on instinct. But it seems the one thing all these people and their stories have in common is that they were in the right place at the right time, knew the right people, and just plain got lucky. Being the first or the best doesn't necessarily mean jack squat, which can be a hard pill to swallow.

Another thing most of them have in common is that their original goals and ideas were merely springboards to funding. Once they had enough money for offices and employees, they had to change their plans and products pretty drastically to adjust to what the market really wanted and was willing to pay for. I know first-hand how true that is, based on the experience of my last couple ventures, and the ventures of a colleague entrepreneur over the last dozen years.

For me, the book was a prime source of nostalgia, since my career started shortly before the dot-com boom, and I remember watching many of these companies from the sidelines. It was also an inspiration for those evenings when I had to decide between sitting on the couch watching mind-numbing television or picking up the laptop and helping my entrepreneur friend get his latest company off the ground. The latter is much more satisfying at the end of the night.

I can't recommend this book highly enough. It's going in the top-ten section of my bookshelf. Pick it up and read a few of the interviews; you wont be sorry.

Sunday, September 9

Rails Rumble

I don't know how this slipped under my radar, but I just discovered Rails Rumble on Friday night, right before it started, and as I'm typing this, with only a couple hours left in the competition, I find out a colleague of mine is participating. Take a look at his team. I think he's playing with a stacked deck. I'm about to hit the sack -- I can't keep up with these young whipper snappers -- but first thing tomorrow morning I'll be checking out the results.

Friday, September 7

Why I hate Microsoft Outlook 2007

Sorry, I just need to vent for a second. Outlook has just screwed me for the second time in so many weeks, and I'm pissed. I have (had) a very large "distribution list" entry in my Contacts, containing several dozen members. With the high turnover rate at my current company, I frequently have to update this list. I open the list, select the member that needs to be removed, and click the "Delete" button in the menu bar. But guess what! It doesn't delete the selected member. It deletes the entire distribution list! Oh, but that's not all. There's no confirmation message, and there's no undo! It's gone, completely, the entire list. I have to recreate it again, by hand, only to re-learn this lesson the hard way, again, in a couple weeks, after I've forgotten about the trap.

Sunday, September 2

BarCamp Orlando

It's coming up soon! I plan to be there. Anybody else? I need to get off my butt and find a hotel. This will be my first time, so if anybody has any recommendations, I'm all ears. Thanks! I'll be spending Sunday night at The Lexington at Orlando City Place, which looks to be a stone's throw away from the watering hole. I checked several travel sites ('cause I'm a penny pincher) and found Travelocity's price was $50 less than the others.

Sunday, August 26

Automated on-demand backups with OS X

Update #2: Although the solution below is free and easy and relatively reliable, I eventually broke down and just purchased SuperDuper.


Update #1: In my haste, I forgot some important tidbits of information, such as where you put all these files...


I just [finally] got myself an external USB hard drive (Western Digital 160GB Passport) and I wanted to set up my Macbook Pro so that whenever I connected the drive to the laptop, the laptop would automatically back itself up, then give me a report of the backup and unmount the drive so I could immediately unplug it and put it away. That's how lazy I want to be about backing up.

So I scoured the 'net and finally figured out how to "git 'er done" just the way I like, thanks primarily to this old post. I'm posting this primarily for my own benefit because I know I'll have to do this again in the near future and I won't remember how I did it. So here's how it goes...

First of all you need to write yourself a fancy backup script (most comments are from the original author, not me) and place it in "~/Library/Scripts". I named my script "backup.com" to conform to the example in the original article:

#!/bin/bash

# Borrowed (and BASH'd) from...
# http://www.macresearch.org/tutorial_backups_with_launchd

# Convenience variables to specify what I want to
# backup and where I want to back it up to

HOME="/Users/ted"
VOLUME="/Volumes/WD Passport"
DEST="$VOLUME/"

# This sleep timer has been added to allow enough
# time for the system to mount the external drive
# On my PowerBook 30 sec. is more than enough time

sleep 20

# This check is added to test for cases when we are
# removing a drive from /Volumes or if the drive failed
# to mount in the first place

if [ ! -e "$VOLUME" ]; then
echo $VOLUME not mounted;
exit 0;
fi

# Create the folder to back up the data to (defined above)

if [ ! -e "$DEST" ]; then
echo $DEST does not exist;
exit 0;
fi

# Copy the files over. Here we are using rsync.

EXCLUDES="$HOME/.rsync-excludes"
RESULTS="$HOME/rsync-results.txt"

rsync --verbose --archive --modify-window=1 --exclude-from="$EXCLUDES" "$HOME" "$DEST" > "$RESULTS"
# --delete --delete-excluded

# Optionally, once the rsync is done, unmount the drive.

hdiutil detach "$VOLUME" >> "$RESULTS"

# Display the results

mate $RESULTS

exit 0

Then you need yourself a "plist" file to tell OS X's "launch control" [launchctl] service to (a) watch for the drive to be mounted [plugged in], and (b) run the aforementioned script when it is. This file goes in "~/Library/LaunchAgents" and in my case is named "net.rudiment.backup":

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" \
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<dict>
<key>Label</key>
<string>net.rudiment.backup</string>
<key>LowPriorityIO</key>
<true/>
<key>Program</key>
<string>/Users/ted/Library/Scripts/backup.com</string>
<key>ProgramArguments</key>
<array>
<string>backup.com</string>
</array>
<key>WatchPaths</key>
<array>
<string>/Volumes</string>
</array>
</dict>
</plist>

Now as the original article mentions, all you have to do is log out and log back in for this trigger to activate, but you can do it manually (and I did) like so:

Teflon-Ted:~ ted$ launchctl list
Teflon-Ted:~ ted$ launchctl load ~/Library/LaunchAgents
Teflon-Ted:~ ted$ launchctl list
net.rudiment.backup

That's it! You're golden.

And also for the sake of posterity, here's the contents of the "exclude" file referenced in the script:

.Trash/
.netbeans/
.netbeans-derby/
.gimp-2.2/
.thumbnails/
.svn/
Library/Caches/
Library/Mail/
Library/Logs/
.history/
iPod Photo Cache/
Printers/

Saturday, May 26

I gave up on referential integrity

I did the unthinkable last week. I gave up on referential integrity with my latest project. It's simply too painful to support in Ruby on Rails.

When I started the project, the first plug-in I installed was Simon Harris's Foreign Key Migrations. This plug-in rocks!

So things were cooking along and I brought on another developer. One night I got home and went through my pre-coding ritual: Get the latest code from Subversion, migrate the database, rake the tests to make sure I'm starting with a solid foundation.

Two of the tests failed.

I was livid.

How dare my colleague commit broken code to the trunk!

We started a back and forth and we eventually discovered that our databases didn't match. He was missing foreign keys that I had, and vice versa. Not good.

I shot Simon and e-mail informing him of the assumed bug and asked if he'd seen it before and had any suggestions. It was news to him. He was incredibly receptive and helpful. He asked for as much information as I could give and I sent him my entire migration set along with all the plug-ins we were using.

Simon discovered that the Engines plug-in was breaking his foreign key plug-in.

As an aside, I had worked with engines in the early days and the experience was so traumatic I swore off them for life. But my colleague had a plug-in that required them and we needed it so I caved... and shot myself in the other foot.

I thought I had fixed the problem by hacking it so that the foreign key plug-in loaded before the engines plug-in. After a fresh migration all the tests passed. But it was temporary. A few dev cycles later we found another database inconsistency.

So in the wee hours of the morning at the second or third day of Railsconf I sat outside the ballroom and wrote unit tests with fixtures for every single foreign key in the database. That's when things really started going down the tubes.

The testing framework in Rails isn't clever enough to load the fixtures in the correct order to avoid violating foreign key constraints. Ditto for the unloading between each test. It was carnage.

So I'd had it. I was fed up. I made possibly the hardest and most painful architectural decision of my software development career... I gave up on referential integrity. I removed the foreign key plug-in from trunk and committed a migration to remove all the legacy constraints. Then, I wept.

One of my biggest pet peeves is fighting the tools. Yeah it's fun to hack and extend the tools with plug-ins and monkey patches but at the end of the day if the tools are working against you instead of with you, something has to give, and in this case I think the pros of productivity, support, and community with Rails outweighed the cons of having my cake and eating it to, so I gave up. I hope I don't live to regret it.

Saturday, May 19

Railsconf Celebrity Sightings

On the way back to my hotel room for the dinner break I shared an elevator ride with Geoffrey Grosenbach (topfunky) of the Ruby on Rails Podcast and perhaps more popular PeepCode screencasts. He wasn't hard to miss with his shirt and blinged-out messenger bag both adorned with the PeepCode logo, and there was no mistaking that signature voice.

At the keynote I noticed the guy sitting next to me was playing NetHack on his laptop with the account name "rbates" and I asked if he happened to be Ryan Bates of the phenomenal Railscasts screencasts. He was.

Saturday, April 28

Grounded in Rails

If you're a Ruby on Rails fan boy, like me, you owe it to yourself to hear the voice of reason that is Dave Fayram on the latest episode of the Drunk and Retired podcast.

Tuesday, March 20

Immutable ActiveRecord Attributes

At last night's Ruby Meetup I posed the question of how to make an ActiveRecord attribute immutable, meaning the ability to set a value once and never allow it to change.

I got lots of theories but no solid answers. One person suggested using callbacks but the hooks aren't there for the logic he proposed. Another person suggested overwriting the assignment method, but I wondered about the update_attributes method undermining it. It turns out the latter calls the former so it's safe.

I wrote some unit tests today and started experimenting and found a solution that seems to work nicely (and consistently).

class Sender < ActiveRecord::Base
def email=(address)
if new_record?
write_attribute(:email, address)
else
raise 'Sender.email is immutable!'
end
end
end

Wednesday, January 24

How do you become an Architect?

During an interview yesterday a candidate told me he had aspirations of becoming a software architect and asked me how he should pursue it. That question caught me off guard. I considered my personal career history and came up with this little nugget:
Always be the guy in every meeting with the best design idea.
That's pretty much how it worked for me. When you're sitting in a room with your colleagues and peers, discussing and debating how to solve a particular problem, and you consistently present the best ideas, the ideas that get implemented, you start to build a reputation and credibility. The next thing you know, you're The Architect. I expanded on the idea with this:
Be able to recognize and vocalize the pros and cons of every idea.
Not just your own; other people's ideas as well. If you can tell them, in a polite and constructive manner, what's wrong with their idea and how it might hurt them in the near or far future, they will [if they're not a complete jackass] thank you for it, and come to you the next time they want a Sanity Check.

But how, you might ask, do you become the guy with the best ideas? Read, read, read! I am a bookworm. I read books on design patterns, frameworks, methodologies, programming languages, antipatterns, usability, etc. If you can grok it and regurgitate it at the appropriate times, you'll be The Idea Guy.

That's how it worked for me. Your mileage may vary.

Sunday, January 21

Absolute Darkness

While I'm awaiting the arrival of my new MacBook Pro, I've been doing a lot of dabbling with the Mac Mini on the desk in my bedroom. Unfortunately for me, I require complete darkness in order to get a good night sleep and the bright light coming off the speakers and the pulsing light coming off the Mac Mini and the orange light coming off the monitor were driving me nuts. So I broke down and fixed them low-tech style today using some black 3M plastic tape. Why this particular tape? No residue! I can leave this tape on there for years (and have with prior projects) and pull it off some day and there will be no gunk.



Saturday, January 20

That Didn't Take Long

One day into development and I've already got a rant... but it's a tame one.

I needed to do some Globally Unique Identifier (GUID) generation for my new project so I Googled up "ruby guid" and arrived at this convenient little library.

Somebody has already done the work for me, and shared it! I love that. My thanks go out to the author.

I downloaded it, installed it, incorporated it, tested it, and everything was golden... on my Windows machine.

After I committed to source control and ran an update on my Mac to continue development there, I started getting this error:

/usr/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require': No such file to load -- Win32API (MissingSourceFile)

Oops, that's rather queer. Why would the code running on OSX be attempting to load a Windows API?

Thanks to the wonders of Open Source Software (and the author) I was able to look at the offending line and this is what I found:

if RUBY_PLATFORM =~ /win/i

The author reports on his download site that he "only tested this library under Win2k and Redhat," so he didn't realize or anticipate another environment other than Windows that might have "win" in its name... like, maybe, Darwin :-)

Friday, January 19

I'm back! Did you miss me?

The last six months have been rather tumultuous career wise. Long story short: my little company was bought by a bigger company and I was technically demoted from Chief Technology Officer (of the little company) to Vice President of Development (of the bigger company) which ironically resulted in more pay and more projects and more employees underneath me, but less stake in the company, and the icing on the cake is that the bigger company still has nobody filling the Chief Technology Officer position so I’m “acting” as that role. Sigh.

But that’s not the reason I’m writing today. A couple weeks ago an old friend of mine e-mailed me out of the clear blue sky. This is the entrepreneur friend that started my last two companies, including the one that just sold to a bigger company, and two more companies after that with which I wasn’t involved. Basically, this guy has the Midas touch. He’s got an idea for yet another company. He pitched it to me, and I liked it, and it’s relatively simple from both a technological and management perspective, so we’re going to play with it and see where it goes.

And what, pray tell, first crossed my mind when the thought of implementation arose? Why Ruby on Rails of course! And what perfect timing as version 1.2 was just released yesterday!

But I had one little road block. I have a plethora of computers around my house, but half of them are far too old and decrepit for development and the other half are owned by my current employer, so I had to get me a new machine for my new endeavor. It’s the law of the jungle that any Rails developer worth his salt has to work on OSX, so yesterday afternoon I ordered myself a refurbished 15-inch Core2 Duo MacBook Pro, and this morning Apple e-mailed me to tell me it had shipped. This will be replacing, or supplementing, my company-issued three-year-old Dell Inspiron. I’m giddy with anticipation.

Once I start getting back into the nitty gritty of Rails development, I expect I’ll be posting here again quite regularly with my rants and raves and trials and tribulations.