Picking a solid platform for your business' app is a complicated decision. Many app developers get stuck with a platform that requires undesirable maintenance effort, and falter on delivering new features. Although native apps can offer advantages that Progressive Web Apps (PWAs) currently don't, they aren't always the best choice. Here's why my startup decided to switch from "native" to "progressive," when the latter is the better choice, and a guide to creating your own first PWA.
What I was setting-out to do
I founded a startup in late 2016, that had the aspiration to be the "Uber of services." I needed to ensure my startup reached customers and became a ubiquitous part of their lives, and so a mobile-first approach made the most sense for me. Furthermore, as a South African business with limited time and money, I had to make sure every unit of effort and time invested was optimised for maximum impact. Going mobile offered precisely this.
My goal, then, was pretty straightforward: reach as many customers as possible with minimal effort, and to do this I needed to address a particular set of tasks:
- Authenticate users: Wire-up a secure system to sign up users and control their access.
- Perform onboarding: Build features to teach users what the app is about.
- Create and manage service requests with offer flows (Core business logic): Build pages that form part of the different activities users can perform.
- Handle payments: Partner with a payment gateway and integrate their services into my app flows.
- Manage after-sales service and monitoring: Manage customer care in the app, and work around backend monitoring and disaster recovery.
With that criteria set, I had an important decision to make: What was I going to use to build the app? I based this decision on my personal preference, falling back onto the programming languages and frameworks I knew at that point in time. Thus, Native Android was the answer for two reasons:
- I knew how to code in Java, and
- Cell phone manufacturers had flooded the market with Android devices.
The Android operating system is the reason many other mobile platforms met their demise. Windows Mobile and Blackberry all went "poof" quite spectacularly once Android gained traction. This alone was enough to reel me into Native Android development. With a thoroughly documented API, a wide selection of UI and utility libraries, a buzzing community, and an easy-to-use app deployment interface (the list goes on), choosing native Android development was quite the no-brainer.
How a native Android app helped me fulfill these criteria
Eyes gleaming with excitement, I made my first git commit. I was well on my way to building an app where customers can access everything that my startup had to offer.
Naturally, there were a few core features that I had to build in order to have a Minimum Viable Product. I will not go into much detail, but I hope you find a few useful considerations and libraries if you are also an Android developer.
With regards to the above criteria, I tackled the following:
I initially built my own JWT-based authentication, which worked well at first. However, this didn't convert to a lot of sign-ups because people found username and password creation quite a hassle. I eventually switched to Firebase Authentication which allowed me to pull in Facebook, Twitter and Gmail users with one click. This also meant I could worry less about the security of my auth. Phew!
Again, I did not reinvent the wheel. I used TapTargetView and Ahoy! Onboarding to design informational cues within the app. I went through a few options but these made the cut because they mostly followed Material Design guidelines and allowed for easy customisation.
This was the bulk of the work. Honestly, if you don't get this right, there is a lot of technical debt to carry later on! I used third-party libraries elsewhere so I could really nail my implementation, which saved a lot of time. Of course this approach wasn't without its share of bugs; just like your own code, bugs may pop up with each library that you include in your projects. In those cases, have a look at the github issues page of each library to quickly report and possibly help resolve bugs. With unmaintained libraries, it would then be ideal to either fork it and fix the issue yourself, or find something better.
Here, I chose a payment provider that provided an integration with both the implementation and UI to handle card and EFT payments - largely because my startup was not in the business of creating a complex system like a payment solution. For now, it was best to rely on an existing solution.
After-sales service and monitoring
Here, I used Zendesk and Firebase Crashlytics. Zendesk is a pretty mature customer service backend that will help you effectively deal with requests from day one - but it comes at a price (literally!). Fortunately Firebase Crashlytics is free. Since the bulk of the backend was in PHP, a full-featured admin panel such as Nova came in handy. It delivers easy CRUD operations, intelligent data operations, and convenient ways to plug in extra functionality.
Honestly, it was so satisfying pulling in a library to meet some major requirements, even if it meant that I couldn't really control the "bloat" introduced into my app the way I'd like to. That said, publishing the app's publicity material was a breeze: I generated my marketing assets (Adobe CC), uploaded my APK and started spreading the word. This was the (relatively) easy part out of the way.
What went wrong
Native Android may still be an answer for many, but I realised that it no longer was for my business. Even with access to customers using a major mobile platform, I was missing out on iOS users and people who spend their days working on a desktop operating system. I thought I could just build apps for those platforms at a later stage, but there is only so much one developer with a day job can achieve. I needed to find a way to broaden the impact of my coding efforts - the "solo developreneur manifesto," really.
The main issues that I encountered either added more work or just slowed me down. Despite my thinking I had optimised the development workflow, there were still lessons to be learnt. This all started when production considerations had to be added to my daily to-do list. These issues were the writing on the wall, and it was time to rethink things...
1. Problematic app delivery via the Play store
People started downloading the app and I was ecstatic... until the first Crashlytics email: "A new fatal error occurred." I was at work and could not fix it until later that evening, by which time more users had experienced the same issue. I deployed a fix eventually, but it only went live about two hours later. Regardless, I had no guarantee that all my users will update the app and I did not like the lack of control I had over the version of the app on my users' devices.
That's issue number one: Delivering Android app updates to users is not fun, and I'm certain there is a similar process for iOS. This is when I started falling out-of-love with App Stores as a developer. I also feel that native app crashes are not very elegant.
2. No recommended Android app architecture
Normally, software development framework makers recommending a way to organise code takes away the issue of second-guessing yourself during the development process. However, the Android team has previously been very vocal about how they don't want to recommend android app structure when it comes to building your business logic.
This left me with a multitude of shiny new ways to lay out my code, as recommended by this or that Medium article. I was starting to really dislike maintaining my Android project, and the increasing build times didn't help either.
3. Dealing with continuous development and production support
I will admit: I did not anticipate being a one-man team would add so much pressure to my life. With dreams of reaching all platforms, I had already started the iOS version of the app while still fixing bugs and deploying enhancements on Android. To ram the nail into the coffin, making sure that backend services are always running and error-resilient can be quite draining. I was spreading myself too thinly and simply had too many codebases to maintain and orchestrate in production.
So, I started drowning in code. I was constantly trying to build new Android features, tests for my code, seeding an iOS app code repository (which didn't see the light of publication) and fixing bugs.
Luckily, however, all of this happened when I first heard of PWAs during one of the Google I/O events. I was definitely primed to try something new. And so I did.
Switching to my first PWA
Being a solo developer, I can choose to change direction anytime - but I have to do the code duplication I so dreaded. My only consolation was that this would be the last time I'd have to do this. With PWAs, I learnt that I could reach iOS and Desktop, and reach my customers on any platform that has modern web browsing capabilities. I would not have to go through another App Store either; my web server was all I needed. There is more to PWAs, and we will get to that.
Armed with some experience, I could choose a new platform more strategically to maximise on the few resources I had. There are many web development frameworks that can spit out a PWA, but I had to set some solid criteria to prevent some issues, such as free-form architecture that could come back to haunt me later on. Having been bitten once, it only made sense that I use my experience to establish a list of things that would prevent the issues to which I had previously fallen victim. I was taking a brand new direction, so my choices needed to make a significant improvement to my software development experience.
My new criteria
- App architecture approach: Finding a development framework that dictates or prescribes a consistent, opinionated way to structure code.
- Development process: The tooling and development environment should deliver a quick cycle between typing code and seeing its results. Since the process can sometimes be long and arduous, hot reload is the kind of thing we can no longer live without. If you are an Android developer, and you have worked on a production app that has been around for at least a year or two, you'd know that the build times can leave much to be desired as you make small iterative changes. Hot reload (which exists in many frameworks gaining traction) makes your development cycles really fast.
- Language style: I love how modern languages like Typescript, Kotlin and so on improve code readability and brevity. The framework I choose also needs to frequently update its support for the latest language features as they become available.
- Delivering a bug-fix quickly: App Stores, be gone! When I want to send out a fix, I want full control.
- Customer reach and ubiquity: This requires the platform to handle user activity across multiple platforms with some level of state carry-over.
How an Angular PWA helped me fulfill this criteria
There were many contenders that could satisfy the criteria I had, but I ended up having to choose between Angular and React. I narrowed it down to these two frameworks by taking the following into consideration:
- Platform maturity: These frameworks have been around for some time and have had really great minds from big IT companies work on them - which means they've been tried-and-tested by the best. I have used many libraries by Google, and Facebook is also backed by great minds.
- Community: There are big Angular and React communities all over Reddit, Gitter, Github and StackOverflow. I am a solo dev for now, and I need to be sure that help will be there when I encounter issues. Medium always has some opinion pieces to help me think in new ways too.
- PWA support and documentation: The framework must allow me to generate a PWA. The documentation must also be detailed and provide code samples often.
App architecture and language style were the main differentiators that helped me make my final decision: Angular, being more opinionated in structure, was becoming the one to beat here. I dived into Typescript, found it intuitive, and was immediately comfortable with the syntax. Ultimately, the mere thought of learning and writing JSX with React was enough to convince me that the Google-made Angular was the answer.
App architecture approach
If you have been through the Angular documentation, you know how well-organised and detailed it is. The image above highlights what I love about Angular: it is opinionated and prescribes we write our code by creating components which may be housed in modules, and may have access to multiple services through the built-in service injection. I am a solo "developreneur," so not having anyone correcting my code structure would send me down rocky paths I could have otherwise avoided. Opinionated app architecture, tick!
I wanted to enjoy writing my code. This begins with choosing the right IDE. Most people choose Visual Studio Code, but WebStorm is far superior in my opinion - and worth the penny. WebStorm has more intuitive code completion when it comes to Angular; Angular also delivers a better experience with the Angular CLI, which is used to initialise projects and generate templates of all the different units that make up an Angular app. This is command-line based but, for GUI lovers, the recently released Angular Console will be your treat.
I simply love Typescript, and I love any language that has features such as Promises, Async/Await and Mixin classes. This is because it means that I write less code and get more done. Simple. Modern applications need to handle asynchronous processes. Even though you might want to fetch data from a server, you might also need to keep processing other user activity while you wait. Promises have been great for such scenarios for me.
Mixin classes, on the other hand, helped me re-use some methods in multiple classes without having to rely on inheritance. Imagine your common logic could be plugged into an existing class: you end-up composing your classes with reusable pieces, which will be a blessing when you get into the code maintenance phase because it'll just work better!
Delivering a bug fix quickly
This brings me to something that is very central to how a PWA operates: The Service Worker. This is a piece of code that lets what would otherwise be a single-page application have superhero-like persistence in the browser. The service worker runs in the user's browser, caches resources and checks for newer versions of your SPA on your server and pulls it in the background. This will then be executed the next time your user opens your PWA. So, to fix an issue, all you need to do is publish your code and your user will see the issue magically fixed! You can also customise the way the PWA behaves to your own needs.
Customer reach and ubiquity
PWAs are - in simple terms - websites. This means any modern browser can access them. Even better, a user can save them to their home screen and they will behave exactly like a native app. I can save, serialise and upload certain information about their sessions to my server and propagate this to all their connected PWA clients. And that's what I did!
In a nutshell, PWAs are perfect for use-cases where you don't need too much access to the device's hardware. You can send notifications and track location, which is more than enough for many modern business applications.
Where to from here: Are PWAs the silver bullet?
In this fast-paced startup world, with limited team sizes, money and time, choosing the right way to implement your solution can be unforgiving in terms of the repercussions involved. My startup suffered initially from a technical decision to go Android first, and continue to develop natively for all other platforms. This was clearly a task too big for a solo developer. The main idea with adopting the PWA approach was to make development and production support easy by:
- Reducing the number of apps to build and maintain
- Taking back control of your app update process
The mere fact that PWAs made my work easier makes them a pretty good bullet - as close to a silver one as I can imagine. I think I will be building PWAs for a long time to come - but, just like native apps, they are not perfect for all use-cases!
For example, my business crowd-sources service providers, much like Uber does with its drivers. The driver app likely needs direct hardware access to build features around GPS, Accelerometer, Camera and Fingerprint functionality. This might not be the case forever, but for now PWAs are completely out of their depth as they cannot build features around such protected APIs.
You might wonder, then: do I need to build two-or-more native apps if I need protected APIs?
Surprisingly, at their basic unstyled form, these widgets are still beautiful for an app that is not client-facing. It doesn't take much to polish the UI when building a Flutter customer-facing app, so you could still build an app for clients with this framework. This would send you back to the App Store though - which, by now, we know I'm not a fan of!
Quick-start guide: Kickstarting an Angular PWA (with Angular material)
If you're convinced by my argument for PWAs, here's a quickstart guide on creating your first PWA. You will also get to have a look at the Service Worker, which is your "persistence" in the user's browser. You can read more about that here. I have also included Angular Material in this short guide so you can start building user interface components. You can then use these user interface components to quickly mock-out an app.
As a bonus, you could also deploy the app in a serverless environment. That's covered in this Medium article which I found really useful.
To start writing your first Angular PWA, have NPM and Angular CLI installed and run:
The first command will initialise a new Angular project with routing configured and styling set to support Sass. The second command turns this project into a progressive web app. This basically adds service worker support and some other files to help you configure your PWA. If you want to get started with some pre-built UI components, I highly recommend Angular Material. Follow the guide on line 5 to get started with that. You should end up with this file structure:
ngsw-config.json file is where you will modify the behaviour of your service worker. That's how I got started, and I'm busy launching the new PWA and sunsetting the Native Android app. One codebase to rule all platforms!
All considered, I am busy developing my business' service provider app. Using Angular and Flutter has helped me maximise the impact of my limited development time. At this point in time, I can't imagine working any other way.
Look out for my deep dive into Flutter blog post soon. We will build a fully functional Flutter!
Kgotsofatso Kgang is a curious penetration tester, agile software developer and a budding entrepreneur. He loves riding the bleeding edge of technology and enjoys the excitement and lessons the journey brings. He is also no stranger to online communities in the formal and casual tech scenes. Currently, Kgotso is establishing his disruptive startup, Neighbourdo, while also working as an Android Developer. Follow him for more updates about what he's busy with on his Twitter and his LinkedIn.