Mobile apps are meant to be used on the go and up close. We want to have a fast and fluid experience that looks great and works well on handheld devices. At the same time, mobile devices—due to their size—have little memory, slow processors, and spotty network conditions. Mobile development is a young, highly segmented area where we as developers have little experience and few tools. Luckily, when going to market, our competitors are in the same position, and great apps take time to build to app stores. We took the approach of increasing quality through tests and automation and reducing complexity by design.
Here I would like to share what I learned from building my first iOS app, immediately followed by an Android app, all while building a Mobile Web app.
We have naturally grown as a team to build great mobile products. Looking back, it is easier to see now what the key factors of successful development of Native apps were:
- Agile processes and test-driven development;
- Design and guidelines;
- and Mobile Backend.
Without those three pillars, even with a very talented team, development fails sooner or later. Process gives stability,predictability, and measurable velocity. Test-driven development gives safety to make changes fast—higher velocity with fewer defects. Design makes users happy and reduces complexity. And a mobile backend cuts the complexity of the system, allows apps to scale, supports multiple test and development environments, and allows for mobile networking.
The first thing we shared across teams and phases were Agile processes and test-driven Development. Both of our Native projects delivered on-time, high-quality apps that got noticed by users and featured in app stores. Things change fast in the mobile world with so much innovation going on around frameworks, developer tools, libraries, and technologies. What helped us to stay on top of the changes were Agile processes and automation. It might sound big, but actually our process was quite light and consisted of:
- Iterations (weekly or bi-weekly)
- Retrospective meeting
- Planning meeting
- Daily standup
- Continuous integration
- Test-driven development
- Pair programming
How Things Have Grown
The “Planting Seeds” Phase
We inherited a legacy backend and mobile Website that had functioned well for a long time, but needed an update. We also wanted to develop a Native experience on a new platform. We started with a hack-week-prototype Native iOS app with no design and a hacked connection to the legacy backend. The iOS app demonstrated a beautiful image scroll of user content and embedded Web views. It was well received and understood by people in power, and we got the green light to start building the app for real.
For this project, we got one developer, one product manager, and one designer. The developer and designer had to pair a lot on designing the navigation flow and layout of the Native iOS interface. Test driving the iOS code was an investment that paid back well within the first year as the product was changing a lot.
The designer and product manager worked together to narrow scope and focus on what was important for the mobile app out of many legacy features of the broader product. The developer put together continuous integration that built and ran tests and also installs the app for everyone to download and try out the latest build. The product manager organized regular iterations with both kickoff and retrospective meetings. We had daily standups to keep everyone up to date and resolve upcoming issues quickly. The team was already familiar with Agile, and everyone understood the benefits of a consistent pace and stable delivery. Often we worked in pairs:developer–designer, developer–product manager, designer–productmanager. At this phase we had tamed the backend to run stable on staging, and we had every instance of the mobile client connecting to publicly visible endpoints.
As soon as we got another intern developer, we began pair programming. It helped a lot to onboard developers, share knowledge of code, and stay the course without getting sidetracked and stuck in problem solving. We continued pairing with our designer, who was eager to learn Xcode storyboards and did a great deal of research on what other apps were doing and what we could borrow from opensource and commercial libraries. To enable designer participation, we also included a styling library and tools where the app could be styled using common stylesheets. Having the designer actively participating in storyboards, stylesheets, and research saved developers a lot of time in the long run. The Agile process helped us to be honest to ourselves and stakeholders on what could deliver and on what schedule we could deliver it. We reached a measurable, fairly consistent velocity of delivering features and had a low rate of defects. Within a few months we launched the first version into the App Store and rolled it out in the US using Canada as a first territory.
Cloning and Scaling Phase
Once we had a tangible success on the iOS platform, naturally we wanted to repeat it on Android. Our product manager was able to pick up Android and learn how to be a user on it. We already knew that we wanted Android developers that could write tests. We had one iOS developer crossing the divide and learning to be an Android developer, bringing with him lessons learned from launching the first app.
At the same time, the iOS team released support for four more languages with localized and internationalized versions. At this phase we realized that a mobile backend needs a responsible owner. We got a dedicated backend engineer who was able to build an authentication service API, backend tests based on shared sample data, continuous integration, a versioned API, and scripts that allowed the backend to run on staging, development, and production environments.
At this phase we dropped the shared staging server and migrated our Native tests to run against the backend running on localhost. The server team worked on making the local instance able to be configurable to run in development mode with local databases and local services. This enabled us to run tests concurrently without stepping on a lot of toes. While the Android team had a successful launch of their first app, the iOS team had launched Apple Pay integration ahead of the competition.
We worked with embedded designers who shared visual style guide, while we built for our Native apps very different user interfaces. The designers, developers, and managers learned to speak a common,domain-specific language. We all grew in our understanding of common business goals and built very similar implementations. We shared a set of high-level automated acceptance tests written in different languages. The designers shared user-centric design principles and adopted them to each of the three platforms.
Product Design Process
Here is what I’ve noticed helped us to design a product and reduce the complexity of the solution:
- Pairing developer and designer
- Design research
- Stylesheet-based appearance customization
- Minimal Viable Product
- User Testing
- Direct feedback from apps
- Customer Surveys
- Responding to user reviews
- App Store promotion
Both teams built a single mobile friendly API incrementing upon each other’s work. The same API got back-ported to the mobile Web app, and now has been opened to third parties, parents, and integrators.
The central component here was our sample data, which helped our understanding of the business logic, test cases, and core relations.
We shared lots of test infrastructure and built solutions suitable for remote and local testing. We did struggle with API ownership, as it was not immediately recognized to be part of the product and was thus shared across multiple teams. We evolved to have dedicated API team that shared code with the rest of server team and priorities with the mobile team.
Our mobile backed has grown to include:
- Public API
- Private extension API
- API versioning scheme
- OAuth v2 service
- Sample data visible on mobile Web
- Local instances
- Special end point to reset and load state
- Staging, production, and development deployments
- API acceptance tests
The OAuth service was a long-term investment that took time to build, but it gave us Freedom to scale while staying secure. We started by having a single staging server where all apps connected and shared data. Our mobile acceptance tests run against that server and reset its state once before the test run. With our team growing, we split to have a couple of staging servers: one for developers, another for accepting user stories. Staging and local instances had additional endpoints to allow us remotely reset server data to a known state. Our development velocity benefited from having multiple local instances of the backend on each developer box. This resolved the issue of concurrent test executions and allowed developers to have independent instances. Continuous integration runs mobile acceptance tests against local instance of server. That local instance had trimmed third-party dependencies with stubs and fakes, for simplicity and performance reasons.
There were three key areas we can share and reuse across multiple platforms. We shared a test-driven approach, product design principles, and mobile friendly backend. This allowed us to deliver on time without burning people up. Build a great team of people, build close connections with your customers, and have fun doing what we love to do.