Thursday, November 3, 2011

BucketSoft's First Profitable Month

These are exciting times. October was by far the best month for Regex Hero ever. In total there were $450 in sales. Now, I must say that I have also been doing some contract work on the side to earn some cash for BucketSoft and keep the lights on, so to speak. So technically I've had profitable months before this. But if you're just counting product sales and company expenses then October is the month to beat.

Traffic hasn't changed much during the past few months. It's consistently been at around 250 visits per day. So how can we explain this success? Well, in part I think it's due to the new Standard / Professional version strategy with Regex Hero. It's wild to think that you can spend hundreds of hours on design and development and only see a slow trickle of sales. Then by employing a simple marketing trick, the sales take off.

In fact, here are the sales before and after I began selling the two versions...

Before (July 1st to August 31st)
$135

After (September 1st to October 31st)
$510

That's nearly 3.8 times more sales in the two month span following this change. Of course I can't attribute all of that just to this change. I've also added some functionality to Regex Hero Professional that allows you to save your regular expressions to your local computer (rather than saving it online). And then I added multithreaded regex matching and highlighting. Both are welcome improvements, but due to the timeline in which they were released it's impossible to know exactly how much these improvements affected sales.

Regardless, it's obvious that BucketSoft is doing better. I still love working on Regex Hero and I'm encouraged to see what the future might bring.

Thursday, October 6, 2011

Avoiding Race Conditions with a BackgroundWorker

Today I released an update to Regex Hero, adding asynchronous regex matching and highlighting. This is something I've attempted in the past. But my past attempts resulted in a race condition.

What is a race condition you say? Well, simply put, it's when two or more threads race to an event or line of code. Sometimes one thread will win the race, and sometimes the other thread will. What this comes down to is that this inconsistent behavior can cause some strange and unexpected results.

Now, often times when I've used a BackgroundWorker, it's for a fairly long running task. The task executes and the user can cancel if they want (which will call CancelAsync behind the scenes). And everything's just fine.

But what if the application calls CancelAsync() very frequently (after every keypress), and every time it does you want the BackgroundWorker to stop, and then start over? And what if the execution time ranges from a couple milliseconds to a couple seconds? Well, as I found, these characteristics can be problematic.

From MSDN's documentation for the BackgroundWorker.CancelAsync Method:
Be aware that your code in the DoWork event handler may finish its work as a cancellation request is being made, and your polling loop may miss CancellationPending being set to true. In this case, the Cancelled flag of System.ComponentModel.RunWorkerCompletedEventArgs in your RunWorkerCompleted event handler will not be set to true, even though a cancellation request was made. This situation is called a race condition and is a common concern in multithreaded programming. For more information about multithreading design issues, see Managed Threading Best Practices.

This is exactly what happened to me. With every keypress I'd check the BackgroundWorker IsBusy flag. And if it was busy I'd call CancelAsync(). Then if the Cancelled flag was set to true, I'd call RunWorkerAsync() to start it over. The thing is, as noted above, calling CancelAsync() doesn't guarantee that the Cancelled flag will be set to true. And this messed everything up since it meant that the last call to the BackgroundWorker wasn't always made. So if you're familiar with Regex Hero, you can imagine a situation where the highlighted matches aren't accurate because it's not in sync. And that's bad.

But of course I found something that works, and that's what this post is all about. I've rewritten the code and made a simple example to make it easier to follow. This example is in Silverlight, but of course the same could be done in the full .NET Framework.




I start by declaring a few variables and initializing the BackgroundWorker...


private Boolean PerformHashRetry = false;
private HMACSHA256 sha256 = new HMACSHA256();
private UTF8Encoding utf8 = new UTF8Encoding();
private BackgroundWorker PerformHashWorker = new BackgroundWorker();

public MainPage()
{
 InitializeComponent();

 PerformHashWorker = new BackgroundWorker();
 PerformHashWorker.WorkerSupportsCancellation = true;
 PerformHashWorker.DoWork += PerformHash_DoWork;
 PerformHashWorker.RunWorkerCompleted += PerformHash_Completed;

 txtBox.TextChanged += new TextChangedEventHandler(txtBox_TextChanged);
}

void txtBox_TextChanged(object sender, TextChangedEventArgs e)
{
 PerformHash();
}

Here's my PerformHash() function, which decides whether to run the background worker, or cancel it and signal it to run again...


void PerformHash()
{
 if (PerformHashWorker.IsBusy)
 {
  PerformHashRetry = true;
  PerformHashWorker.CancelAsync();
 }
 else
 {
  PerformHashRetry = false;
  PerformHashWorker.RunWorkerAsync(txtBox.Text);
 }
}

Then the PerformHash_DoWork() function is pretty straightforward. I've set it up for 10,000 iterations so the delay will be noticeable. However, notice how it checks for the CancellationPending flag, so it can terminate early if necessary...


void PerformHash_DoWork(object sender, DoWorkEventArgs e)
{
 for (int i = 0; i < 10000; i++)
 {
  Byte[] HashValue = sha256.ComputeHash(utf8.GetBytes((string)e.Argument + i.ToString()));
  e.Result = utf8.GetString(HashValue, 0, HashValue.Length);

  if (PerformHashWorker.CancellationPending)
  {
   e.Cancel = true;
   return;
  }
 }
}

Last, but certainly not least, there's the PerformHash_Completed() function...


void PerformHash_Completed(object sender, RunWorkerCompletedEventArgs e)
{
 if (PerformHashRetry || e.Cancelled) // instead of relying solely on the e.Cancelled flag, we're also using our global PerformHashRetry variable
 {
   // This is where the magic happens.  
   // If the BackgroundWorker has been flagged for a retry, 
   // then we call PerformHash to start this whole process over.
   // By doing it this way, race conditions can be avoided and
   // the last call to the BackgroundWorker will always be made. 
  PerformHash();
  return;
 }

 txtHash.Text = (string)e.Result;
}

Download AvoidingRaceConditions.zip (Visual Studio 2010 project) to try this out yourself.

Monday, September 5, 2011

The Benefits of Selling Standard and Professional Versions

In February I posted a question on the Regex Hero Blog, which trial experience do you prefer? There aren't a great many people who read that blog so I only got 3 votes, but they were unanimous...
  • Make Regex Hero full-featured for everyone but with a nag popup every 5 minutes for non-paying customers. 3 votes (100%)
  • Leave it as is with a trial where you can sign up to evaluate the software uninterrupted for 64 days. 0 votes

3 votes is not as conclusive as I'd like but I did think it was interesting that everyone preferred the full-featured / nag popup approach. This was a surprise because I thought everyone hated popups. In addition to this bit of information, I learned something valuable about marketing from Dan Ariely. From his book Predictably Irrational, he states, "When Williams-Sonoma introduced bread machines, sales were slow. When they added a "deluxe" version that was 50% more expensive, they started flying off the shelves; the first bread machine now appeared to be a bargain."

So the past few days I've been working towards a solution that I'm hoping will make most people happy while also resulting in more sales. Essentially I've ditched the free-but-limited version entirely in favor of a new "Standard" version which sells for $10. The Standard version has more features than the free version it replaces, but not as many as the professional version. And like the free version, the standard version is what people will be using when they first come to the site, except that they'll see this old nag popup every 5 minutes...


The whole nag popup thing is annoying, but then again so is a limited trial. At least the nag popup has some advantages. Because you can close the popup as many times as you'd like, this allows for a variable trial period. By contrast, sometimes a potential customer could be annoyed by a 30-day trial if they're not using the software frequently enough to properly evaluate it during those 30 days. Then they'll want to re-evaluate the software months down the road, but they can't because it's expired (happens to me all the time). It also means that the user doesn't have to register or do anything special to start the trial. They literally navigate to regexhero.net/tester and just start using the software. I think breaking down that barrier to entry has huge advantages.

And then the "Professional" version has moved upmarket to $20, and it now provides the ability to save your regular expressions locally with the desktop version. This whole Standard / Professional idea was taken right out of Dan Ariely's book. It's nothing new of course, but I never understood what influences having different product editions could make on the buying decision. If the anecdote about bread machines has any parallels to software, one could assume this will result in more sales of Regex Hero. So I'll track sales for the next couple months and post back here with the results.

UPDATE
You can read all about the results of this change in my new article, BucketSoft's First Profitable Month.

Saturday, June 25, 2011

Amazon CloudFront vs RackSpace Cloud Files Performance Testing

I've been using Amazon S3 coupled with Amazon CloudFront as my CDN for about half a year now. It's been a great service. It's fast and cheap, and relatively easy to set up. There's no monthly fee as they charge purely on usage. My sites are only receiving a few hundred visits a day collectively. And all I'm using the CDN for are small images so far, so it's only been costing me about 10 cents a month. Yep, 10 cents.

But after a recent post at Coding Horror, I was left wondering if perhaps there was something even better than Amazon CloudFront that wasn't orders of magnitude more expensive.

Turns out, there is. It's called RackSpace Cloud Files. Pricing is comparable to Amazon and is based on usage, but RackSpace uses the Akamai network. Akamai has been around a long time and has a huge widespread network with servers deployed in 72 countries. As a result their speed is good pretty much everywhere.

So I signed up with RackSpace today and performed some tests with webpagetest.org. I tested 7 locations which I chose loosely based on the traffic I actually receive from different parts of the world. And to keep it simple, I was loading an 18KB jpg. Because bandwidth on these test servers can sometimes fluctuate, I ran each test twice and recorded the faster times for each respective location.

Below are the results...



So RackSpace wins in 5 out of 7 locations. If you add up the load times of this sampling you'll get 5.645 seconds for Amazon CloudFront and 4.203 seconds for RackSpace Cloud Files. So RackSpace is about 34% faster in my tests. The location that really stands out is Australia which is roughly 4X faster with RackSpace. And then Amazon seems to be faster in India, but not by nearly as large of a margin. Some of the others are a little closer to call, and it may be a different story at a different time of day (too many variables to predict). But it's pretty clear that the abundant Akamai network being available in so many locations has its advantages.

At this point though, the big reason I'm switching to RackSpace is that they support gzip compression natively. Up until now I've mostly just been using a CDN for images, where gzip doesn't help a thing anyway. But I'd like to push my css and js files to a CDN if only the CDN supported gzip. You can use gzip with Amazon CloudFront if you set up a Custom Origin, but this looked like a bit of hassle.

RackSpace, however, enables gzip compression automatically for any css or js file you upload. So now that gzip is at my disposal, I'm in the process of moving even more content over to the CDN world in hopes of making my sites unbelievably fast.

Thursday, May 12, 2011

Windows + Eye Tracking = Awesome

I have a habit of checking out the videos at Microsoft's Channel 9 every day. I tend to skip around but in general I like to stay up to date on what's going on. Today they posted one of the coolest videos I've seen in a long time... A New NUI - Tobii Eyetracking Hardware

(I linked to the start of the demonstration because talking about it doesn't do it justice.)

Tuesday, May 3, 2011

Following My Dream : A New Life in Colorado

I've had a dream for years to create my own software company and start a new life in Colorado. Of course I founded BucketSoft back in 2009. But today I'm proud to say I'm living at 6,500 feet. I moved into my new apartment in Colorado Springs on April 11th.

The apartment is in southwest Colorado Springs near Cheyenne Mountain (home of NORAD). It's a truly beautiful part of the city, and I think I'll be happy here.

I've taken a few pictures around the general area...


This deer was just kickin' it behind my apartment.




The nearby mountains.




This is on the way to a park a couple miles away.




I just like this picture.




Overlooking Colorado Springs from Cheyenne Mountain Zoo.


As I've said before, Colorado Springs strikes a nice balance. It's beautiful, but it's not too expensive or too remote. I mean, I have every modern convenience up here including a smoking fast internet connection. Of course it'd be nice if there was an even stronger technology presence here. Most of the big technology companies, conferences, and such are going to be in Denver or Boulder. Nevertheless, it's a pretty cool spot.

Wednesday, April 20, 2011

My Favorite Marketing Videos

I constantly look for inspiration and education outside of programming. After all, programming all the time makes Steve a dull boy. The talks below were truly remarkable and are worth sharing.

First up is Malcolm Gladwell who talks about the revelations behind spaghetti sauce made by Howard Moskowitz. He sheds some light on variances in human behavior, which all comes down to personal taste. He even demonstrates how an individual doesn't necessarily know what they want. As Howard Moskowitz puts it: there is no perfect sauce, there are only perfect sauces...



Dan Ariely is a behaviorial economist. His research is truly fascinating and in many cases defies logic. He talks about how seemingly irrational human behavior affects major decisions. And you don't have to know much to see the implications in marketing...