April 27, 2015

MadCode Meetup: Backend without code. Parse: Myth vs Reality

UPD: As you already know, Parse is shutting down. We shared our own vision of what are our next steps and possible ways out.


Mobile backend services are now more popular than they ever have been. They allow users to create complicated mobile applications more easily than ever before. Is it worth it? Do third-party backends solve more problems than they create?

We dedicated our 5th Stanfy MadCode Meetup to talk about Parse.com service; it’s limitations are our use-cases. The speaker is @vixentael, iOS engineer at Stanfy, who spent part of her life creating lots of Parse-based applications.

You can watch the full presentation below:

MBaaS or not MBaaS?

On one hand, using your own backend server offers flexibility. You can customize it to your needs and goals. But you should have an engineer that cares about servers’ maintenance and a backend developer that writes backend code. It works well when you have time and money.

But what if you don’t? If you have only the great idea, mobile development skills, 2 weeks vacation and a smoothie — third-party backend service solves maintenance problems and allows you to focus only on the business logic of your application.

You can read more about pros and cons using MBaaS:

Why Parse?

We use Parse.com because it solves our problems. We tried it several years ago and built many applications using it. Some of them support features like anonymous login, geo-based push notifications, and offline synchronization. There were social apps (post-like-comment-share actions included) and personal apps (on-the-fly customization and offline mode).

Of course, Parse.com is not the only MBaaS service. You should select a service carefully, taking into account that different services are adapted to solve different problems.

Read more to compare different MBaaS:

I definitely don’t recommend using Parse for huge content-based applications (with many data objects, like an online shop) or streaming/chatting applications. The reason is related to service limitations: streaming is not supported, pricing depends on request/sec and queries have 1000 objects limit per request.

Parse limits are widely discussed in the network. I won’t dig into details, but rather share some links which are useful for deep investigation:

Limits of Parse

Parse scalability

Parse performance

Security

Usually, our clients ask us three questions when we suggest building the app using Parse.com. These questions are: how much will it cost, how scalable is it and the most important question — what about security?

More about hacking applications =)

Some links from the Parse security guide how to prevent hacking described above:

iOS SDK

I won’t spend much time describing iOS SDK. You can read about its features directly on Parse.

But I can say that it’s really easy to install SDK using cocoapods. There is the set of UI interfaces which can be used for rapid prototyping. And some tiny things that make app developers’ lives easier: inner storage, caching, support for image downloading/uploading and awesome ‘saveEventually’ methods.

Weird crashes happen sometimes, but they happen to everyone.

Cloud code: awesomeness and pain

Parse allows you to write custom code that will be executed on the backend. Usually, I try to apply all business logic to the backend.

Cloud code guide: https://parse.com/docs/cloud_code_guide

This approach has one evident shortcoming, the need to write javascript code, but a lot of advantages: cloud code is much easier to change without re-submitting the client application; you need to write logic only once; and it can be used by many clients (iOS+Android+web apps f.e.).

Let’s look on example: the application that shows photo feed for users. Feed contents depend on the user: his geolocation, his role (regular user or admin), his previous likes.

The application only calls Cloud Code function, content filtering occurs on a server side.

Simple cloud call from iOS side

Simple cloud call from iOS side

Complicated (well, not very complicated in this case) logic on server side

Complicated (well, not very complicated in this case) logic on server side

Parse supports REST API — you can do almost everything using requests. I like writing the simple python functions to perform routine operations: f.e. to upload images into the database or to remove some specific object from tables. I prefer coding such things rather than opening web admin panel and doing it by hand.

Another awesome thing is background job. This is a function that could be scheduled and run automatically every day or every hour. I use background jobs to send push notifications to inactive users or to collect user activity during the day to generate statistics reports.

Scheduled background jobs

Scheduled background jobs

Another thing that I use quite often is Cloud Modules. There are some javascript modules that allow you to integrate with third-party services to send emails or to connect to popular websites via API. The ones most used by me are Mandrill and Twilio.

As with Cloud Code drawbacks, I want to pay attention to the debugging process. My debug flow looks like this: write some code -> deploy to server -> press button inside app -> hmmmm -> look on logs -> change code line -> deploy to server -> press button…

It really annoys me that to check Cloud Code I need to deploy it to the real server, wait until this code is deployed, simulate user behavior (open the needed screen and press the needed button inside the app) and check the result. And the only debugging instrument I have on the server side is logs.

This flow can be simplified by triggering CC not from the app, but from python script on the next console window. But I can’t say that CC debugging is easy and fun.

My CC sometimes looks like spaghetti because it’s async, based on callback and Promises. It usually turns code into the branch of callback hell. I’m trying to split CC into files and functions, but it doesn’t help too much.

Also note Cloud Code’s resource limits. Cloud functions will be killed after 15 seconds of wall clock time. Trigger-functions (beforeSave/afterSave and beforeDelete/afterDelete) will be killed after 3 seconds of run time. The execution time of a function includes the execution times of all called sub-functions and triggers.

Background jobs also have their own limits. Jobs will be terminated after 15 minutes of running time. Apps may have one job running concurrently per 20 req/s as their request limit. The number of running concurrent jobs are limited by your Pricing Plan.

There are also some points to how many objects can be returned in one query, and how the ‘count’ function works. But you can read this and other Parse limits: http://profi.co/all-the-limits-of-parse/.

Applications versioning support

It’s always easier to write an application than to support it. When you have implemented awesome new features in an iPhone application, you should support them on the backend too. Still, there are slowpoke users that don’t update to the latest version of an application. You can’t just drop support for them.

Currently, I solve compatibility problems each time I prepare a new application update.

Real users shouldn’t suffer when you’re developing new releases. That’s why my first release flow looks like this:

Create application MyAwesomeAppDev on Parse:

1. Create application MyAwesomeAppDev on Parse.

2. Develop-develop-develop-develop iOS application pointed on MyAwesomeAppDev.

3. Setup tables, deploy cloud code for MyAwesomeAppDev.

4. When the first release looks ready to submit, create the new application on Parse named MyAwesomeApp. Set up texts, icon, and useful links for this application in Parse Settings.

5. Clone tables (only needed data, without rubbish, arose during the testing process) to MyAwesomeApp.

Development and product applications in Parse

Development and product applications in Parse

6. Don’t forget to deploy cloud code for MyAwesomeApp!

7. Release an iOS application that points on MyAwesomeApp.

8. Remember to turn on ‘PROD’ mode indicator in Parse Settings when the application is approved.

9. Develop the next update in MyAwesomeAppDev environment.

My AppDelegate usually looks like this:

AppDelegate with different Parse keys depends on current app setup

AppDelegate with different Parse keys depends on current app setup

‘IS_BETA’ is an indicator whether the current application is built for AppStore or for testing.

Usually, I set up environment depending on the branch. Develop and master branches are used for developing new updates. IS_BETA is true there. In a perfect world, I merge to master branch only once per sprint, when making a demo and preparing build for a client.

Release-appstore is the sacred branch! I merge builds there from the master, prepare them for review and submit them to Apple. IS_BETA is false there. Logs are disabled, crash reporting is enabled and other good-luck-attracting sorcery hand passes are made.

The question is how to distinguish users that use the newest application version on backend. The API version indicators are useful in this case.

Every time a user opens an application, the API version is being saved on Parse as the field of the User object.

Saving current API version for user object

Saving current API version for user object

In my case, usually the API version is equal to the application version number.

The most tricky thing is to transform String to Number correctly on cloud code 😉

Such a versioning approach allows me to estimate user segments (slowpokes vs. fresh-updaters) and to customize backend logic for different users.

Also, it turns your backend code into spaghetti-code! Certainly, when logic changes dramatically, it’s better to write new functions that will be called by new application version. Anyway, it’s better to have spaghetti-code for versioning support than to not have any versioning at all!

Migration

What if you decide to move from Parse to your own server? What challenges should you expect?

Of course, migration depends on the size of your application and its integration level with the Parse. Some applications use Parse only as a cloud storage, another ones are firmly linked to the business logic written on Parse.

Client SDK can percolate through all parts of the application. In this case, migration from Parse can be quite difficult. Normally, I try to minimize the usage of client SDK and move all business logic to the backend layer.

Consequently, the iOS application should have a lightweight network manager that calls cloud code functions. In order to use another server, you just need to rewrite the network manager and data models (and implement all business logic on the new server haha), but don’t touch the rest of the application too much.

In addition, you must extract all the data that is stored on the Parse servers. Parse introduced the magic ‘Export’ button that allows you to export data from all the tables in the set JSON files.

The Magic Export button allows you to get database zip to your email

The Magic Export button allows you to get database zip to your email

This process becomes more complex if data entities have intricate relations, or you have a lot of multimedia such as images, audio/video, or binary files.

All you need to do is to query this JSON and load data into your new database.

The problem is compounded by users who haven’t updated their applications yet. That is, some users are still linked to Parse, and others are ready to use the new server. You should ensure data coherency between servers. Otherwise, users will post kitten pictures on the new server, and they (the kittens) won’t be available for those who still use the old server.

Read more about migration:

Benefits and disadvantages using Parse

For app developers, Parse service looks really handy and easy to integrate. High-quality iOS SDK and good documentation makes the integration process painless, in a some short hours you can have the fully working application with custom backend logic executed in a cloud. It’s also easy to support even crafty features like push notifications or Twilio integration. From a manager’s perspective, it’s full of benefits, too.

Everything goes faster: faster development, faster publishing and going to market. There are lots of decent infrastructural out-of-the-box services, like sign-up email confirmations, sending emails, etc. High-quality analytics helps you to understand how reliable your application is and how your customers actually behave. Parse provides easy-to-use database administration tools, which don’t require serious technical skills for administrating your backend. And all this won’t cost a fortune; pricing models are really flexible and close to real-world needs.

Unfortunately, some things are not so bright in the developer’s world. Development limitations revolve around the number of requests as the economic measure. This frequently leads to odd architectural decisions. Some administrative functions are very developer-unfriendly (for instance, you can’t drop a whole table programmatically). Free plans, which are frequently used to develop, don’t have auto backups. And magical code that runs in a cloud is sometimes very painful to write.

Some things scare the clients, too. You don’t own your hardware, you don’t even rent it — you buy a ‘service’. It leads to strategic risks — what if Facebook shuts down Parse tomorrow morning? Are the service-specific investments and unreproducible solutions worth the convenience and flexible pricing? Different clients differ in opinions.

One thing, which frequently goes unnoticed before it’s too late — when your spending scales with every user, you can’t really plan your spending.

In the end, there is a cost to being a mass service: performance issues, no specific feature requests, tools are generic and done for everybody — take it or leave it.

If you have any questions about app development, get in touch with us. We would love to hear from you!