The Time Has Come To Seek an Apprentice
Over the past twelve years, I’ve made my way from lowly code monkey to systems engineer to tech lead all the way up to architect, engineering VP, and CTO. I’m really pleased with my professional life so far, and I’m comfortable doing what I do. Now, though, I find my career at a threshold. Where two years ago, I was struggling to keep one contract going, now I am actively turning away work because I simply don’t have enough resources to accept new projects. My lead times are now approaching six months. While I’m enthralled to have such an honor, I’m not ready to scale to fifty employees. I’m not interested in hiring a team of junior-to-mid-level geeks to do all the work for me. I like doing the work. I like being hands-on. Moreover, I don’t want to suffer through client-facing failure resulting from ineffective oversight, nor do I want to be the micromanaging douchebag that I would need to be in order to deliver the level of quality I expect from myself. I’ve come to realize this means I need one dedicated apprentice.
I like the idea of focusing my efforts on building an efficient working relationship with a single person, helping them learn and improve their skills, and working together as a pair. I love offering faster project schedules to clients for only a marginally higher cost. To do this means to place a great deal of faith, trust, and hope in one person – to put all your eggs in one basket, as it were.
So to those reading this, I issue this challenge: if you’re tired of the same old day-after-day mundane existence, being ordered around when you know the boss is an idiot and you could do things so much better, get in touch. Give me a reason to believe that you can take the ball and not only push it down the field, but shove it down the throats of the defense as you point at the scoreboard and gloat. Show me that you want something better, something impossible. I guarantee you I can show you the impossible every day, and together we will find a way through it. Because that’s what I do. I deliver miracles, one at a time, for a lot of money.
To be clear, I’m hiring one person. There is one slot available. If you want to work with some of the best architects, designers, developers, and promoters in the world, I can guarantee you that level of access. But you need to bring your A-game, because your contributions will be scrutinized by hundreds of thousands of people. So, if you’re a college student, recent grad, or seasoned developer who just really loves mobile software and want to find a way out of the corporate world into a leaner, more agile environment, please contact me.
Designing a Universal Mobile App
Earlier this year, I gave a talk on universal binary design at the Suncoast iOS meetup (slides available here). The talk focused on two Migrant Studios apps (Uptimetry, a productivity/utility app, and Pucker, an OpenGL action/puzzle game). Both apps follow the universal binary approach, where one bundled executable file runs on both iPhone and iPad form factors. Uptimetry has the same user interactivity model on both devices. It looks and acts the same on both the phone and tablet form factors. Pucker has the same underlying game engine on both devices, but dramatically different interactivity rules, taking advantage of additional screen size on the tablet device. Both apps are designed to reuse code wherever possible, allowing common behavior to be defined in code shared by both phone- and tablet-specific implementations.
That’s part of the story – iOS. What about Android?
When I first started working with Android, I found it challenging, as there didn’t seem to be any overlap between the “iOS way” and the “Android way” in terms of user interactivity. All the iOS platforms have only a single hardware button, which universally exits whatever app is in the foreground. The Android platforms have home, menu, and back buttons, as well as cursor controls (up, down, left, right, ok). The on-screen component design is dramatically different when the user has access to hardware buttons in addition to the software buttons and other controls displayed on a touch-enabled screen.
After a time, I began to realize where the decoupling is. At its core, a software application is largely defined by its object model. The objects and their relationships with each other define the basis for a language, a set of rules that govern the conversation between the user and the data. This is the essence of interactivity. In nearly every case, the user is creating, reading, updating, or destroying an object and/or modifying its relationships to other objects. The conversation that takes place could be transcribed as a journal of operations, mutating and reorganizing objects and collections.
The importance of a web service API
In a typical mobile app, the object model mirrors that of its web service counterpart. This implies a transport layer, designed to convey information between the mobile app and the web service. The transport layer is the decoupling point. By distilling the conversation into discrete platform-agnostic messages, using open standards-based tools, the system can be cleanly broken up into functional components. The web service API paradigm solves this beautifully. Sure, the code looks different on the iOS and Android platforms. One is ObjC; one is Java. At their core, though, they both do the same thing – ferrying structured information between the app and the web service.
Highlighting key use cases
The goal of all mobile apps is to provide the user with a simple, easy-to-use way to access information, make informed decisions, and take action. If you’re building an app to allow users to check the status of their home appliances, chances are you don’t need to include the ability to tweet about the ambient temperature of your living room (nobody cares anyway). Your users need to see the current value of their thermostat setting and have options to increase it, decrease it, or manually enter a different value. That’s a label, two buttons, and a spinner control; or maybe just a label and a slider. Then, if needed, add a cancel/confirm prompt whenever changes are made. Simple, elegant, and laser-focused. More importantly, the emphasis is on the behavior – getting something done quickly and efficiently.
“Set ambient temperature to 70F“
Just as we have conversations while interacting with each other, we have them while interacting with machines as well. We don’t usually speak verbal commands to our machines (at least not yet, and the thought of sitting on an airplane listening to 50 people try to dictate emails or tell fart jokes to Siri is not appealing). We usually touch, type, or click to convey our intent. Equally compelling is this: just as we bound the discussion through verbal and non-verbal cues, we are constrained by the presentation of options on an app interface. The machine bounds the discussion by offering a limited set of options. Your interface can leverage this to guide the conversation with the user.
You can offer lots of options and have an open-ended discussion, or you can offer only a few and have a brief, goal-oriented chat. Knowing precisely - within the context of the mobile experience - what your user’s main goals are and how they want to achieve them will help you deliver a usable mobile app and not just a mobile-enabled version of your web or desktop app. More importantly, these goals transcend the device, applying to the mobile experience as a whole, meaning your iOS and Android apps can do the same things, but still maintain the themes and pre-baked interactivity idioms provided by each platform.
Web Service API Essentials
After spending the past 15 years of my life building web applications, many of which included some capacity for client-server messaging beyond the browser, I’ve seen a lot of attempts to solve the puzzle. Some folks fail utterly at this task, while others merely miss the mark. Occasionally, I come across a solution that is not only functional, but also elegant in its design and implementation. I could rant for pages, perhaps scores of pages, about the failures and how they lead to developer frustration and slow development to a crawl, but I’ll save that for another time. Now, I’d like to talk about the techniques that bring about graceful, refined web services that are easy to learn, use, and integrate into other apps.
Know your user, the developer.
It took me ten years at least, working in the information systems world, to realize that the key to great design is to understand the people who use the product and their core motivation. Knowing what is most important to the end user is critical to managing development projects. As a result, a significant part of the development effort is invested in pursuit of a basic understanding of the user’s core needs. One can not simply ask for these things. The user may not be conscious of their needs. They may even lie to you, as they do to themselves, believing they need something they really don’t need or even something counterproductive. In API development, though, your user is another developer, so you might think you can assume a minimum level of knowledge about how web services work.
Don’t do that. If you assume anything about your user, set the bar as low as possible. Document everything as plainly and thoroughly as you can. Developers love reading documentation, learning the rules, so they can turn around and apply them to their relevant need. Remember, a developer is reading your documentation specifically because they intend to integrate your API into their system. This will likely make your company money, and in many cases will make their company money as well. This is often the purpose of the API integration project – to increase revenue by attracting new users with a new feature.
Embrace naming standards.
Wherever you stand on the convention vs configuration debate, there are some really valuable lessons to be learned from the convention camp. At an architectural level, a web service is typically an interface layer for interacting with objects or collections of objects. If a significant class in the object model is Song and another is Playlist and you’d like to expose the ability to add and remove Songs from a Playlist, it makes sense that GET /playlists/1/songs shows me only Songs associated with Playlist 1. If I want to see the same collection in XML or JSON, I simply append .xml or .json, respectively, to the path. This is significantly better than GET /cgi-bin/my_servlet.jsp?cmd=list&playlistId=1&format=xml.
There are also situations where custom actions beyond basic Create-Read-Update-Delete (CRUD) are desirable. Sometimes, these actions can be simulated by simply updating an object. For example, instead of having a custom publish action, one might simply update the record, setting the published_at column. Adding a non-standard action adds complexity to the controller and functional tests, as well as the web client code for interacting with this resource. In contrast, using existing standard actions means less code required on the client.
Provide details. Be specific.
Remember you’re building a request processing system, probably a synchronous one. Your user (the developer) needs to know exactly what your system expects and what it will return, in success and failure cases. If you expect a given schema, with nested objects connoting their associative relationship, you must provide this schema, preferably with an example of sending a request and receiving a response. If there are required fields, note them in the schema. If there is any validation on the request data, describe it in the context of the relevant field. If there are query string parameters your action accepts, describe them and how they are used by the controller.
Paginate collections.
In some rare exceptions, it’s not necessary to paginate a collection. For example, if you have an object model validation that eliminates the possibility that there could be more than ten objects in the collection, pagination is unnecessary. In almost every other case, though, it’s a requirement. Pagination sets an upper bound on database overhead, server memory, and processing time of every collection request. The additional development time on the server and client sides are well outweighed by the benefits to bandwidth, availability, and overall performance of your API.
Minimize and document hidden and/or triggered behavior.
There are many good reasons to introduce hidden or triggered behavior. Maybe you want to update an associated record when another is updated. Maybe you need to create stub placeholder records to satisfy data integrity when creating a central record. Maybe you want to trigger some background task to perform some aggregation function. Whatever you do, document it. If creation of a Song automatically creates a Playlist with a default name and adds the Song to the Playlist and you don’t document it, I promise you a developer (or a hundred) will email you to ask you why there’s a phantom Playlist in their account. They might even think they’ve been hacked. You can’t predict or overestimate the shit storm you will bring upon yourself if your API is not crystal clear and easy to use. Every line of documentation you write will save you support time.
Use OAuth.
If the purpose of your API is to give your users the ability to allow your app to interact with other apps on their behalf, you would be a fool to use anything but OAuth. It is the gold standard for B2B integration. Authorizations are completely controlled by the user. A user need only authenticate once to grant indefinite secure access to a certified 3rd party, and they can cancel the approval at any time. You will need to build the OAuth provider and a management portal for consumers to register, but as with pagination, the investment is well worth the benefit.
Test your controllers. Include all supported formats.
Most important of all, publish a functional API. If your API doesn’t work as advertised, your developers may jump ship before they even get settled in. I can say from personal experience (the inspiration for this piece, as it turns out) that trying to work with an API pales in comparison to actually working with an API. If my first impression of your API is “I did what your docs say, and it’s borked,” or worse still “I can’t figure out how to sign in because the docs say ‘to sign in, you must be signed in’ and provide no explanation of how to do that,” there’s a real chance I won’t contact you to work through it. Instead, I’ll move on to your competitor, or worse for you, I’ll build a competitor app.
Apple Says App Store Subscriptions Are Media Only
After weeks of development and testing, confirming that my app, Uptimetry (website, app store), correctly processes payments using Apple’s new auto-renewing subscription in-app purchase framework, I submitted to the App Store. Then, after an extended review period, my app was rejected. The rejection letter stated simply that my in-app purchase type was invalid. After some digging, I discovered this meant they would prefer I use a non-renewing subscription type for this app. I engaged in a dialog with the reviewer, asking for clarification on why exactly my monthly recurring service (provided by an external system) was not suited to be an auto-renewing subscription type. The answer I got was “I can’t help you. File an appeal.” So I did.
Several weeks later, I received a phone call from Apple regarding my appeal. Their explanation seemed astonishing to me. They rejected the appeal, standing on the initial position that despite the clearly recurring nature of my product, it was being delivered not in the form of visual or audio media, such as a magazine or streaming video, but in the form of a service. They also acknowledged that Netflix doesn’t even offer the ability to purchase and/or modify your subscription through their iPhone or iPad app. I almost pointed out that this was a tacit admission of failing to enforce the widely scrutinized policy forcing developers to offer subscriptions through their iOS apps if the same subscriptions are available through external channels. I thought better of it, preferring a more diplomatic solution.
I championed the notion that to prompt the user repeatedly to renew their subscription would be far more of a burden than asking them to manually cancel future renewals for their active subscription(s). I made the case that especially in my situation, the auto-renewing feature was adding value to the user’s experience. The whole point of Uptimetry is to monitor your web resources and guarantee they are always being served correctly with the desired content. I want my customers to trust me to keep a watchful eye on their assets, informing them at the first sign of trouble. If they receive an email every month asking them to renew and prompting them to open the app and update their subscription, that’s going to get old fast. If instead, the system simply processed their renewal automatically through their iTunes account and pushed updated subscription data to their device, their service would remain uninterrupted until they manually cancel their subscription renewal and the active subscription expires.
Why Not Annual?
You might be thinking “ok, sure, monthly renewal notices are annoying, but that just promotes purchase of an annual subscription.” As I spoke with Apple’s appeal representative, I argued further that an auto-renewing monthly term is better than a non-renewing annual term because it gives the customer frequent opportunities to modify their subscription, but saves them from frequent reminders. For Uptimetry, this is key. A user might start using the monitoring service with a few URLs, so they buy the Micro plan. Then, as time goes by, they realize they need to monitor a few more, so they need to upgrade to the Medium plan. With an annual plan, they’re locked in and can not upgrade without committing to the new service level for a full year. With a monthly plan, they can buy a plan with a higher or lower service level any time, and the commitment is limited to a month. All they need do is cancel renewal of plans they wish to drop, and they will naturally expire. This additive subscription system makes it easy for customers to combine multiple subscriptions as needed to match their needs.
Product vs Service
This all begs the question, “what is the difference between digital product delivery and digital service delivery?” In the case of products, a specific digital item, such as an app or song, is transferred to the customer, granting unlimited future use. The license agreement in this case is simple. The customer agrees not to distribute copies to other people. Often, there is no cost associated with delivering a digital product, as there are no materials required to produce it and no shipping costs required to deliver it. In the case of services, though, the field widens. Digital services can be more than simply data storage and hosting. Often, they perform actions on behalf of the customer, such as waiting on hold while seeking customer service with a third party (FastCustomer), monitoring a web server to adjust its hosting parameters to match the real-time load (HireFire), or searching CraigsList for specific items and sending notifications when matches are found (CraigsCrawler). Digital services represent a unique corner of the market, as they deliver value to customers and cost almost nothing.
Line in the Sand
Apple has clearly made a policy decision here. They have decided they will allow auto-renewing subscriptions for digital media products, such as music, magazines, newspapers, and video, but digital services are expected to be provided on a non-renewing basis. While there is clearly an appropriate use for non-renewing subscriptions, this distinction between media and non-media seems arbitrary, especially given there is no mention of it in the developer guides. Speaking as an iOS developer who is heavily invested in App Store subscriptions and in-app purchasing, I sincerely hope Apple re-evaluates this position. I realize that the digital service market is still defining itself. I do not expect them to be ahead of the curve in all aspects of the industry, but this feels more like a block than a fumble.
Rock Solid Piracy Prevention
For years, piracy has been commonplace among the 18-24 crowd. Services like Napster, Kazaa, and LimeWire made it easy to find and download music, movies, and TV shows. The quality was never as good as a DVD. Sometimes, there was even a silhouette of a theater patron as they walked in front of the camera on their way to the bathroom. If you’re trying to capture the complete essence of the movie theater experience, complete with baby crying and cell phone conversations, I’d say there is no substitute for a zero day cam, recorded clandestinely in the back of the theater on opening night. Most people, though, want the high quality DVD experience.
I’ve seen and heard a wide spectrum of pirated digital media over the last ten years. I’m not going to tell you how I found any of it, as the simple act of explaining how I found it is apparently illegal, thanks to the DMCA. Needless to say, it’s out there, and readily available if you know where to look. And it’s not just music and video. If you need a copy of Microsoft Office, you can find one with a well-defined query and be downloading in minutes. If, on the other hand, you’re looking for something more obscure, like a PC game from 1994, you’ll probably find it and be unable to download it quickly. App piracy is a population contest. The most popular apps download quickly, while less popular ones take longer, possibly days or even weeks. Generally, though, you can find whatever you want if you look hard enough.
So, how do software companies make any money with all this piracy? The reality is software sales in brick-and-mortars are declining as vendors move to virtual product delivery workflows, where customers buy apps and content directly on their Playstation, Xbox, or mobile device. Many are also adopting a subscription-based business model. Metered service is a surefire way to prevent piracy. Give away your app for free and charge a recurring fee to access content and features. Even a jailbroken app can’t hack through the iTunes in-app purchasing system. If metering doesn’t fit your business, a consumable or non-consumable product may be more appropriate. Instead of an up-front price that can be avoided on a jailbroken device, give the app away and include a one-time activation fee as a non-consumable product.
Piracy will continue to thrive as long as there is some benefit to be found in its pursuit. By shifting the focus of your business model from an up-front purchase to a freemium model, you guarantee your apps have rock solid immunity from pirates.
How To Guarantee Success in Product Development
You’ve heard it before – frustrated project managers or executives, maybe even yourself, complaining about work products and deliverables that didn’t match their expectations. Often, this has nothing to do with the competence of the vendor or consultant. It is a direct result of incorrect or incomplete communication. Sometimes, it’s a language barrier or cultural dissonance, such as the use of unfamiliar terminology or idiom. Other times, it’s the inability or unwillingness of the client to endure the sometimes frustratingly tedious process of confirming clear understanding of their project’s needs. Confirmation is a critical component of communication, without which there is little hope of a positive outcome. One challenging scenario that illustrates this well is the toxic attitude that the listener “should just get it.”
Communication problems are almost always compounded with time, if not addressed. If the entire project duration is a few months or more, the risk of failure due to divergent understanding is significant. If expectations go unmet for more than a week or two, the client begins to develop a negative impression of the contractor’s value. This strains the relationship. It’s easy to see how this strain can grow over time, eventually culminating in a product review wherein the client expresses a strong negative reaction to the delivered work, claiming it is “nothing like what [they] asked for.” If these reviews only happen once or twice over the course of a project spanning several months, the reaction can be very strong, so strong even as to result in irreparable damage to the working relationship. All of this risk can be mitigated by organizing the project into small discrete iterations. This is the essence of the agile development methodology.
By breaking up the effort into one- or two-week boundaries, the total risk to the client is capped at the cost of two weeks of labor. At the beginning of each iteration, both client and contractor agree to the features to be delivered. Then, at the end of the iteration, the client has an opportunity to evaluate the quality of service or work product. If the value represented in deliverables does not match the client’s expectations conveyed at the outset, there are two options: termination of the working relationship or reconciliation in a subsequent iteration cycle. In the case of reconciliation, the contractor has an opportunity to react to divergence of understanding quickly, rolling over the unsatisfied requirements to the next iteration cycle.
There is no set of working policies that will guarantee that the work product is delivered as desired, but by following a strictly bounded iteration-based product development methodology, the risk to all parties is minimized. By focusing the effort into discrete, well-defined goals, the task of tracking and managing project progress can be greatly simplified. More importantly, this approach increases the level of communication between the principal parties, resulting in a greater sense of clarity and confidence across the board.
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.