Sunday, August 16, 2015

DIY Call Center with Call-Forwarding

One thing most startups need, but can't afford to spend much time setting up: a hotline.

We first started with a phone lying around the office dedicated to the hotline. This is very easy to set up and is also cheap. The problem we faced: who answers the phone after office hours (a startup might want to support customers even on weekends)? No one it seems.

The solution to our problem was a DIY call center, based on Twilio: customers call a number hosted by Twilio, which forwards the call to up to 5 different phone numbers. The first one to pick up can speak to the customer immediately. This works fine for us right now, however, it also brings a few problems with it:

  • It seems the lag increases drastically (varies from call to call though) because - I think - the call always flows through Twilios datacenter first.
  • For the same reason (call constantly goes through Twilio) you have to pay although you're being called. No big deal as long as you only receive a few calls a day.
  • Nobody knows if someone else picked up eventually (nor do you know who picked up). This can be solved using a separate internal chat of course, but it should be optimized in the future.
  • If one of the phones forwarded to is offline the whole hotline is unreachable for some reason. I'm sure this could be fixed with some manual tweaking.

Setup is incredibly easy:
  1. purchase a phone number on twilio.com
  2. configure the "Request URL" to be the URL generated by the Simulring Twimlet
  3. wait for calls
  4. ...
  5. hopefully PROFIT

AppEngine Datastore: Best Practices

After working with AppEngine and its Datastore for several months now (in production!) there's a few things I learned. Although I think you have to get some hands-on action first in order to fully grasp the concept and how to work with the Datastore, I think it's still useful to read a few tips first, keep them in mind and eventually implement them when you hit a wall for the first time during development.

The thing with consistency

It took me some time to fully understand "eventual consistency" vs. "strong consistency". This article describes it very well: Introduction to data models in Cloud Datastore

In a nutshell, eventual consistency does not guarantee when updates to the Datastore will be visible to your server, whereas strong consistency does. Usually eventual consistency is enough: fetching a list of products, etc. There is a very important usecase for strong consistency though: checkout / billing / everything that touches money. You don't want a transaction to disappear...

To put a long story to an end: use ancestor queries for strong consistency!

Speedy Datastore writes

Obviously, smaller entities are written faster than big entities. However, there's another thing that can slow down Datastore writes, even for the smallest entities: indexes! Imagine you have an entity with only a few properties, but index those in all variants (ascending, descending, etc): for each new and updated entity the Datastore has to update indexes accordingly resulting in - again - Datastore writes.

Here's how to calculate the cost of a write: Understanding write costs
And here's the overall process of saving an entity and updating indexes: Life of a Datastore Write (make sure to take a look at the articles linked in "Related links")

Tip: use indexes wisely (I usually don't create any until I really need them) and use unindexed properties for things you'll never have to query anyway!

Default values for all properties

Of course it seems convenient to use null-values for properties where there is nothing to save (yet). However, null-values are not queryable in the Datastore! Such entities simply won't show up in any query based on the property that's null. The only way to find properties with null-values is iterating all entities of a kind. Nothing you want to do repeatedly!

Upgrading your model and cleaning up your data

Chances are high you won't stay with your schema forever (sorry!). Whenever I need to adapt our schema I use the Remote API to iterate all entities and change them accordingly. Same goes for cleaning up a mess or setting "Default values for all properties" ;)

Easy refactoring and maintenance for Datastore-related code

There's two more things I'd like to mention, although they're not specific to AppEngine but good code in general:
  1. Keep all code related to the Datastore in a class (in the best case one class per kind) and do not scatter different queries across your whole code.
  2. In those classes per kind, store names of each property and the name of the kind as constants. This allows you to rename a property in a breeze!

I hope to be back with more tips as my journey with AppEngine continues...