This year, Apple delivered their in-app subscription system, allowing developers to leverage the payment processing tools of iTunes to offer automatic recurring billing for products made available in their apps. We built a wrapper around Apple’s toolkit to make it easy for developers to take advantage of in-app subscriptions. We’re very proud of EnrollMint (http://enrollmint.com), and we’ll be releasing it to the developer community soon. Over the years, we’ve learned a lot about software-as-a-service (SaaS) order fulfillment, and one thing stands out above all when working with subscriptions in the App Store – migration.
The Problem: Tiered Pricing
If you are using a subscription product to unlock a feature in your app, EnrollMint will work perfectly for you right out of the box. If, however, you plan to offer a tiered pricing structure, where each product unlocks some quantity of A and some quantity of B, you’ll quickly discover a significant deficiency in the App Store subscription system. Apple has built an excellent system for processing recurring payments, but their system does not allow pro-rating or cancellation, either by customer or by vendor. That means you have no obvious way to allow customers to upgrade or downgrade from their current tier to different tier. There is no way to pro-rate a purchase. There is also no way for the customer or vendor to cancel a subscription. The customer can mark a subscription as “do not renew,” but the vendor can not. There are also no refunds. Once the customer has purchased a product, it’s a done deal.
Before we discuss the solution, let’s have a look under the hood of both the simple case and the tiered pricing case to get a feel for what’s involved.
Case Study: Feature Unlock
With a single product that enables or disables access to a feature in the app, the app simply checks the expiration date of the subscription for a given customer and a given product identifier and compares the result against the system time. If the expiration date is in the past, disable the feature. Simple. Even better, EnrollMint does all this for you. All you do is call a productActivated method, and it returns true or false.
Case Study: Tier-based Metering
When you have a range of products, each allowing a quantity of items, services, or some other key feature of your app, you can’t simply call productActivated and set your allowed quantity based on the product identifier. If the customer wishes to downgrade, say from a plan that allows 5 items to a plan that allows 3, you’ll encounter the following problem. First, the customer buys the more expensive plan and your app assigns 5 allowed items. Then, when the customer decides to downgrade, they purchase the less expensive plan and your app assigns 3 allowed items. At first, this sounds fine, except there is no way to cancel the original plan. While both plans are in effect, the customer is entitled to use the product and/or service they purchased, but they’re only able to use the allowance for the product they most recently purchased.
The Hack Solution: Additive Composition
Since you can’t directly map a specific product identifier to a quantity of allowed items or service level, the best solution is to add up the allowance for all active products and use the result. This adds some complication to your code, as this is not a feature offered by the InventoryKit framework that EnrollMint uses. It also adds an additional step the customer must take to migrate to a different subscription level. However, it is an effective strategy for guaranteeing that features purchased by the customer are available for use until their expiration date.
The Awesome Power of Asynchronous Functional Programming
In the object-oriented world, where everything is a container for mutable state data, it’s critically important to be aware of the number of threads that may potentially access this data. Much of the time spent designing and testing multi-threaded software applications is focused on identifying shared mutable data – areas of the code where data is being accessed concurrently from multiple threads. This poses a significant challenge to development teams around the world and can be a key factor in hiring decisions. In fact, the first three questions I ask in interviews center on this very issue. So, how do development managers mitigate this risk without compromising agility? The answer is simple if you’re working with a modern language, like Ruby or Objective-C. If you’re working with just about any Windows platform, your tools are limited, so most of this post will not be useful to you. Otherwise, read on.
0. Assumptions
This post generally assumes you’re developing a desktop or mobile app on the Mac or iOS platform (using the Cocoa SDK). Asynchronous programming doesn’t find much utility in the vast majority of web app workflows. There are some situations where web apps can take advantage of asynchronous processing, but that’s beyond the scope of this discussion. Here, we’re primarily concerned with user interactivity and effectively managing information being presented to or collected from the user. The techniques are basically the same, regardless of whether the information is stored locally via database or remotely via web service or other mechanism.
1. Closures
A key component of the functional programming methodology is the encapsulation of procedural code into discrete segments, called closures or lambdas, that perform some specific operation on a set of input parameters and produces a set of output values. Depending on the language, they may have another name. For instance, in both Ruby and Objective-C, they’re called blocks. Closures are typically anonymous, which means they are not explicitly associated with a class or object type. This has many advantages. The behavior defined within a closure can be defined within a class, allowing procedures within the closure to access fields of its enclosing object. It can also be injected from an external source. This flexibility offers a wide range of options to the developer.
2. Queues
The discrete nature of a closure is especially well-suited for process queuing. Once a block is created, its logic can not be changed, but that logic can be used repeatedly with different input parameters. Each instance of a block can be added to a processing queue, including its input values, for execution dictated by the configuration of the queue. On the Mac and iOS platforms, there is a very powerful framework called Grand Central Dispatch (GCD). GCD provides two mechanisms for process management. The developer can programmatically choose to execute a block on the main thread or a background thread. This makes it really easy to control the process flow and be sure that code responsible for updating UI components only runs on the main thread, while CPU- or time-intensive operations only run on a background thread. You can also assign priority to blocks executing in the background.
Even better, closures can be nested inside each other. That means you can define a time-intensive process to execute on a background thread and subsequently update the UI on the main thread. GCD gives you the peace of mind to be absolutely certain which thread your code is executing on. For tasks executed on the main thread, you can be sure that these tasks are executed in a first-in-first-out (FIFO) order. The same is not true of background threads, as GCD manages thread resources across the entire operating system and may change the number of active background threads your application can work with at any time. One thing that’s important to remember when working with background threads is that tasks dispatched to the background queues may not be executed in the order they are added. In fact, if the system has available resources, it may execute multiple tasks concurrently.
3. Immutable Data
Most object access inside a closure is read-only. When using a Cocoa block, you must explicitly flag object instances as mutable in order to change their state from within the block. This is due to the fact that everything inside the block retains a copy of the original, not the actual object. You can change the state of the object, but it will not be reflected in the original as you might expect.
Note: The self object is an exception to this rule. That means you can access properties and invoke methods on self to save state.
This is a significant departure from the paradigm most C programmers work with. In C, the best practice is to pass variables by reference, allowing a function to mutate the references directly instead of returning a value. This is an elegant and efficient solution in a single-threaded environment, but it breaks down when a second thread is added that might need to access the same references concurrently.
Working with immutable objects and collections has advantages and disadvantages. The disadvantages are simple. Any time a change is made to an immutable object’s state, a new immutable object must be instantiated as a copy of the original, with all its contents carried over, plus the changed state. Instantiation of objects is computationally expensive, whereas modification of a mutable object requires nearly zero resources. The advantages are equally simple. Since an immutable object can never be modified by any thread, there is no risk of concurrent access violation. As such, there is no need to coordinate access with a locking mechanism. Locking is computationally costly and can lead to deadlock. By working with immutable objects, no locking is needed.
4. Callbacks
One of the most common examples of an asynchronous workflow is the retrieval or modification of application data. Whether the app is working with a local database or a remote web service, the workflow is the same. The user requests some information by clicking or tapping on a UI control. The system then dispatches this request to a background thread, where the information is retrieved from the data source. Once the data is loaded, a callback closure is dispatched to the main thread, so the UI can refresh with the loaded data.
Closures can streamline this entire process, resulting in a clean, elegant design that is robust and efficient. The initial request is encapsulated into a closure and executed on a background thread. Once the operation is complete, the resulting immutable object and/or collection is returned as a block parameter. This allows the requesting code to define the callback behavior (what to do with the results when they are returned) and handle errors (what to do if an error is encountered while retrieving the data).
Tying it All Together
By combining tasks executed on the main thread and tasks executed on a background thread, it is possible to implement a system of arbitrary complexity without sacrificing performance. More importantly, by following the best practices defined here, the developer can eliminate defects due to concurrent access, leaving only human error as the sole contributing factor in defective behavior. Also, the discrete nature of closures helps to isolate troublesome code, allowing developers to set breakpoints inside the appropriate blocks to see exactly what’s happening without needing to hunt through stack traces or step through endless lines of synchronous code.
It’s not all fun and games, though. You will end up with a lot of obfuscation and a ubiquity of dispatch_async calls. It may also be somewhat challenging for less experienced developers to adapt to the asynchronous approach. The training investment you make to educate your team and promote the functional programming mentality will dramatically reduce the cost and time required to test and certify your application’s behavior.
Best Value for Internationalizing Your Apps
Many developers focus exclusively on publishing their apps in their native language. While this works well for some demographics, such as English-speaking users, it does not work well for others. Regardless of the language, publishing content in only one language substantially limits the total number of users your apps could potentially reach. By internationalizing you apps, you can dramatically increase your market.
So, it’s clear that it’s a good idea to consider internationalization (i18n) in your development process. What’s not so clear is which languages you should include in your apps. There are many languages in the world, and it would be imprudent to support all of them. There’s a cost per language to translate and localize each app. At some point, the cost of localizing reaches diminishing returns. As such, it’s important to prioritize the languages that will best expand the market.
Mathematically, you can ensure that you’re maximizing your potential market by prioritizing those languages that are used by the largest percentage of the global population. According to wikipedia, the top five most used languages in the world are Mandarin, Spanish, English, Hindi, and Arabic. These five languages combined represent about four billion people, more than half the world’s population. Based on that, it makes sense to focus on these as a core group and add other languages as needed. Beyond these core languages, it is recommended to add languages only in response to requests for localized content.
By targeting a core set of languages, you can reach a large percentage of potential users and still manage to keep costs low. This helps you derive the most value from your localization investment.
URL Content Matching with Uptimetry
Sometimes, checking the web availability of your URL is not enough to guarantee its health. In some cases, the server consumes errors and presents a human-readable “something went wrong” page, thus masking the HTTP 500 Server Error with a HTTP 200 OK. In these situations, your resource is in need of urgent attention, but the Uptimetry™ basic availability check will flag it as “up” and move on.
In order to combat this insidious behavior, a new feature was added to support explicit pattern matching on the URL content body. Now, you can specify a CSS selector or XPath. When your URL returns a valid response code, Uptimetry™ will attempt to match your content filter against the response body. If there is no match, the system will flag your URL as “down” and notify you accordingly.
This opens a wide range of possibilities for real-time metrics on your key web-based assets. It’s easy to guarantee, for example, that your sign up page contains a form with specific text fields. Simply create a CSS selector or XPath that matches a set of elements in your markup, and Uptimetry™ will ensure they are present in your production environment.
This feature is currently available on the web and will be available in the next update for the mobile app.
Integrating SimpleWorker into Uptimetry
Today, Uptimetry got a make-over.
After last week’s Amazon EC2 outage, I discovered a scalability flaw in the architecture I’m using to monitor customer URLs. Due to a time-out in the regular background task that queries sites, not all sites were being monitored. In fact, 90% of the 100+ sites monitored by Uptimetry were being ignored. Once this was discovered, I quickly began searching for a cloud-friendly mechanism that would allow parallel background processing in a scalable architecture.
Heroku’s worker system is wonderful, but it is far too limited (and cost-prohibitive) for my needs. Given that the worker process takes about 3sec to query each site and react accordingly (longer for sites that do not respond, due to the added email notification), it would take nearly an hour to query the 100+ sites we currently monitor, running a single worker. Using Heroku’s worker system, this would cost about $36/mo, and we would already be approaching the maximum capacity of that solution. That means as more users join and add their sites, we would quickly jump up to the next price point (2 workers = $72/mo). That quickly gets very expensive.
SimpleWorker offers a much more scalable solution (no max workers, compared to Heroku’s max of 24) at a fraction of the cost. SimpleWorker charges based on total compute time used, instead of a per worker per hour charge with minimum increments of one worker and one hour, respectively. Where a single Heroku worker would take nearly an hour to process all the sites, initial live production testing shows SimpleWorker burning through them in under 5mins. The compute time is still the same, but the system processes the queue much faster. This gives us a lot of room to grow, which means more users can use the service without suffering the eventual performance bottleneck we would encounter with Heroku’s worker system.
As a direct contribution to the app’s sustainability, SimpleWorker is helping me solve a critical problem in the Uptimetry system architecture and making it easy for me to set affordable subscription pricing when the beta phase is complete next quarter.
Intellectual Property is the Life Blood of my Business
As a consultant, my business lives or dies on the value of the copyrighted material I deliver to clients, usually in the form of source code. Occasionally, I contribute to the conception of trademarks during the product development process. Even more rarely, I co-invent something with or for my client, resulting in a provisional patent. These things represent the most important and valuable aspects of the business transaction between my company and my clients.
Every contract I have ever signed with a client includes a clause that defines the nature and scope of the intellectual property (IP) and any transfer of ownership rights. I find it essential to define the scope as narrowly as possible. Each contract has clauses explicitly defining the copyrights, trademarks, and/or patents created during a finite development period. Since the parties in the contract – the people creating protectable works – don’t often know what they’ll create at the time the contract is signed. This often means there must be re-negotiations or contract amendments. There’s nothing wrong with having 20 contracts with a client.
Here’s a real world example I experienced several times last year. I entered into a contract with a client initially expecting to produce no IP beyond the source code copyrights. Then suddenly one day I had a eureka moment in a meeting with the client. This moment led to the development of a provisional patent and a trademark. I re-negotiated the contract to be amended to include these things explicitly and to increase my equity in compensation for the additional value being delivered. You can and should add language in a new contract or amendment to explicitly include the IP resulting from events like this. In fact, you can save a lot of time and headache if you use a management tool to organize all your ideas, contracts, assets, and rights. I manage my IP portfolio with VerifIP (http://verifip.com).
VerifIP helps me keep track of all my noteworthy ideas – those things that may contribute substantially to revenue generation in the course of business. You’ll know these ideas. They leave you thinking “holy shit, we can make some money with this!” Right now, you’re probably not capturing those ideas in any meaningful way. Maybe you jot it down on a napkin or in a lab notebook or even email it to yourself or a colleague. That’s not enough. If you really want to maximize the potential of these great ideas, you need to do three things – track, socialize, and promote. You’ll be hard-pressed to remember to include them in a report to your boss or bring them up in a team meeting. By capturing them digitally in a structured format, you can easily track them. Once you’re tracking your ideas in a centralized location, you can easily prioritize them and share them with friends and colleagues to get that critical early feedback. Once you kick your idea around and have a group consensus on its apparent value, you promote the idea to a new level of importance. This is when the idea becomes an asset, something that will add value to your business and put more money in your pocket.
Over the last three months alone, I’ve generated at least ten ideas, two of which evolved into startup concepts, each with its own set of trademarks and copyrights. One of these startups (EnrollMint) has the potential to generate a whole lot of money over the next five years. It also has a killer trademark. With the right marketing, I will be able to leverage the trademark to magnify profits in an untapped vertical. I track all that – from concept to publication to solid IP protection – with VerifIP. I imagine if I weren’t using VerifIP, I’d probably be paying my lawyer a lot of money to manage this for me. Or, worse, I wouldn’t even be protecting myself at all.
Using InventoryKit to Transition a Paid App to Freemium
After watching the free vs paid trends in App Store sales for Uptimetry™ over the past two months and comparing that against the server load and mobile app session analytics, I’ve decided to make the transition from a paid app to a free app with in-app upgrades. Normally, this would be a sure-fire way to alienate all the folks who paid for the app. If I simply change the app without any consideration for these good people’s investment in my fine product, they will hate me (with good reason) and leave nasty one-star reviews. Since I’m moving to an ad-based free app with an in-app purchase to disable the ad, followed (eventually) by a monthly subscription for further feature upgrade paths, I need a way to transition cleanly. The goal is that after the transition, all the folks who bought the app so far will be able to enjoy the app without the ad (no further payment required), while simultaneously offering new customers a free app with ad and the option to pay to disable the ad.
Sounds tricky, right?
Fortunately, I already have a handy tool to make this difficult transition with very limited development effort. My in-app purchasing library, called InventoryKit (available free on github), has a simple transition mechanism to make this a walk in the park instead of a customer relations clusterfuck.
I’ll be releasing an update for the Uptimetry™ iOS app early next week. This update will prepare existing customers’ apps for the change. No customer effort is required beyond a simple update. All the user needs to do is update the app before the next update is published (30 days), and then update again when the next release hits the App Store. These updates will also contain additional features and improvements. Fortunately, Uptimetry™ requires a user sign in to provide its core features, so I have email addresses for all my customers. If this were not a service-driven app, I would basically be crossing my fingers hoping that all my users update their device frequently. As it is, I will send an email notifying all users of the change.
For any developers facing this sort of problem, I strongly encourage you to save yourself a huge headache and use the transition features in InventoryKit. Of course, once you get started with it, you’ll want to use it to integrate your in-app purchases as well. You can use the non-consumable and consumable products right away. App Store subscriptions will be available in Q2 2011. Once they are available, InventoryKit will support subscription integration with its sister product, EnrollMint™.
Peak-to-Mean Ratio as an App Performance Metric
Predicting mobile app performance is impossible. The market changes very quickly, and the outliers leading the pack in downloads and revenues followed no special secret plan. They simply got lucky, offering a product the market wanted when it wanted it. But, that’s the 1% of statistical anomalies we hear about in the news. What about all the rest of us mobile app developers (the 99%)? How do we determine if our app is performing well?
What I do is track the performance of existing apps through analytics. Ideally, I have analytics integrated into the app from initial launch. That gives me key information about what I can expect to see in terms of relative performance in the long-term. The reason I need the full life cycle data is that the launch represents a spike in the numbers that is a critical component in the overall evaluation. Without the spike data, it is impossible to determine the peak-to-mean ratio. Peak-to-mean ratio (PMR) is exactly what it sounds like – the ratio of the peak (maximum) value to the mean (average) value. When analyzing app performance data, we focus on two things – active sessions and total number of downloads – and we are interested in these values over time. My studies typically use weekly data.
Looking at the entire life cycle of an app, the session and download data follow similar trends, with a spike corresponding with initial release, smaller subsequent spikes at each update, and a mostly stable average otherwise. Data seems to indicate a steep exponential decrease in values, reaching the average value within four weeks of each peak. This trend helps developers narrow their expectations in the early days after launch. It can be easy to be caught up in the excitement of launch day numbers. Being aware of the trend helps developers maintain a sense of perspective. Here’s a real world example.
During the first quarter of 2011, I launched two new iOS apps. One is a game (Pucker). The other is a utility (Uptimetry). They both have dramatically different use cases, so we expect to see different PMR metrics, and we do. Pucker’s PMR for sessions/day (the app hasn’t been available for four weeks as of this writing) is about 6. Uptimetry’s PMR for sessions/week is about 14. This makes sense. Being a game, we expect Pucker to have a higher number of sessions per user. Conversely, Uptimetry is a utility app where the expected number of sessions per user is very low. Uptimetry has 2.68 sessions per user. Interestingly, Pucker has only 2.12 sessions per user. As a point of reference, Scorched Earth, another game, has 5.0 sessions per user. It’s possible that it’s too early in Pucker’s life cycle to extract reliable metrics. At the two week mark, Uptimetry’s PMR was 8, so I expect Pucker’s PMR will settle around 12-15. I don’t have launch data for Scorch, or I would include it in this analysis.
To summarize, the value to the developer in knowing the trends is identifying a rough estimate of the T+30day average after only one day. By factoring in the expected use profile (once a week vs twice an hour), it’s possible to establish an upper bound on the average number of downloads or sessions per week based purely on the launch day numbers. This helps developers chart a course for their business model, providing an early warning indicator for fledgling services that may not be viable.
Bottom line: you can expect PMRs in the 10-20 range, and that roughly equates to an average of 5-10% of the launch day value as a long-term average daily value. If you’re able to achieve 1k downloads on launch day, you can expect 50-100 downloads per day at the 30 day mark.
Pucker 0.95 Release Delayed
The latest update to Pucker was rejected by Apple, due to failure to adhere to in-app purchase guidelines. After a few messages back and forth and a few follow-up phone calls with Apple’s review staff, the in-app purchase interface needs to be redesigned. In a nutshell, the interface presents five quantity options for buying undo tokens (10, 20, 30, 40, or 50), but the way they are presented is not aligned with Apple’s guidelines. As designed, the five options are displayed along with their total prices in a picker. Apparently, the preferred approach to displaying multiple quantity options for a single product ID is to show the product title and description with the per unit price and show a quantity picker with only the quantity (not the price) in the picker. As a result, there will be a brief delay in releasing this update while I redesign the interface to reflect the changes in the UI. I expect to submit a new build within the next 24hrs. Sorry for the delay.
Pucker 0.95 Submitted to App Store
Pucker is moving ever closer to its full release with this update (0.95), which includes ten free undo tokens and an in-app store to buy tokens ten for $1. This was made possible using the consumable product support recently added to InventoryKit, an open source tool for simplifying in-app purchases.
Pucker is available free on the App Store. Download it today and enjoy!