October 14, 2015

Lessons Learned: Four Years of Startup Mobile App Development


Here at Contactually we are reinvesting in our mobile apps. Four years after our founding, we finally have the ability to build mobile applications that live up to the standard of our core web experience.

To make that a reality, we've taken a step back to plan out our vision and framework for the future. In doing so, we wanted to document how we got here, and hopefully help any other startups who find themselves pulled in a million directions to develop products across all the platforms available today (just kidding about that last one).

In part one, we're going to give a history of our mobile app development -- the choices we made at the time and lessons learned.

Lessons Learned from 4 Years of Startup Mobile App Development


Our founders were web developers (Ruby on Rails) who naturally focused on building out our app for the web. We specialized in scaffolding things out quickly and seeing how users reacted. After gaining traction, by 2012 the demand for an iOS app had grown to the level where we knew we had to do something. HTML5 apps were gaining in popularity (though not without some major detractors later that year), and it was a technology we already understood, so we went for it.

We built our first iOS application using Appcelerator Titanium, and covered all the basics of Contactually. You could create contacts, bucket them, and make notes about them. Because we understood the core of the technology before building it, it only took a month to go from first commit to initial release.

During that development, we also built out the first version of our API, which covered only the things we needed as we went along. The conversation often went something like,

"Done with the contacts list, we should have a section that shows the notes we have on each contact, I'll add an endpoint for it. What do we call that again?"

"The Rails model is ContactNotes."

"Alright, I'm gonna add a ContactNotesController and namespace it under api that takes a contact_id as a param."

It wasn't pretty, but it worked, and we had customers to ship to.

Once published, we iterated on its features and got it to a place where we were comfortable with its stability after only four months by December of 2012. We considered this a great success - Scrum at work, developing and publishing an MVP as quickly as possible.

Unfortunately, even as we stepped back we felt that there were some lingering problems with our Appcelerator app. It just didn't "feel" fast.


2013 was the year of Android. With an iOS app in the App Store, we felt comfortable taking a step back and spending some more effort in developing out a full Android app.

Besides representing a huge section of the smartphone market (~80% globally at time of writing), Android employs a much more liberal permissioning scheme than iOS. That scheme allows developers greater access to many more aspects of the user's phone usage (if they allow it). This means that for Android users we get access to phone and SMS histories, a huge boon when your goal is to help users keep in touch and track their interactions more closely.

For our second app we decided to "go native" and brought on a new engineer with expertise in the area to help us do so and leverage all these native APIs. While some mobile frameworks gave us the ability to "hook in" to these native APIs, we wanted to make sure that our app was as performant as we could make it from the beginning after noticing the lag of our initial iOS app. HTML5 just wasn't ready yet.

With our second app we also found that we needed to be able to do more than in our iOS app, and that some of the assumptions that we made while building it didn't quite hold up when we had access to all this additional data. As an example, we could now create records of phone calls and text messages that used to have to be created as "manual history events" in our old system (they always used to be entered by the user). The API was starting to become difficult to work with, and we were working in creative solutions to the original assumptions and limitations.

Around the same time, our users started to ask to be able to integrate their own apps into Contactually via their own custom systems, forms, etc. In order to make the system manageable and extendable, our developer docs were born. Users could now sign in, grab their API key off their settings page, and build their own integrations.

Not only are we supporting our own apps, but now we've published those endpoints to the world as a source of truth.


We shipped our Android app the first week of January, 2014. With an Android app that could do more and do it faster than the iOS app, our attention once again swung back to Apple's ecosystem.

Contactually for iOS version 2.0 was rewritten from scratch in Objective-C with an emphasis on tying into the native ecosystem as much as possible. We hired an iOS specialist, adopted Apple's design standards, tied into their native components via contact importing, and tidied up our transitions to make it "feel native" as much as we could.

Unfortunately, it wasn't the silver bullet we hoped it would be. All this shiny new code was still written on the original v1 API with all its limitations. Additionally, the iOS specialist was just that; a specialist. They were put on their own island and told to "make it work, report back when you're done." No one else on our team full of Rubyists and Javascriptors could help them when they got stuck, and visibility into what was going on was extremely limited.

The new iOS app was published with high expectations, and was certainly an improvement over the Appcelerator version, but left something to be desired and served to greatly highlight the need for a more comprehensive and understandable API to build on.

Throughout the development of the new iOS app, features were being added to the web app via an unpublished portion of the API. Many of those new features were then hastily ported to Android via webviews as we looked to "ship it" as soon as possible then polish it later. Our performant native app became hybrid.


The first half of 2015 was spent refining our existing apps. Performance bottlenecks were discovered, patches published, support for the latest operating systems offered, and progressive improvements made across the board.


By mid-2015 it had became clear that Contactually had grown up a lot in the prior three years and that in addition to no longer reflecting our application's internals (as it had done originally), it required a user (developer) to know a bit about how certain objects interact within the Contactually ecosystem. It was time to take a 30,000 foot view of our app and design a new system that was both easier for outside parties to look at the docs and understand as well as give us the freedom to build and refactor without needing to bend over backwards to make old apps work.

To do this, we read through lots and lots of "API best practices" blog posts and articles and talked to people who had been their and done it before. At it's core, we found that the following rules aligned with what we wanted to build, and what we wanted to build on:

    • Require JSON requests/responses

    • Observe RESTful patterns and HTTP verbs

    • If you're going to be performing a particular action more than 10% of the time, give it it's own endpoint (e.g. /api/v2/me/tasks/{id}/complete)

    • Always send back the object that was created, updated, or deleted

    • Authenticate Users/Apps over OAuth2

    • Allow users to pick which permissions they're giving an app

    • Keep responses as simple as possible

    • Think of what the rest of the world calls something, not what your code says

    • Treat your developers as well as you treat your users

After sitting down and plotting out how we wanted to lay out our routes, we also established the order in which we wanted to implement them. We work in two week sprints, and wanted to dice up the work so that we could complete a single portion in those two weeks, then start the mobile development while another teammate continues to work on subsequent portions of the API. We ended up splitting our API v2 into v2, v2.1, and v2.2. All of these endpoints will be located at the same level, but this helps us keep it clear when something will be coming online.

Having written v2.0, we are currently dogfooding the new API by building our next generation of apps on it, as well as porting over our automated cucumber test suite to utilize it. Soon we hope to move the web app to this API and publish it for all to use.

The Future of Our Apps

In our next post, we'll explain how we evaluated different frameworks for mobile development, and the key considerations when making a selection.