Recently in Business Category

Five Years of Full-Time Self-Employment

Today marks five years since I last received a paycheck. There’s a folk stat that 80% of small businesses fail in their first five years, so I can now safely put that one behind me (the business itself is nearly seven years old, but it’s questionable whether the part-time years should count).

Five years ago, I was operating solo out of a spare bedroom with a $1600 printer that I’d leased of all things, because I didn’t have anywhere near enough money to actually buy it.

Today, we have three full-time people (including a summer intern who started earlier this month) plus one part-time person, 3500 sq. ft. of office space, and a printer that costs $1600 per month.

Bootstrapping a business has its downsides, but it’s really nice when it works. This one’s working.

Rule #1 of Caching

Don’t cache something if it takes longer to retrieve it from the cache than it takes to generate it in the first place.

Corollary: Improving performance on the cache mechanism allows more stuff to be cached effectively.

This was discovered while attempting to speed up the work list and order detail pages. Fortunately, adding an index in a particular place in the database was enough to get the cache retrieval and validation to be well under a millisecond.

That, combined with roughly eight hours of tweaking code and database calls, means that an average order detail page request now only requires 182 database calls, down from 689 (74% reduction), and is generated (in most cases) in well under a second, even without touching the cache.

With the cache, it’s generated in roughly a tenth of a second (with about ten database calls for the entire request), though I still have some work to do, since it’s taking the browser between a quarter and half a second to render.

The work list load time is just under a second now, with some selective caching, down from about 2-3 seconds, using a test set of 40 active mailings. That’s fast enough for now. Database calls also dropped from 789 to 402, which is a nice bonus. At 100 active mailings, it slows down to about 1.5 seconds with the selective caching (down from about 10 beforehand), so I’ll probably need to do some more performance work once we start seeing that on a regular basis.

When "right now" isn't fast enough

Normally, my company is able to do a good job of meeting or exceeding people’s expectations for order fulfillment. We certainly try. Even during this crazy time of year, when there’s a ton of mailings, nearly all of them unusual, often under-specified, and with lots of people stressed out over sending the order a week or three later than they’d hoped, we’re keeping up (if barely, and with somewhat less sleep than usual on the part of myself and Christine, but as well as or better than past years).

Yesterday, though, an order took a turn for the strange. This particular order had been submitted the day before at 3:28pm. That’s after our mostly-unofficial cut-off for the day (3:00pm), and there were no special instructions about it being a rush, so we went through our normal practice of waiting ‘til the next morning to work on it.

Mid-morning the next day, we get an E-Mail from the customer asking how backlogged we are, how long it will take to get his letters sent, and whether it would be faster for him to send them himself. An hour later, having just talked through the (moderately complex) order with Kevin, I reply saying that we’re not backlogged, and are working on setting up his mailing now. Assuming there’s no delay in approving the order, we’d have the letters sent that day.

Nothing weird so far, but then the next E-Mail arrives. “I think I’ll send this mailing myself” (to get it out sooner). Err, what? So, I send back a reply, asking if he’s sure, and repeating that we shouldn’t have any trouble getting the mailing sent within a few hours. Maybe he’d misread the original E-Mail. But another reply comes in, saying he’s sure, so I cancel the order.

I’m guessing there must have been some other issue. It was admittedly a small list, so it’s technically possible that if he dropped everything and started work on them right away, he could have gotten the mailing sent before we would, assuming he could produce 4x6 photos at a nearby 1-hour lab with no delays during the busiest time of year, and assuming he already had a pile of the specialty reply envelopes he’d requested, along with the normal paper, envelopes, and stamps. It only would have taken us roughly 10 minutes to send his mailing, including the photo printing, but he wasn’t at the head of the queue at the time, so it would’ve been an hour or two before it got to the printing stage.

Even so, it wouldn’t have mattered — at least in this area, it makes no difference if I mail something at 7am or 8pm; it’ll still go out that day in the ~11:30pm truck. I assume the same is true where he lives (if anything, he’d probably have a 5pm cutoff), and his recipients were spread out around the country, such that there wouldn’t be any major savings geographically.

That one may very well take the cake for oddest customer interaction of the year.

Deef in Texas

Before this evening, the closest I’ve ever been to Texas has been either northern Colorado or Florida, depending on how you want to measure geographical/cultural distance. Next thing you know, I’ll be visiting California — scary thought!

Travel was pretty boring, all told. I got caught up on E-Mail (offline) during one of the flights, and played Bejeweled on the other, with a stop at Chick-Fil-A in Atlanta (albeit a rushed one, since I’d arrived in Concourse C, had a flight leaving out of Concourse B, and it was in Concourse A).

Wednesday afternoon in Manchester is apparently a great time to fly. The airport is dead quiet. No lines for check-in, security, or food. Way better than flying out of Boston.

Oh, and I should probably mention why Deef is in Texas. I joined the National Association for Printing Leadership (NAPL) a little over a month ago, hoping to improve my understanding of the industry and also to improve my company’s foundation by figuring out and implementing some best practices for the printing side of things, much of which looks like it’ll carry over into the technology side as well.

These guys have a conference every year for owners of smallish print shops, which happens to be in Austin, TX this time. So, here I am. It should be fun!

Unreleased Product has #1 Position in Google

Just for curiosity’s sake, I did a search yesterday for the name of a product that I haven’t released yet, and which I’m pretty sure I haven’t mentioned anywhere (by name, at least) on the web.

Imagine my surprise when I found it already has the #1 position in Google for its name!

Freaky. But it does explain why some people have already found said unreleased software program that was in theory only accessible by knowing the right domain name to type. The domain name is descriptive enough that a search on other terms would let you find it.

Neat SQL Query

Update a table according to the results of another query (slightly simplified):

UPDATE letters AS final
   SET preview_file_id = outer.preview_file_id
  FROM (SELECT letter_id,
               (SELECT preview_file_id
                  FROM inserts AS i
                 WHERE i.insert_id = inner.insert_id) AS preview_file_id
          FROM (SELECT letter_id,
                       MIN(insert_id) AS insert_id
                  FROM letters AS l
                  JOIN letter_insert_ref USING (letter_id)
                  JOIN inserts AS inner_i USING (insert_id)
                 WHERE l.preview_file_id IS NULL
                   AND inner_i.preview_file_id IS NOT NULL
                   AND l.submitted > NOW() - '1 month'::INTERVAL
              GROUP BY letter_id) AS inner) AS outer
 WHERE final.letter_id = outer.letter_id;

Or, in something resembling English, “In the letters table, set the preview file according to the following list of recent letters and the associated preview files for the first insert of each of those letters.”

There might be a way to simplify the two sub-queries into just one using grouping, ordering, and limiting, but this worked well enough for a one-off query.

Background: I thought I no longer needed the previewfileid field in letters, so I deprecated it recently, and stopped setting it. The API layer had a workaround, but there’s still a program that uses a database view that needs the previewfileid to be set in letters, so I needed to revert that change and back-fill the previewfileids that didn’t get set. UPDATE FROM to the rescue!

(Also, if anyone has any layout suggestions on how to make complex SQL more readable, I’m all ears. I’ve been center-aligning to the keyword for years just because I haven’t found anything better.)

Vermont Income Tax Withholding Form

Vermont’s form for submitting taxes withheld has enough boxes on the “Vermont Income Tax Withheld” line for an employer to withhold $999,999,999,999.99 per quarter.

Yes, that’s one cent shy of $1 trillion in state taxes. Vermont’s overall personal income tax collections for 2008 totaled $622 million and change ($83,042 in change, but who’s counting?), a quarter of which would be $155.5 million. So, I suppose that if Vermont has one exceptionally large employer, accounting for two thirds of all salaries in Vermont, one could actually use all those boxes.

It really makes my contribution seem rather measly, though. It’ll take some growth before I’ll be contributing $100,000,000+ in state income tax on a quarterly basis, even assuming I discriminate based on geographical origin and hire all future employees from Vermont.

(“Some growth” = using loose assumptions, and ignoring scaling issues, my customer base would need to include roughly one missionary for every ten people on the planet. Give or take.)

Credit Card Forms

Back when I was looking for office space, I gained a keen interest in loading docks. One evening, I actually drove around town looking for any building with a loading dock, to see what kinds of businesses had them, where they were located in the building, and how much the truck driver would be swearing in order to try and get the trailer to fit (i.e. what kind of driveway was involved, and whether the truck had to block traffic on busy roads in order to back in). There are all sorts, and I still go out of my way to look around when I come across a new building.

The space I ended up leasing doesn’t have a loading dock, alas (it had everything else on my list, unlike other places I was considering), but at least it’s easy for trucks to get in and out (they can back in without needing to block traffic, and it’s a straight shot).

Now, I’m in the process of improving how I handle credit cards. So, once again, I’m getting geeky with my research, this time looking intently at other sites’ checkout forms, paying for and downloading a 63-page usability report on checkout design (I’m very curious how well that site’s checkout lines up with its design recommendations), and so on.

Eventually, I figure I won’t be able to look at anything remotely commercial without evaluating its pluses and minuses in terms of usability, efficiency, and customer satisfaction.

Watching a Spam Bot at Work

I started tracking error messages on one of my production sites over the weekend, wherein I get notified whenever there’s an error, rather than it just going into a log that I’ll probably never read unless someone brings it up.

It’s a good practice — usually the errors are due to little bugs that I can fix before the person has finished reading the (customized) error page letting them know that the bug is my fault and that I’ve been notified. They hit reload, everything works, and everyone’s happy.

404s are errors, too, though they’re not always my fault, so I have a different error page for those. Sometimes they are, and I can usually fix them just as quickly. Sometimes they’re the result of people mistyping links into their own sites, and I can notify them about that, or write a redirect.

And sometimes I get insight into the latest scams, viruses, and bots that are running around in the wild. Today’s the latter. I just got quite a few notifications, all at once, from a couple dozen different IP addresses (i.e. zombies), from someone who’s apparently trying to put comment spam on one of my sites. It’s fun to watch, as it’s not going to work, for three reasons:

  1. There’s a captcha as a simple deterrent (which works surprisingly well, all told, though it’s not perfect).

  2. There’s a moderation system — posts don’t show up until they get approved.

  3. Even beyond the above two, which have eliminated all spam to date, the reason I’m absolutely 100% confident that this spammer isn’t going to be successful is because the page that all the zombie bots are trying to access doesn’t exist any more.

A note to network administrators everywhere

When making changes to your network layout, be sure to update the monitoring program as well (e.g. Nagios). If you don’t, and the IPs are different enough, and you wait a while before updating the mail server on the monitoring server…

Well, let’s just say that the queued up E-Mails are still coming in half an hour later, and it’s only for the last three days of “trouble” notifications, though thankfully it’s down to a rate of about one E-Mail per second due to throttling.

Recent Entries

Who's on first?
I’m going through the settings of a hard-core E-Mail program, and came across this lovely setting: Copy To Address to…
USPS Extreme Cost-Saving Measures
In the July 2010 edition of the PCC Insider (a USPS publication) is found the following statement: There are 26,000…
At 1:15am this morning
At a hotel, after a day of eventful travel and 3.5 hours of sleep the night before, it’s (almost) needless…

Elsewhere on the Web

My Amazon.com Wish List