Skip to content

Review of SkaDate dating software

I recently helped a client move an existing dating site from a custom ASP/MS-SQL system to an off the shelf PHP/mysql platform.

The off the shelf software we ended up choosing was SkaDate.  I haven’t really found a good review of SkaDate out there, so I asked Max Chadwick to collaborate with me on a review.  (Max provided some design, system configuration and project management, and I focused on back end system setup and data migration.)  Updated 4/2: We used SkaDate 7.5, versions 1485 and 1550.

Note that we had a challenge not present for a typical SkaDate installation: migrating ~1100 user accounts (and mail messages) from one unknown system to another.  I had to learn two very different data models and map from one to the other.  On a fixed bid project.  Whoops.

Oh well, live and learn.

Skadate pluses:

  • Price: this is a big one.  You get a lot of features for only $350.
  • Technology: it is built on the LAMP stack, so there are a lot of developers out there who can help you extend the platform.
  • Support: they have a client site with some useful PDF documentation.
  • Had a defined and documented upgrade procedure (even though it was a hassle).
  • Changing look and feel was relatively easy; we went with one of the many predefined templates and only had to hack a little bit of CSS and a couple of images.
  • Caching: SkaDate caches of php files and css.  Performance was reasonably snappy on a shared hosting account.
  • You get the source code.
  • Version control support: it wasn’t hard to find out which files/directories to pull into version control.
  • Support for 5-6 languages out of the box.  We didn’t use this, though.
  • Geographic features: SkaDate knows a lot about cities and where they are located, around the world.

SkaDate Minuses:

  • Support: they charged $90/hour $95/month for support; I didn’t end up asking them for much help, but the times I did, they immediately wanted ssh access to the server (which tweaked me out).  I’m guessing that SkaDate might be a loss leader for ‘support services’.
  • In general the administrative interface was unintuitive and could use some work.
  • Intricate object and data model: lots of indirection, and because it is PHP, no IDE to help you unravel it.  I discovered this when asked to turn off a particular feature (that didn’t have an admin setting)–I’d have to hunt through 3-4 files to find out where a UI element was set up.
  • Secretive nature: they don’t really give you any documentation until you pay for it; however, they do provide a demo and when I emailed them and explained the situation (“I’m a developer and want enough information to do a bid for a project”) they responded with some of the documentation.
  • They had a new release midway through the project, and then a user found several of the php files had been hacked.  Not exactly confidence inspiring.
  • Initial configuration of the site was complicated: all the site features were turned on and the site was pre-configured with specific payment/membership options. The tricky part was not just turning off features, but figuring out that not only did the feature need to be turned off, but that the navigation needed to be disabled for that same feature on another section of the admin area.
  • Setting up custom dating fields was cumbersome partly because of the poor interface but also because there were a number of dating fields already set up that needed to be removed.
  • The software uses dollar signs ($) in some of the automated directories.  This caused an issue with mod_security on one of the hosts we tried to use; go with one of their suggested hosting providers.

I think SkaDate is a good choice for a basic dating site; if you need just the features in the demo and you’re willing to spend some time unraveling the administration UI and you are on a tight budget. Expect some bugs and frustrations, but hey, you only paid $350!
I would hesitate to use it as a platform for a more fully featured dating site until I’d reviewed the alternatives.

Final grade: B-

[tags]skadate review,dating site software[/tags]

BrowserMob: Load test your applications using the cloud

Via this tweet from Matt Raible, I learned of BrowserMob.  This service allows you to easily load test your web application.

I set it up in about 2 minutes to do a simple load test of a client’s site (though 5 pages).  They make it free to ‘test drive’ their service (though the free not enough to actually stress your site).  It is extremely easy to test a path through a publicly facing system.

The report was good enough; you get screen captures of pages that have failures, and they do a good job of making some of the performance data pretty and intelligible.  Again, I didn’t really load test anything, so I didn’t examine the report as closely as I would have in a real world scenario.  The service is built using Selenium, and I believe they allow you to upload full featured selenium tests (if you have already invested in this technology, but don’t want to build out a cloud network).

This service is of particular interest to me because last year I was part of a project that built a selenium grid on Amazon EC2, using these instructions.

If we’d known about BrowserMob, I’m not sure we would have used them, as I don’t know what our budget was, but it would have been nice to have that in the evaluation mix.

[tags]browsermob, cloud services,load testing[/tags]

Moving data from MS-SQL to mysql

I recently worked on a project where I needed to port data from a MS-SQL database to a mysql database.  There are programs, both payware and freeware that will help with this process, but I didn’t have ODBC access to the MS-SQL database, which these programs require.  All I had were a bunch of insert statements that looked like this:

INSERT INTO
[sample].[dbo].[users_info]([ID],[registration_day],[registration_month],
[registration_year],[registration_time],[first_name],[last_name],
[username],[userpwd],[repeat_pwd],[birth_date],[birth_day],[birth_year],
[street],[state],[city],[zip_code],[email_address],[membership_startdate],
[online],[gender]) VALUES(463,N'15',N'7',N'2009',N'9:13:52 AM',N'Homer',
N'Simpson',N'homerrocks',N'beerbeer',N'beerbeer',N'January',N'1',N'1999',
N'234 Main',N'Alaska',N'Springfield',NULL,N'homer@thesimpsons.com'),
CAST(0x9AE90000 AS SmallDateTime),
CAST(0x00009CD700000000 AS DateTime),N'Man');

I wrote a perl script to turn that dialect of SQL into a mysql friendly dialect (feel free to download it).

The most interesting parts were those CAST statements.  This forum post and this blog post helped me turn those casts into real dates.  (After loading the inserts into mysql, I did some post processing, using the helpful case statement and str_to_date function to rationalize some of the data.)

[tags]mssql, mysql, data migration[/tags]

How to get IE to accept third party cookies

Third party cookies are most often used by ad serving companies, because they are set by an image or other resource that can be pulled from a different domain, they are one way of tracking behavior across websites.

A short example: If I visit site A (www.foo.com) and site B (www.bar.com) and they both pull an image from site C (www.baz.com), it can set cookies for the site C domain (.baz.com), with a value of siteA or siteB.  Then when I visit site C, it ‘knows’ that I’ve been to sites A and B.

This can be sinister (see the wikipedia link above for privacy concerns).  However, if you have multiple web properties, then you may want to tie user behavior together across properties.  Third party tools like Google Analytics offer one way to do this, but if you want custom application behavior, then third party cookies are probably the way to go.

Firefox (at least my version of firefox, without any add ons or options changes) treats third party cookies much the same as first party cookies; that is, they just work.  However, IE requires a bit more hoop jumping, as they check the p3p compact policy.  A compact policy is basically a header you set which looks like this: P3P: CP="CAO PSA CONi OTR OUR DEM ONL".  I looked around for an easy explanation of what the various values are and how to set them in a coherent manner, but didn’t run into anything very useful.  However, I finally stumbled on this tutorial, which points you to this IBM software, which you can use to create a compact policy.  The tutorial also walks you how to use that software, which is not entirely intuitive.

As far as I can tell, IE doesn’t actually check for the existence of the corresponding policy file, nor does it care if the site does what the p3p header says it does, but it does require a valid compact policy.

After you’ve added that header, IE (versions 6-8) will accept your third party cookies.

[tags]third party cookies, compact policy, howto[/tags]

Upgrading to GWT 2

I recently had the pleasure of upgrading from GWT 1.5.3 to GWT 2.0.  The client has a variety of GWT applications–some standalone widgets that integrate into an existing site, an application written with GWT look and feel, and an application written with the GWT-EXT library.

The upgrade to GWT 2.0 was much less painful than I thought it would be.

Even code that was written for GWT 1.0 (the mortgage calculator, written back in 2006) compiled cleanly (well, plenty of warnings, but no loss of functionality).  Classes that have been deprecated (some for good reason; hello HTTPRequest, I’m looking at you) have not disappeared.  I’m not saying that the code I wrote for GWT 1.0 shouldn’t be rewritten, just that GWT hasn’t forced me to do so.  Also, other than changing some switches to the command line compiler, the ant script used to compile the GWT modules didn’t have to change at all.

Hosted mode was a different story.  I never really spent a ton of time in hosted (err, dev) mode, but had it working in that past.  Now, I wanted to get it working fully because of the tremendous productivity gains possible–no minute long compile cycle to pull my attention away from my code.  And the fact that you can run and debug in eclipse in more than just one browser is really attractive.  Since I’d never really had it working fully, there wasn’t a lot of upgrade work; it was more like starting from the beginning.  However, I was unable to use the internal jetty server.  Again, my use case is atypical, with a lot of JSON and JSONP requests for data.

Big pluses for GWT 2.0, right off the bat:

I haven’t yet looked into Speed Tracer or some of the updated widgets (I’d really love to toss the gwt-widgets lightbox we use in favor of the GWT standard PopupPanel).  There’s also the tremendous benefit of being on the modern version of any platform–it is easier to get support in forums and in other venues.

GWT widgets and code splitting, a match made in heaven

If you are writing a typical GWT application, which is monolithic and controls the entire viewport of the browser, you probably don’t want to read this post.  Go on, read something else interesting–you probably have emails or tweets or something better to do with your time.

OK, now we just have the people left who are using GWT to build widgets; that is small encapsulated pieces of functionality that integrate into an existing web based application (case #2 outlined here).  If you’re doing this, and you use the “span to enable” gwt mini pattern, you want to upgrade to GWT 2.0 simply to get the new code splitting functionality.  If you don’t want to read that previous link, the synopsis is that code splitting lets you define a number of pieces of distinct code, using GWT.runAsync.  Then, that code won’t be downloaded until it is reached.

Previous to this feature, if you had a number of widgets, you ended up with a large chunk of code to download on every page (this is an issue with GWT that the monolithic applications simply don’t have to deal with).  Some of that code will be run.  Some will not, but you’re still paying for download and parsing of that code.  You had some unsavory options to deal with this–let all the code be downloaded, or manually split up code into separate modules that you managed (either by hand or with deferred binding).  The second solution led to smaller downloads, but meant a lot more management–if you wanted to add a widget to a page, you not only had to add the enabling span, you had to recompile the entire GWT module–and much longer compilation when you deployed your entire web application.  However, if your widgets were static, this path might have been an option.

My client used the former solution (entire code download on every page), and was very excited about the code splitting, since that essentially automates the second choice above.  In the space of about one half hour, I was able to reduce the initial download size of the GWT javascript by 10%, and there’s scope for much more, since the code is pretty naturally split up into separate chunks for each widget.

It’s not perfect, however. The two concerns I’ve had so far:

  1. The XS linker is not supported.  This means that if any of your widgets need to be cross domain, you need to create an additional module specifically for that.  For example, if I have one module, A, which is used to start up all the widgets on the site, and inherits from both module B, which has some GWT code split calls, and module C, which needs to be cross domain, the compiler will error out when compiling module A.  I need to create a second module XSModuleC which inherits from module C and is compiled by the XS linker, and then use that module for all cross domain purposes.
  2. If you call GWT.runAsync from an event handler, like onClick, you will not have a valid event on the first call (when the module is loaded) but will on all subsequent calls.  This is easy to fix, but was a bit mystifying to me.  Basically, if you have code like this:

onClick(ClickEvent event) {
if (event.getSource() instanceof Image) {
// do something with image
}
}

you need to replace it with:

onClick(ClickEvent event) {
if (event.getSource() instanceof Image) {
// save event.getSource into an instance variable
GWT.runAsync(new RunAsyncCallback() {
// do something with image in instance variable
}
}
}

I'm sure there are other complications I'll find once I do more code splitting.  (Here's an interesting post about code splitting in large applications, and simplifying the API of code that is split (plus, you get to see the word cromulent in context).)  But, for now, code splitting and GWT widget development seem like a match made in heaven.
[tags]gwt,code splitting[/tags]

Finding supported GWT user.agent values

The GWT compile process has been taking longer and longer, as I’ve moved from 1.0 to 2.0, because they keep adding optimizations and functionality.  You could deal with this in a number of ways.  You could write less GWT.  You could buy a faster computer.

More realistically, you could live in your IDE, and run in development (nee hosted) mode.  However, sometimes you just have to test with javascript.  The external browser development environment can be tough to set up correctly, especially if your application depends on external resources, and you can run into weird bugs.  I’m currently dealing with an issue where I depend on an external library and it is consistently throwing an assertion exception in development but runs fine in production mode.

If I am compiling to javascript repeatedly, I often compile for just one user-agent, which saves about 80% of the compile time.  When I have code more put together, I can compile for all other browsers.  This process is not flawless, as I am currently debugging a cross browser issue (something that works fine on FF doesn’t work on Safari) but I probably wouldn’t have tested on Safari until after I was through the lion’s share of development anyway.

The way to target just one browser is to put this string in your module (.gwt.xml) file:

< set-property name="user.agent" value="safari,ie6,gecko1_8" / > (remove the spaces next to the angle brackets)

How do you find valid values?  Via this thread, I found that you look in UserAgent.gwt.xml, part of the gwt-user.jar file.  This has the javascript code that looks at navigator.userAgent.  It is not as fine grained as the ubiquitous browser detect script, but shows you what different browsers are known to GWT.

For other tips on speeding up the compile process, check out this series of posts.

[tags]gwt,user.agent,gwt compiler, slow as molasses[/tags]

Andrew Hyde: tips for your startup job hunt

Andrew Hyde, Boulder startup hub, gives some tips on the startup job process.  Having been a part of a few startups, I thought I’d review the high points of the list, but it’s worth a read regardless.

Lots of his tips are useful to people hunting for jobs at established companies as well–I’ve long been a believer in personal blogging (#12) as a ‘living resume’ (among its other benefits), and the research required to write a blog post about the company (#10) will be useful to you as well.  Here’s another post with good tips.

I especially enjoyed his riff on generalists: “In the history of startups, not a single ‘generalist’ has ever been hired.  They are called founders.” I’m not sure I entirely agree, but I do think that if you want to be hired for money at a startup, you should have a strong focus (whether that be development, marketing, sales, operations, etc, etc).  However, even if you are hired as, say, a developer, you’ll need to do other things (testing, customer service, perhaps even marketing)–that’s part of the fun.

He does give some contradictory advice: have a resume in PDF (#2) but at the same time “ditch a resume” (#8).  I think he means that while a resume is nice, startups are more interested in specific projects and achievements than a typical big company with an HR department.  Some argue one should approach all job interviews in the same manner.
The most important piece of advice that Andrew gives is #7, which is worth quoting in full:

Be clear.  You are looking for a job.  Cut the buzzswords, what is the best fit?  Steady?  Fast paced?  Live in Boulder?  Just say it.  Cut the shit.

In my job hunts, knowing what I actually wanted was the hardest task, but the most important.  Why is it hard?  When I’ve been looking for a job, I am anxious and concerned about the future, often thinking about my slowly draining savings.  Declaring what I actually want (beyond “a job, any job, please God, give me a job” which is sometimes how I feel 🙂 ) necessarily excludes opportunities.  However, that is the exact reason that it is the most important task.  By excluding opportunities that wouldn’t be a good fit, or would make me bitter, or wouldn’t serve the employer well, I freed myself up to focus on situations where I could help the employer succeed and be happy.

[tags]startup jobs[/tags]

Real life inspiration from laid off ad agency workers

“Lemonade” is a great 35 minute movie about people making lemonade from getting laid off.  The “lemonade” ranges from coffee roasting to yoga; artist to blogger (on ads, or on employment).

The content is entirely interviews with people were laid off from advertising agencies.  But the advice is good for anyone.  As always, good advice is easy to give or hear and hard to follow.

Here’s four ways to support the movie makers if you should be so inspired.

Via Seth Godin.