The Path to Making and Maintaining a Professional-Grade Mobile App

My sincere thanks to Sharon Hibbert Seitz for contributing her deep understanding of successful product management, and Jenny Kolcun for her rich knowledge of best product design practices.

Generated with Adobe’s Firefly.

There is a trusted path for developing and maintaining a mobile app.

By now it’s a pretty well-traveled path most teams follow, but some still get lost along the way. Even big companies who have been developing software for decades.

It’s not to say there aren’t other paths you can follow, but these are twenty one steps I’ve seen, time and time again, that can get you through the forest, and help you keep your app evolving and growing years into the future.

#1. Create an Environment of Trust and Respect

Of all the steps, this may be the most important. Building a professional-grade mobile app takes a diverse set of highly specialized skills and experiences, and you need a team of people who work well together, and who can respect what each person brings to the project.

The most effective (and enjoyable) development groups I’ve worked in have structured their resources and responsibilities around five primary practices: Product, Design, Engineering, QA, and Marketing—each bringing their own unique, independent contributions to how the app is built and delivered. If everyone appreciates and trusts what each practice offers, the richness of that work will come through in the quality of the app.

Product

Product, defines what problem the app will solve for users, what value it delivers for them, and the overall business strategy. Through discovery research they define the target audience, the product’s value proposition, the key performance metrics, and build a strategy that aligns with the businesses core competencies and overall business goals. By defining a North Star metric, they help to continually prioritize the product’s roadmap based on decisions that will have the strongest positive effect on the target outcomes and which take into account learnings from user behavior, feedback, and data.

Design

Design brings a deep understanding of visual and user experience to the project, formulating an app that meets both the product and users’ goals. Ideally Product owns the overall vision and target outcomes, and Design and Engineering collaborate to help create the actual solutions. Prototyping, user research testing, feasibility investigations, and other tools help them define the best approach. The final outcome of this work is the app’s detailed Design Specs* that the team will then use to plan resources and build the app.

*Here, Design Specs means all of the detailed documentation needed for creating and testing the app, including but not limited to the screen designs, screen flows, detailed feature requirements, etc. etc.

Engineering

Engineering starts by helping Product and Design create the Design Specs, providing guidance on what can and can’t be built. From that, they begin architect the app’s technical design, and from that they work with QA and Product to estimate, schedule, and execute the necessary engineering tasks to build and test it. Engineering also helps incorporate tooling into the app to help Product and Marketing monitor how the app is being used by users to help evolve the Product Roadmap over time.

QA

During the creation of the Design Specs, QA provides their own valuable input into what should or shouldn’t be built from the perspective of ensuring quality in the final product. Then, when it’s ready, QA will test the app to make sure Engineering has built what everyone has agreed upon in the Design Specs, and makes sure it works dependably on all the devices it should. Often QA will use or develop their own specialized tools to help with this testing.

Marketing

Marketing works with Product and Design to determine how to best launch and promote the product once it has been built and tested, and then Marketing provides Product with ongoing feedback for how the app is performing in the market so that Product can make course corrections as needed.

#2. Create and Follow a Product Roadmap

Good apps are never finished. They evolve and improve over time based on learnings, user data, user feedback and the latest business needs.

To plan for this, Product creates a Product Roadmap, which is essentially a plan for releasing additional features and functionality over time. This helps everyone on the team keep track of where the app is currently at, and where it needs to be. The Product Roadmap should include all of the proposed features planned for the app, as far out as can possibly be envisioned, with high-level dates (by quarter, months, etc.) for when those features are meant to be built and released.

From this Product Roadmap, everyone on the team can plan and schedule their work in a much more reasonable and effective way, accommodating course corrections in the app as needed.

#3. Have Good Design Specs

Rarely is anything complex ever built well without having a complete and comprehensive understanding of what is being built and why (user needs, business strategy, requirements from other internal stakeholders like legal, regulatory, etc). The Design Specs are what provide this. They should show, in detail, everything that Engineering needs to build the app, and everything QA needs to test it by.

Often, the Design Specs are driven by the Product and Design practices of the team, but it’s important to make sure Engineering and QA contribute to the Design Specs early and often, too, providing critical input on what can and can’t be built or tested.

Including the entire team in the creation of the Design Specs keeps everyone invested and informed, not to mention that it helps create significantly better Design Specs.

#4. Keep the Design Specs Updated and Well Organized

Nothing can more troublesome for a project than having detailed design plans that the team can’t easily find or navigate in order to carry out their work. For Engineering, not being able to find and understand the latest Design Specs means they will be building features incorrectly. For QA it means they won’t know how to judge whether or not the app is meeting expectations.

Keeping the Design Specs well-organized, easy-to-access, and up-to-date is critical.

#5. Use a Centralized Tool to Manage Tasks

A screenshot of Jira taken from their web site.

A good development process breaks down work by feature, and then breaks that work down even further into manageable development tasks. This makes it easier to assign to team members, and easier to track.

You want a tool like Jira or Trello that lets you create and prioritize these tasks easily, giving you the flexibility to monitor and update the progress of each task as they get worked on (“waiting to be assigned”, “in progress”, “ready for review”, “rejected”, “completed”, “backlogged”, etc.).

A tool like this also gives you the ability to be “agile”, managing change as it happens, which will almost certainly be a constant part of your development process.

Make this tool your team’s central point of focus to manage all work related to the app.

#6. Have a Process for Managing App Content

It may seem like an obvious thing, but all apps have content (text, images, sounds, animations) that needs to be created, reviewed, edited, and approved before it can get coded into the app. This can often be a long and drawn-out process. And even then, the content will usually change, again and again, once people see it running in the context of the app—which will require further editing, reviews, and approval. This cycle will likely continue until the app is released.

Taking the time and effort to create a clear and organized way to manage this process, along with having a centralized place where the entire team can access all the latest content, can be incredibly helpful for the success of your team.


#7. Plan For User Authentication and Access Control Up Front

If your app requires your users to log in to access any of its features, you’ll want to plan for that up front, and architect it accordingly so that it can be implemented well. The app’s Design Specs should clearly define which features require authentication, and which ones do not, and the app’s code should be structured around that access to help ensure it gets controlled effectively and safely.

Too often, authentication is added on as an afterthought, and it can get you into real trouble down the road. Plan and design for it early, and design it in as part of the app’s technical architecture.

#8. Architect For Proper Navigation and Deep Linking

How users navigate your app can be one of the more complex and important user-experience details to work out when architecting a mobile app.

For example, you may not need this for the first iteration, but inevitably you might want to be able to jump (or deep-link) to certain locations within your app via outside event triggers. Tapping a push notification, for example, might need to take your users to a particular feature that is buried down in a screen or two within your app’s navigation.

Or maybe you want the app to return the user to the screen they were on the last time they quit the app.

You also need to consider access control limitations as part of the navigation. Does a feature require the user to be logged in to use it? If so, are they logged in? And if not, where should the user be directed to instead?

All of this requires a well-organized and flexible navigation architecture, along with a well-thought-out access control approach, to be able build and maintain it as the app evolves with new features.

#9. Build in Analytics From the Start

One of the most effective tools you have to monitor how your app is doing, and to figure how to iterate and improve it over time, is to incorporate analytics into your app using something like Google Analytics, or my current favorite, Mixpanel.

Many teams add analytics after the app is built, but it’s helpful to have both Product and Engineering think about it up front, making it a part of the Design Specs, so that you can include the code that records the analytics in all the right places. Do you want tapping a button to be recorded when the button is tapped, for example, or is it more helpful to analyze the results of that tap? Getting it right can make all the difference in the quality and meaning of the results.

#10. Plan for User Onboarding Early

User onboarding features are often treated as an afterthought, added to existing UI later to try and improve app engagement. However, implementing onboarding correctly can be one of the more tricky features to build into an app. It can be helpful to think about this early, as part of the app’s design. Then it can be included as an integrated part of the app’s technical architecture. This is especially important if the onboarding will be complex, utilizing things like contextual or progressive onboarding that will pop up in different places as the user interacts with the app.

Often, onboarding requires saving some state that keeps track of how much of the onboarding the user has completed. This is also something helpful to plan for early.

Similarly, the effectiveness of onboarding is something that can often be improved with A/B testing. This will require some up-front planing as well.


#11. Organize Your Code to Match the Design Specs

Once you have the Design Specs defined, and your navigation, access control, and onboarding strategies figured out, you can start to organize your app’s code and project files. I usually like to break code up into four major groups:

ui

The ui group contains all the visual elements of the app, organized by the app’s screens. If you organize the UI code based on the app’s screens, you have a clear, documented means of navigating that code with a structure that existing engineers will already be familiar with, and that new developers can learn quickly.

For common UI elements that are shared across multiple screens, consider putting those in a shared or common group within the ui group.

api

The api group contains the helper classes for accessing your server APIs. I usually like to create a separate helper class for each endpoint the app will be accessing. And, depending on the complexity of the server interactions, I might break those down further into separate classes for GET, POST, etc.

data

The data group contains the data model objects and related business logic used by the app.

utility

A utility group can contain everything else needed to support the app’s codebase and functionality.

#12. Follow a Consistent Naming and Coding Style

Entire books have been written about the subject, and many heated debates have been fought over it, but there can be no denying that naming your classes and variables correctly in your code, and following a consistent, understandable coding style can go a long, long way in documenting your app well and making it easier to maintain.

Some might say the all-time enduring authority on the subject is “Code Complete” by Steve McConnell. But one of my personal favorites lately has been “Naming Things: The Hardest Problem in Software Engineering” by Tom Banner. It actually can be the hardest problem in software engineering, and this book address it well in ninety pages. If you can implement and follow proper naming practices, your app’s code can become one of the most important, single best sources of documentation your engineering team has.

#13. Prototype the Hard Stuff

Prototyping is one of the most valuable tools a software architect has for figuring out if and how something can be built, especially for the more complex and risky features of an app.

The best time to build a proof-of-concept prototype is early, during the design process for new features. That way a costly course can be correct if needed, before it even begins. And if you approach the prototype with the same care and best practices that you do when writing “real” application code, it can quickly be integrated and used in the app as the actual feature, saving a ton of time down the road.

Prototyping is a highly effective engineering tool that should be a normal part of every product’s development process.

#14. Unit Test All the Business Logic

In my humble opinion, don’t bother writing UI tests. At least don’t make them a priority. A mobile app’s interface evolves and changes far too often and too quickly to make those kinds of tests worth it for most Agile teams. But do unit test your app’s business logic. And cover it thoroughly. If you have a custom Date object, for example, make sure you have coverage for all its functions. (Calendar and date logic can be surprisingly complex and full of bugs.)

Your API calls? Unit test those like crazy too.

Your data model objects? Unit test them every way you can think of.

Any logic not related to UI code is an excellent candidate to write tests for, and will be a huge step towards reducing the long-term load on your QA team.


#15. Be Mindful of Thread Safety and Null Values

It has becoming easier and easier to write threaded, asynchronous code in mobile apps, but it can still get you into trouble, causing some of the most difficult bugs to find and fix. Add it to your normal code-review checklist to make sure any multi-threaded, asynchronous code is being implemented properly and safely.

The same goes for null values. It can be easy to overlook this when trying to get code out quickly, but neglecting to guard for a variable that might not have a value assigned to it at runtime is the quickest and most common way to crash an app. Look for this kind of thing in your code reviews, as often as you can, to help make your app all that more stable.

#16. Optimize Your Network Requests

Most apps rely on back-end services for their content. These apps are constantly making network requests to fetch the latest updates to that content. Make sure the app is only making those requests when it absolutely needs to, and make sure you aren’t inadvertently making multiple requests for the same content. It’s all too easy to tie network requests to UI interactions, which can trigger multiple view updates, causing a network request to trigger more times than it needs too.

Also consider having a dedicated back-end engineer embedded in your mobile team. Most of the time REST service APIs are designed to be generic, single-purpose endpoints so that they can be flexible for multiple use cases. This can sometimes require an app to make multiple requests to multiple endpoints to get all of the content it needs. It would be more efficient and less error-prone for a mobile device if those requests could be combined into a single network request. Having a dedicated platform engineer on your team that understands both the back-end architecture, as well as the specialized mobile app requirements, makes it possible to create new and more specialized API endpoints specifically for the mobile app to use, allowing it to optimize its network requests better.

#17. Handle All Errors, and Handle Them Well

The difference between the good apps, and the really, really great ones, often comes down to how well they handle errors.

Mobile apps are complex, with a lot of ways in which errors can happen. There are networking errors, user input errors, data formatting errors, and the list goes on. And despite what many think, error handling is not strictly an Engineering problem to solve. It’s a Design problem too. Errors are part of the user experience, and should be designed and treated accordingly.

Make error handling an important part of the Design Specs, and an equally important part of the app’s technical architecture.


#18. Guard Against Mad Monkeys

It may seem like such a small thing, but one of the quickest ways to make your app feel unstable is to inadvertently allow your UI to respond to multiple taps if the user taps it too quickly. Especially for gestures that trigger network requests or any other asynchronous task that may take time to complete—time when the user can tap the button or link that initiated the task again and again before the first task completes. This can often causes all kinds of strange side effects in the UI that makes it feel buggy. Guarding against this doesn’t always have an easy, straightforward solution, so it’s worth setting aside time for it, to make sure your app feels more stable.


#19. Use Remote Content and Configuration

Updates to your mobile app can take time to get into the hands of your users. The more you can pull out any content or configuration details to a central server that your app fetches and uses to control its behavior, the more direct and immediate control you can have over it without requiring formal App Store updates.

Dynamic, featured content is an obvious candidate. Coding the app to fetch this content from a central CMS gives anyone with proper access to that CMS control over the app’s content, making it easier and more effective for Product and Marketing to manage.

Another common candidate is to build in forced-upgrade logic into your app. With a quick network call when the app launches or foregrounds, the server can provide your app with the latest version number available in the App Store, and whether or not the user should upgrade to that version. This can be incredibly helpful for late-braking hot fixes, for example, that you want to make sure everyone gets.


#20. Build in Tooling For Development and Testing

A test harness framework for SwiftUI apps.

Creating built-in tools to better develop and test an app are almost never a waste of engineering time and energy. Particularly tools that lets the team quickly simulate behaviors of the app that would normally be difficult or time-consuming under normal use.

One particularly important aid is to build in the ability for the app to use local, sample data sets for its API calls. This can be particularly helpful for developing new features that may not have API endpoints ready for them yet. It can also be helpful for creating App Store screenshots, or to demo the app in different ways.

I have built these kinds of tools, in one form or another, into almost every app I’ve ever worked on. They are so useful, in fact, that I’ve finally taken the time to create a new open source Swift Package to help develop and test the SwiftUI apps I’ve begun working on lately.


#21. Embrace Change, Be Agile

It has become clear, especially over the past decade or so now, that one of the best approaches to developing and maintaining a successful mobile app is to get it out early and iterate over it quickly, over and over again, based on user feedback and evolving business needs.

With this, comes a development environment made up of constant change. One week your Product Roadmap may be filled with all the familiar features you’ve been thinking about for months, the next week it might be filled with entirely new features you never even thought of.

Change is a given. Even valuable. If you can correctly implement your own form of these twenty one steps described here, you should have a tried and proven path to navigate change in a sensible and effective way. And before you know it, you’ll have an amazing app to show for it.

Previous
Previous

Easy Onboarding with a New Visual Callout Framework

Next
Next

A Reusable Test Harness for Developing SwiftUI Apps