My Experience with Parse and Cloud Code

parse-1

 

Back in 2013, I had a chance to work with Parse, and in particular, the Cloud Code hosted backend solution they provide.

For those unfamiliar with Parse, it’s a nifty platform for developing apps. Not only do they provide several SDKs for various platforms like iOS and Android, but they also provide a hosted backend known as Cloud Code, which is essentially a pseudo implementation of Node.js with their own db engine based on MongoDB.

The App

Without going into a lot of details, I was essentially responsible for the backend infrastructure for an iPhone app which would have a strong social media component. Because of the nature of the app and the likelihood of expanding to Android and other platforms in the future, a lot of the core logic and functionality was centralize in the cloud (what Parse calls Cloud Code).  That way, the various platform applications can be lightweight, and the core logic can be centralized.

What I Liked

The concept of Cloud Code, complete with the seamless integration between frontend and backend using cloud functions, is quite the novel idea. Using this approach, it is extremely easy to get up and running with a complete iPhone or Android app with a centralized backend. You could do everything from scheduling background jobs to pushing notifications. With Cloud Code, you can centralize a significant part of your business logic outside of your device. With mobile, this is certainly an important consideration with limited battery life and bandwidth.

All of the database objects are shared between the mobile application and backend cloud code, meaning your iPhone app can query the same data being used by cloud code. There’s no need for setting up gateways through the backend to access this data.

What I Disliked

Unfortunately, the list of dislikes outweighs the list of likes.

Transactional Queries

For starters, the Mongo implementation was severely lacking. I couldn’t find any way to do some of the more useful things I’m used to, like transactional queries. That means if a series of queries (particularly writes to the database) fails, I can’t treat those series of queries as an atomic object and only save them if they all pass.  Instead, I have to clean up already-processed queries (in an asynchronous environment no less).

Failing Queries

The transactional query issue actually became a significant problem because my queries were constantly failing. Despite using exceptions and promises and the full gamut of options at my disposal to catch failures, I would get a very vague error object that looked like this:

code:-1,message:””

WTF kind of error message is that?

Cloud Function Limitations

Because of the nature of cloud functions, I found quite a few limitations. A cloud function can only run for a few seconds. Because of this, if a function runs for too long–say Twitter’s API is responding slowly–I end up with a failing cloud function. Ugh…

Background Jobs

In addition to cloud functions, Parse also has support for running background jobs. This is intended for larger batch jobs that need several minutes to run. Unfortunately, there’s no convention for kicking off longer running jobs other than to set them up in a cron-like interface through the web-based admin system. That means it’s not all that easy to kick off a background job when a particular event happens, or to get around my cloud function limitations.

That said, I was actually able to get around this by leveraging a http based mechanism for launching background jobs. I believe this http-based interface was really intended for testing your background jobs from your testing environment using curl, urllib2, etc., but I was able to make a Parse.Cloud.httpRequest to the Parse server from a cloud function, which allowed me to run the Parse background function.  Definitely feels like a hack, but it works.

Background Job Status

The most annoying thing about background jobs had to do with the interface for viewing job status. In their admin portal, you can view previous background job status, including whether the job ran successfully or not, and any errors that occurred. Unfortunately, this job status was a table of every background job that was ran since the beginning of time (or at least several weeks worth), and it was in random order with no ability to sort. So if you wanted to see the status of the last five background jobs that were ran, you would need to scour dozens of pages of job statuses to find the most recent one, essentially looking for a needle in a haystack.

Worst of all, there has been an open forum thread for months now, where a Parse representative said they’re aware of the bug and fixing it.  But still no fix.  How hard is it to sort a table?

Performance

Along the lines of performance, I found the performance of Parse to be quite predictable, and not in a good way. Being based in Austin, TX (CST), I found Cloud Code development to be almost unusable at times in the afternoons. New code deployments to the cloud would fail, or I’d get the dreaded code:-1,message:”” error more often in the afternoon.  Since the cloud server is essentially a black box, there’s no way to view metrics or determine if there are load issues on the server.

Local Development Environment

There is also no local dev environment for Parse, meaning every code update needs to be pushed to the cloud for testing. There’s also no easy way to split environments between development and production, other than just creating multiple apps. In our case, we had three “applications” in Parse: one for cloud code development, one for integration with the frontend iOS app, and a third for demoing to prospective investors.  This is a minor annoyance, but one we could probably live with.

Debug Environment

Since everything needs to be pushed to the cloud to test, Parse has a nifty “develop” command that is essentially a daemon. This command line daemon does two things: first, it monitors your code for changes and uploads the changes to the cloud; second, it “tails” the parse log output so you can see what’s happening on the server.

Unfortunately, the logging feature was hit or miss. At first, it was great. But over the last couple of weeks, it appears it has broken, as it suddenly quit showing a live log for a few days. I suspect this was a performance issue where the server was unable to keep up with logging for all development daemons.

There is also no way to do any extensive debugging, other than littering your code with console.log() commands. I would have loved an interactive debugger, or an interactive object browser to run queries in Mongo and/or retrieve results.

Finally, it appears the parse command-line app (which is what runs the development environment, e.g. the develop command) is written in Python.  Want to console.log() something with some meat, like the json response from Facebook?  Good luck, because you’re likely to encounter encoding issues which result in an ugly fatal Python error. Yikes!

Release Environment

While each deployment is given an incrementing release number with the ability to roll back releases, there is no support for Git or any SCM tool. This could almost be forgiven, except for the fact that there’s no adequate development environment for managing multiple versions of the same app (I.e. development, integration, demo, and production versions of an app). So it’s left to the user/development team to come up with a process for pushing new releases to the various development app environments and tagging those releases as they get pushed, so that there’s traceability to what’s out in cloud code.

To give a better example, I had a release in the integration environment that had a bug. I had forgotten to tag the release prior to pushing it out to the integration environment. When alerted of the bug, I had to try to figure out which commit in git corresponded to the release in the integration environment. And to top it off, there’s no way to download your code from the cloud once it’s deployed, so I couldn’t even download the code in the cloud and do a diff -r to compare to existing commits in git to what’s in the integration environment.  This seems like a basic feature that’s missing.

Support for HTTP Verb-based APIs

This ended up not being a big deal (since our API was essentially the iPhone app making cloud function calls to cloud code), but there is no native support for HTTP verb-based APIs in Parse.  That said, you can create this sort of infrastructure/routing in Express, which Parse supports.

Socket Level Development

We actually needed to do some socket level development with our app. Unfortunately, the only interface from cloud code to the outside world was through http requests. So there was no way to query a particular port on another server, unless you create a HTTP-based broker outside of Parse to handle the requests.  So we’re actually forced to create a Node instance alongside Parse to handle this.

Support

It’s not that the Parse team doesn’t try, they are certainly active in the forums and answering people’s questions. However, I got the feeling that they would ignore questions related to known defects, almost like a programmer would ignore an email about a glitch until its been fixed, or until he has so many people complaining about it that he can no longer ignore it.

The fact that my development environment (via the parse command line develop command) was busted for several days, yet received no responses in the forum or from direct email to parse, was unacceptable to me. As someone looking to launch a full blown iOS app using Parse, I was quite frustrated with the sounds of crickets chirping in some of my support threads.

Conclusion

Parse is great for iOS and Android development using their SDK, and even some lightweight cloud code/backend development.  If they can address most of the issues above, it would be a great platform for development. As it stands now, don’t expect to centralize a large amount of logic in cloud code, as it’s not always reliable, and is difficult to develop in.

For our application, we are putting the finishing touches on our MVP using Cloud Code for a very significant portion of the app. However, we’re already looking into migrating this code to a pure Node.js solution, realizing that the Parse Cloud Code platform is not a reliable backend for our complex backend. Unfortunately, that means we’ll need to develop interfaces, authentication, etc. between Node.js and the iPhone subsystems, rather than the handy interfacing that Parse provides.

Let us Help!

Contact Caffeine if you need help with your existing mobile or web-based application!  We’d love to chat with you.