Technical


11
Aug 11

Where We Tell GoDaddy To Sod Right On Off

For the longest time, we’ve been using GoDaddy for domain names. And for primarily three reasons:

  1. It was I was using for my personal domain names
  2. It was pretty cheap, all things being equal
  3. Inertia (it wasn’t so unpleasant that I felt I had to move)

And yes, #1 & #3 are effectively the same reason.

That changed today. For those who might have tried to access our website this morning or tried to send us an email, it failed. What’s more, it failed ignominiously. Why would such a thing happen?

Well, because GoDaddy changed our domain name from pointing to our servers to pointing to theirs instead (you know, the ones that are plastered with advertising and oh so tasteful?). I called GoDaddy to find out what happened; it turns out our domain had not been renewed. Even though we had set the “auto-renew” flag to true, even though we had two credit cards on file which were both valid, GoDaddy elected not to renew the domain.

Their position is that the specific RE domain was not linked to the credit card, and so it lapsed. When I asked about notifications, they said, “well, we sent out an email saying your domains were expiring and you should do something about it”. Yes, they did send that email. Of course, they sent out a ton of other emails for the other domains I have with GoDaddy (all of which were set to auto-renew) and everything worked out fine for those.

The net result of this is that we are moving from GoDaddy to DNSimple. DNSimple is much more techie friendly, there’s no annoying upselling and advertising everywhere, and the rates are cheaper, if you have enough domains. Which we do.

The lesson here is that GoDaddy didn’t have to loose me as a customer. If their “your domain is expiring” email had been adeptly worded — or, even better, a dedicated email saying “your domain will expire because the financial information is incorrect” — it would have been caught and all this anguish wouldn’t have happened. But, it did and we’re moving right along.

DNSimple uses a flat rate tier structure; coupled with their rather high transfer fees and we’re going to be more in the red this year over DNS but will make back the costs on year 2 and be black there on out. But, DNSimple isn’t all peaches and cream; the transfer interface is a bit clunky, not to mention very manually intensive. Of course, if I had the copious free time, I could probably script something up using their RESTful API, but that’s actually more time consuming (coding/testing/etc — the by hand approach I can do here and there while I’m doing other things).

Another thing; we’re going to stay on GoDaddy until all the subsidary domains change over to DNSimple and we’re satisified with the stability of our configuration. We’ll probably migrate the main domain cluster over an upcoming weekend to limit any possible disruptions. Watch this space and our twitter feed for more details.

TwitterFacebookLinkedInXINGViadeoRedditDiggEmailShare

8
Aug 11

Beehive, Day 3

Another short night, and we’re back for more. Today, Scott finished up rest of the application process and Heather banged out a fair amount of the UX. I spent most of my time continuing to work on the financial model as well as formally defining what are going to consider to be our MVP for SEATS. After four or five hours of work, though, it became clear that we were done for the day.

One of the upsides of doing a startup in our late 30s is that we’ve been doing this long enough to know the right way to do things and the pitfalls to skip. One of the downsides, however, is that 60 straight hours of work in a weekend leaves us exhausted, not invigorated (like it would have done with I was in my 2nd or 3rd startup 15 years ago).

In any case, it was very good and useful. I would say we probably made as much technical progress in one extended weekend as we had in the few months preceding. I believe a large part of the accelerated progress came more from the unbridled focus than the being in the same room aspect. Don’t get me wrong; being able to get immediate answers to questions or problems definitely made things better, but the absence of distractions (family, other clients, TV, etc.) allowed us to get into our respective grooves and just churn out quality work.

One of the upshots from this weekend will be a regular, scheduled bit of co-working; probably once every two weeks, assuming we can find a good space that works for each of us.

TwitterFacebookLinkedInXINGViadeoRedditDiggEmailShare

7
Aug 11

Beehive, Day 2

Back at the Beehive bright and early today. We picked up right where we left off, back in the saddle again. The only downside about this place is apparently the A/C is off for the weekend. And there’s a heatwave. If we had known, we would have brought fans.

But that’s a small thing. The benefits of being together in the same place are paying dividends nicely; we are nominally a virtual team, but the ability to get immediate feedback and turnaround is pretty critical. I think we may hop through TeqCorner (or somewhere similar) in the DC area twice a month to see if we can’t keep this momentum going.

After a few hours, Brian Sierakowski dropped by and interviewed me for Prodcast. It’s always good to catch up with friends and this experience demonstrated how out of practice I am with interviews. Not that I said anything wrong, just badly — lots of hemming and hawing as I wandered through my thoughts. For next time, it’s always message, message, message.

We’ve also been busy paring down the feature list. One of our selling points is everything that you need, nothing that you don’t; one of the many reasons why we are targeting the mobile platform is to enforce that sort of constraint. However, it’s made the ‘killing your babies ‘ phase of design come quicker than it might have otherwise. It’s a good thing, but there have been two or three ideas that I just know would be killer features now deferred to the indefinite Phase 2.

On the happier side, the functionality for posting a job through the system is complete and seems to be pretty solid (at least, for the golden path it is). Scott is about halfway through the application process. We’re applying the lessons from the $300M button and trying to simplify the application process as much as possible. The good and bad resulting from our approach means our application process is unlike most of the major systems. I view that as a good thing.

Towards the end of the night, we hit a snag with the best way to address agency recruiters (i.e. firms recruiting on behalf of some other company). Some of the rub comes from security — specifically, what can be shared between the two entities, how should it be shared and delegating permissions; other parts of it comes from billing — how do you bill both the agency and the hiring firm fairly? 16 hours straight at work dulls the mind, so we’re punting on this problem until the morning light.

TwitterFacebookLinkedInXINGViadeoRedditDiggEmailShare

6
Aug 11

Beehive, Night 1

Tonight was a pretty productive night at the Beehive. The primary goal for the weekend is to have the system to a point where we can post our own two job openings using the system. By the way, we’re hiring for a marketing/biz dev person and a mobile developer (Android & iOS).

First, the trip. For future reference, the DC-Baltimore thruway is a parking lot on Fridays. Scott and I left DC at noon (to skip the weekend traffic) and arrived at 3pm. In this case, it was because a tractor trailer overturned on 95, blocking three lanes of traffic and causing spillover onto every other highway, road and goat path heading north. Heather had it worse, though; she left at 4:30 and got both the wreck traffic and the traffic for the Orioles game.

Once we got here and found the place, we got to work. Scott knocked off the login/account creation process and made it about halfway through the creating a job req process. Heather made good headway on her designs, and I’ve been working on getting our financial projections into better shape, reflecting our new pricing model and some revised information about churn rates specific to the HR software industry.

TwitterFacebookLinkedInXINGViadeoRedditDiggEmailShare

5
Jul 11

The Mobile Experience – Mobile Layouts With Rails

So, you have this great web app and you’re targeting mobile users (for our purposes, this means Android, iOS, and anything small/hand-held with a full browser and a touch screen). How do you make sure your app is usable on small, hand-held devices?

There are a number of interface strategies, including:

  1. Design only for mobile, and particularly the phone (iPhone/Android phone only).
  2. Design for desktop only, and assume mobile users will zoom as needed.
  3. Design separately for mobile and desktop, and figure out which interface to use somehow.

and there are permutations here based on whether you want to have a different experience on tablets versus phones, tablets versus desktops, Android vs. iOS vs. Blackberry, and so on.

For our system, we’re starting small – designing for iPhone – and scaling up to the tablet and desktop as we go. This will allow us to make sure the interface really works when all you have is your phone. We’ll test the app on both the iOS simulator and the Android SDK simulator on several versions of Android and the two latest iOS, and again in various Desktop browsers (Safari, Chrome, Firefox, and some reasonably intelligent version of Internet Explorer – just as soon as Microsoft releases one).

For our initial purposes, we need different layouts for both desktop and mobile. So we need to figure out how to serve mobile differently. Again, there are a couple different strategies:

  1. User-agent testing on the server-side (check HTTP_USER_AGENT or its equivalent).
  2. User-agent testing on the client-side, via JavaScript and DOM manipulation.
  3. User-agent testing on the client-side, via specialized CSS calls. A List Apart has a great explanation of how this is done.

#2 and #3 both seem overly complicated to me. For now, we’re going with sniffing the user-agent string. The strategy for implementing this in Rails is to do something like this:

# app/controllers/application_controller.rb
before_filter :check_layout

def check_layout
case params["layout"]
when "mobile" then session[:layout] = "mobile"
when "full" then session[:layout] = "full"
when nil then
  session[:layout] = ua_is_mobile ? "mobile" : "full"
else session[:layout] = "full"
end
end

def ua_is_mobile
uastring =
  request.env["HTTP_USER_AGENT"].downcase rescue nil
# you could check several patterns here
return true if uastring =~ /mobile/
return false
end

and then you can either use layout :mobile or the default layout depending on the value of session[:layout]. In our case we have separate layout files entirely, which reduces the number of if statements in the layout. We can then DRY up the layouts using shared partials and helpers. I also seek to minimize repetition in the views by having view snippets be device agnostic, and leaving it to CSS to make things look right for mobile.

Alright then, so that works just fine, but how do we test it? A major problem we run into is in making sure the right layout is rendered for the right user agent. You have to adjust your test to pass in the right user-agent string:

# test/functional/users_controller_test.rb

  test "should detect mobile" do
    @request.env["HTTP_USER_AGENT"] = "Mozilla/5.0 " +
      "(iPhone; U; CPU like Mac OS X; en) AppleWebKit/420" +
      "+ (KHTML, like Gecko) Version/3.0 Mobile/1A543a " +
      "Safari/419.3"
    get :index
    assert_template :layout => "application.mobile"
  end

which will assert the appropriate layout is used. See the description of assert_template for more goodies.

TwitterFacebookLinkedInXINGViadeoRedditDiggEmailShare

9
May 11

Full Circle: the Return of Client Server

Once upon a time, I wrote simple client server applications in Visual Basic (VB3!) with an Oracle database back-end. My government client, who dictated requirements to me by reading DOS Clipper code aloud, was mystified by the whole client-server revolution, and impressed at what we could get done by leveraging the desktop computer to do some of the number-crunching required for this system. It turns out that, since everyone had increasing amounts of CPU power on their desktop, this newfangled client-server thing meant reduced costs in owning and maintaining mainframe server assets (which, in most cases, could be pushed off the nearest cliff).

This was the mid 1990s, and the government was finally catching on to this whole client-server thing. But the web revolution was brewing, and the hottest new language, Java was about to change everything. You see, client-server solved a lot of problems, but introduced a new one: version inconsistencies in the client could break your server. This meant paying strict attention to Progressive Enhancement, that paradigm in which each new change is magically backwards-compatible (or, at a minimum, wouldn’t break things for people on older clients).

With Java, though, this didn’t matter: since the applet actually lived on the server and was distributed via the web, each client would download the latest version of the software at the start of every session. But what about the server? In the database, subsequent enhancements to the data model have to be done in a way that prevents corruption or integrity problems in the existing data. So we still had to obey the Gods of Progressive Enhancement, lest the whole thing come crashing down around our heads.

Eventually, the N-tier system won out, and today we have the familiar architecture of browser (view-only client), web server/app server (functional/procedural business logic), and database (data storage and integrity rules), with other services thrown in as needed (server-side observers/notifiers, periodic taskings, etc.). The so-called Services Oriented Architecture can be thought of as a bunch of highly promiscuous application servers existing in a series of XML-over-http-wrapped cocoons. It’s still N-tier, but N is a really high number indeed.

Throughout all of this, the paradigm of Progressive Enhancement remained: there’s always *something* out there relying on your service continuing to meet the contracts you established when you published the service. There’s a reason that features are deprecated long before they are removed. And there’s a reason why languages and platforms are pilloried for breaking older code (I’m looking at you PHP 5.3, and you, Python 3, and I just don’t even want to look at Facebook).

So then there’s this post by Nick Morgan suggesting the end of Progressive Enhancement may be near, at least for JavaScript. Interestingly, it’s occasioned by a bit of history repeating itself; what’s happening now is that JavaScript is increasingly becoming a tool for building an independent client application that just happens to be deployed by the browser. If you don’t believe that’s really happening, then try updating your resume on Monster.com while you have JavaScript turned off. I’m betting you won’t get past the login prompt.

So, with JavaScript, we’re working our way back to the way things were under Java. Everyone gets the latest app! Only, they don’t all have the same runtime environment, so now our applet has to check for browser versions and behave differently in each one … sound familiar? We know how this ended for Java – with a move to well-controlled server VRE (and the slightly less well-controlled Android SDK) and eventual deprecation in the browser by Google (who recently disabled Java by default) and Apple (who gave up maintaining their own Java VRE).

One possible path for JavaScript, which I agree is essential to the current web experience, is that changes to the JavaScript, HTML, CSS and to browsers will make it possible to write your app DRYly, rendering HTML in exactly one spot, while exposing a JSON or JSON-like API for JavaScript clients. And another possibility is that the rise in mobile devices may make JavaScript irrelevant. The web server would then exist as a glorified API, spitting out HTML for web-based apps and JSON (or maybe Objective-C Object Notation, or Python Object Notation?) for use in mobile devices.

TwitterFacebookLinkedInXINGViadeoRedditDiggEmailShare

30
Apr 11

Dear Git, Please Give Me Uncluttered Sparse Checkout

Dear Git project,

We really need a working, uncluttered sparse checkout feature. With Subversion, CVS, and numerous other version control systems, I used to be able to store multiple subprojects in a single large repository, so I’d have a setup like this:

foo-incorporated/
→foo.com/ # Foo's web site
→app1.foo.com/ # rails app
→app2.foo.com/ # some other app

which would allow me to build a single system composed of multiple related apps, guarantee the relative directory structure of those apps, and then publish to production with a single command. I could also decide to work just on an app:

svn checkout /svn/foo-incorporated/app1.foo.com/trunk app1.foo.com

and put that working copy in my local web-served directory (~/Sites on Mac OS X), and then access http://localhost/~me/app1.foo.com to see my running app.

With git, however, if I want the same workflow, I have to maintain foo.com, app1, and app2 in separate repositories entirely, or else the local URL becomes http://localhost/~me/foo-incorporated/app1.foo.com. Then, when I want to work on app1 I have to descend an extra directory level to get there. It also means I can’t easily rely on objects defined externally. I could use git submodules, but that would require maintaining a “collector” repository with nothing but submodule definitions alongside the separate repositories for each subproject.

This gets really bad when projects get large but are still related. Then git’s sparse checkout could give me several levels of depth before I get to the one small piece of a project I really care about. The git solution seems to be to create new repositories to solve this problem, but it’s really much simpler to deal with a profusion of folders in a hierarchy than it is to deal with a profusion of repositories.

I’m really missing the strict separation between working copies and repositories that I had with Subversion and CVS. Please let me have it back.

Thanks,

Scott

TwitterFacebookLinkedInXINGViadeoRedditDiggEmailShare

25
Apr 11

Why We’re Not Using AWS (For Now)

Amazon’s cloud crashed a few days ago. We were not affected, but only because of a lucky bit of scheduling. As it turns out, we completed migrating to a more sedate host the day before the outage hit as a result of our pivot.

AWS is a great platform for just about any startup in general and for our sync project in particular. The offered scalability and the price for companies in our situation (free) is kind of hard to beat. But that price tag runs on a clock. We don’t need AWS while we are doing customer validation, so we switched.

Don’t get me wrong; the outage won’t keep us from going back to AWS when we launch our ATS. It will, however, cause us to dual host on AWS and another completely distinct, shared-nothing provider. Perhaps Rackspace, as a quick example. While the business case for a true 5 9′s (99.999% uptime) operational model is not quite there (trust me, for each “9″ you add, it’s usually at least an order of magnitude in operational costs — if not two or three), I do believe our customers will be expecting 99.999% uptime during general business hours.

TwitterFacebookLinkedInXINGViadeoRedditDiggEmailShare

16
Apr 11

Signup Form Woes (with thanks to Jim of NY)

Good news: ResumeEverywhere was mentioned in US News & World Report’s article 8 New Websites for Your Resume. Bad news: the signup form failed to, well, do anything at all.

Someone (Jim, of NY) noticed the issue and commented about it. I’ve now fixed it, by axing the offending Ajax calls. So, thanks to Jim (who I’m pretty sure is @HRNewsFeeds on Twitter).

Two important questions remain – first, what was the actual root of the problem? I don’t know right now; it was faster to remove the offending script than to analyze it. Analysis will wait until next week. Second, why did it occur on the live site, and not in our testing prior to deploying the script? This is an excellent question, as I’m convinced the signup form was working on the live site when it was deployed in February. So something must have changed since then. FRIN.

Apologies to anyone out there who found us, tried to sign up, and then gave up when the sign-up form failed. I hope you’ll consider giving ResumeEverywhere another chance.

TwitterFacebookLinkedInXINGViadeoRedditDiggEmailShare

15
Apr 11

How Resumes Are Stored

If you are a job seeker and you go to most job boards these days, you will be asked to do one of three things:

  1. Enter your resume information section by section
  2. Upload a file containing your resume
  3. Open your resume in some other program (Word, vi, Pages, et al), then cut and paste the resume text into the appropriate section on the web site

All of these approaches get the data into the resume database, but some of the approaches are better than others.

  1. Entering resume data piece by piece is tedious, but it results in clean and neatly segmented data. Which is quite helpful in making sure that your resume appears in search results in the best way possible. From a data management perspective, this is the preferred approach; not so much for the job seeker.
  2. Uploading a file has the advantage of being quick but is also quite dirty. In general. it’s a bit hard for the website to break out the data in a regular and repeatable fashion. This is particularly true as quite a few people use their own personal and unique style of organizing data. If you’ve ever uploaded a resume and wondered why the name of your university became “Sept 2004-Dec 2008″ instead of “UCLA”, you’ve run afoul of an automated process tripping up on the format of your resume.
  3. The worst of all worlds: first, the job seeker is required to take extra steps to open her resume and then manually copy the data into the website. Next, the website still has to work on dividing the resume text into meaningful groupings — this time without the helpful assistance of format cues embedded in the resume file from option #2.

Parsing

Well, the overall process is generally the same, but the specifics depend on the specific format of the file. All resume processing sites parse the resume file, attempting to allocate the resume data into neat buckets — this block of words are all associated with the work experience at firm “X” during dates “Y”-”Z”, but that block of text is about the time spent at university “A” majoring in “B” from date “C”-”D”.

Depending on how the document is formatted, the format itself — particularly with an XML variant like .docx — may contain clues within the structure of the data relating to the content of the data. In other words, metadata. While the generic overall schema specific to .docx (the MS Word format for Office 2007 and later) is not even vaguely resume oriented, the general convention amongst most resume writers is to use various formatting options (bold, italics, underline, white spaceetc.) as signifiers for changes between resume data elements.

Pretty much any format into which a resume can be saved can also be parsed. Some formats (text, docx) are easier than others (PDF, I’m looking at you); in general, it’s a matter of how much work it will take to find and correctly identify the sections of data. In the next post of this series, we’ll go into further detail for the most common resume storage formats, their associated pluses and minuses and other assorted details.

This is the first of several posts about the technical details on how resumes are commonly stored in the online world.

TwitterFacebookLinkedInXINGViadeoRedditDiggEmailShare