This blog post is about how companies waste a lot of time and money by forcing a bad method of working on their mobile engineers.
It usually starts with the old and seemingly wise observation that writing code twice is bad. Now some managers stop right here and make this a mantra for the company. By which they hurt their own business. I’ll explain why and show that implementing the “same” feature twice is sometimes the best thing one could do.
I’ll walk through some common scenarios that comes up at companies, like they have an iOS app and they want to port it to Android ASAP. At the same time I’ll probably rant about my previous bad experiences.:)
At first a little about me. I started working on Android in 2009, and on iOS in 2010. I use mostly C++, Objective-C and Java on mobile. I worked both on apps and mobile games.
1. Don’t use the same UI on all platforms
This isn’t that common nowadays, but almost every company made this mistake when they started working on Android. Usually they had an already successful iOS app and they wanted to release an Android version. Android was nowhere near as important for them as iOS, so they wanted to cut corners wherever they could. They started by reusing the design of the iPhone app. A phone is a phone, what harm could it do if they look the same? Well, as it turns out plenty.
Let’s first look at it from the high level. The problem is that every mobile platform has different UI patterns. Users gets accustomed to their chosen device’s UI. If an app uses a different pattern they will get confused or even get angry. One good example is when there is an iOS Back button in an Android app, and to make it worse it does not respond to the hardware back button (which is present on every Android device). This often means an instant uninstall.
But this choice will affect the engineers work too. If a UI pattern is not present on one of the platforms, then they have to implement it from scratch. It means wasted time, and bugs. But even when they have a seemingly similar Widget on both platforms, subtle differences in them could cause a lot of problem. I’ll talk about an example in detail below, but before that I’ll mention an exception.
Exception to this rule
If the existing app already has a unique UI then it perfectly makes sense to use the same design on both platforms.
I started working at Ustream in 2009. I spent almost 4 years there. We made the same mistake as above. Our first Android app used iOS buttons. We didn’t make all the mistakes that can be made though, like not responding to Android buttons, but it still looked liked an iOS app. I actually loved that first version.:) Because the Android UI was super ugly back then compared to iOS.
Half a year later they split the Android team, and I became the lead of one of them. We worked on the core apps and the other team started to work on a new ambitious product. Their product manager wanted to use the same UI patterns on both iOS and Android. He knew and liked iOS, so naturally this meant tons of extra work for the Android devs.
Where this approach failed miserably was the TabBar (TabActivity) widget. Android had a TabBar just like iOS. Seemingly the only difference between them was that the buttons were at the top on Android. As it turned out the Android implementation was so horrible, it was unsuitable to be used in a real app. Google get rid of that view later. There is a better one on Android now.
When the engineers realised this they tried to convince the product manager to use a different pattern on Android for this particular feature. They didn’t succeed. So they ended up reimplementing an iOS like TabBar on Android. It turned out to be a huge task. What supposed to be a simple few days long task became a several month long job. In the end, for various reasons that product was cancelled, and the product manager has been let go.
Unless your app already has a very unique UI, use platform specific UI patterns. By the way, this is a prerequisite for the app to became featured in it’s app store.
It means more work for the designers, but it also means much less work for the engineers as they can use the best design patterns for the given platform.
An important fact of the mobile platforms is that they evolve very quickly. If you force to put extra layers on the APIs in any way, you’ll find that most of the time is spent on maintaining those layers. And you will cut yourself off from using the new best patterns, or the hottest new features. For these reasons I always recommend using the native APIs for app development and suggest to stay away from PhoneGap and similar offerings.
2. Feature parity should not mean source code parity (apps)
For many companies feature parity across all platforms have the highest priority. The problem is that – in my experience – most companies wants to achieve this by forcing source code parity. Like having the same classes in Java and Objective-C. Or generate code from one platform to the other. Maybe there is something wrong with me, but I never understood how and why one leads to another. :)
There were several mobile teams at Ustream. (Symbian, iOS, Android, Blackberry, Windows Mobile, Maemo …) Implementing Ustream on mobile was very challenging, as there was no public API on any platform for what we wanted to do. Sharing best practices and even code – when it made sense – came up constantly. There weren’t any arranged meetings to discuss these. But we were sitting close to each other and were always curious how the others solved a difficult problem on their own platform. I enjoyed working there a lot at that time.
We naturally knew that as the platforms are so alien to each other, there is little room for sharing codes between them. We had a C library that was shared between the iOS and Android teams for a few common tasks, but nothing else.
But this doesn’t mean that we didn’t port codes from one OS to the other. We did that several times, but on the other hand we found that making time to adjust the code for the given platform always worth it in the end.
Interestingly enough, it came up once that the BB team and the Android team should share the same code base, because you know … both use Java. ;) But thankfully the managers quickly rejected that idea.
The most important thing is that the teams owned their own code. We could use the best design patterns, the best tools the platform provided to us. I can’t emphasize this enough.
Every team was as fast implementing a new feature as fast they could get. We were not hindered by adhering to anyone. But we shared our knowledge and ported code from each other when it made sense.
Exception to this rule
Seriously, do yourself a favor and stay away from JS on mobile.:)
2. Feature parity should not mean source code parity (games)
Let me start with an exception here, because it’s more important.:)
Exception to this rule
If you start a game from scratch - as games usually don’t use OS specific features (or only a few) -, I think the best approach is to use a cross platform engine. I would prefer to use my own C++ engine. But Unity or the Unreal engine could be a good choice too.
LibGDX is very good for Android, and it’s cross-platform, but I personally wouldn’t use it for iOS.
I don’t recommend cocos2d-x. It’s one of the ugliest engine out there that you can find.
Porting from iOS to Android
The most common scenario is that you have a cocos2d game and you want to port it to Android.
I think there isn’t a good approach for this. There are only bad ones and super bad ones. I think the best thing you could do is to rewrite it from scratch in a good cross platform engine. That way at least the team could learn a more useful engine.
Having two separate teams, one for iOS and one for Android could work too, in that case they could use the best engine/methods on their chosen platform. Reaching feature parity can be reached easily by having the same product manager (or producer) for the teams. They would probably implement a given feature in a different time scale, but they would usually be a lot faster than if they would have been tied by shared code somehow.
And here comes a super bad choice.
Use cocos2d-x, just because it has a similar API to cocos2d.
Objective-C devs might like this at first, as cocos2d-x use similar design patterns as objective-c. But as they get to know C++, they would either start hating C++ for not having a dynamic runtime or would start hating cocos2d-x for being the ugliest C++ engine out there. Did I mention that it is also slow? :)
Reaching feature parity would be the least of your problems. Because you would encounter a scenario where one thing works in cocos2d, but it doesn’t work in cocos2d-x. Then you either have to hold back the cocos2d team, or you would have to release a lower quality game on iOS. I think no sane game dev wants to use cocos2d-x the second time.
Porting from flash to mobile
This is another common scenario.
I don’t know ActionScript well. But I think it’s common knowledge now that it’s not good for mobile. I think most companies doesn’t think to reuse ActionScript code on mobile. When this time comes in a company’s life it is best to hire engineers with mobile experience.
Personal experience (cocos2d-x)
I worked for a few months at a game dev company. I was hired to find the best approach porting their existing iOS (cocos2d) games to Android. They already worked with an external company who ported one of their games to Android. I think many things can be learned from their mistake. They choose cocos2d-x, because well, it looks like cocos2d. This seems a reasonable move if you don’t know already cocos2d-x. After that, their approach was that every other week they manually rewrote every objective-c line to c++. This is where I threw an exception. :)
My first reaction was that I was amazed that this method worked. I mean they reimplemented half of the Foundation framework, some of Cocoa Touch, they even found a way to mimic objective-c’s runtime. I think what they achieved there is commendable. On the other hand the end result was a much slower game, than the original. The code was super ugly. The engineers did not own the code. They were more like robots, who manually rewrote every line of code to cocos2d-x. They were game devs who were forced to never made a single creative decision during work.
They worked like this to reach feature parity on both platform. The funny thing is that as far as I know they never get there. The iOS app was always several steps ahead of them.
The entire rewrite took them more time than what the iOS team needed to release the original game on iOS. I think this alone would have been enough to abandon this method forever. But the end of the story is that I couldn’t convince a manager to try out a new approach instead of this completely broken one, so I didn’t stay there long.
I think it’s always a bad move to tie your engineers hands in any way. I’ve yet to see a good enough reason for that. The sad truth is that most of the time when I encountered this at different companies, they didn’t reach their original goal. Sometimes rewriting a code from scratch is not the worst choice you can make. Reaching feature parity does not require to bound two source code together. By doing that you would instantly put a serious burden on every engineer in your team, with little hope to achieve the original goal. Sharing knowledge between teams is much more important than putting artificial chains on them.