Native mobile apps at FARFETCH

Mobile native apps at Farfetch had a humble and curious beginning in 2014. As the Farfetch marketplace evolved on the web, the first app (iOS only) was conceived to be a companion app describing the boutiques that were geographically near the user. In 2015 the company strategy changed and plans for full-fledged e-commerce native mobile applications started to be put into action under the umbrella of the company’s Mobile First strategic pillar. The pivotal moment that allowed this was the development of our first REST API.
Initial challenges
Mobile engineers had to tackle two different sets of challenges. The first was technical and the second was business related.
The first set was the need to release the iOS app in tandem with a moving API that was trying to accommodate years of development within a REST approach. This raised two challenges, keeping up with contract and business logic changes. The iOS app was being built in Objective-C - Swift was out but we couldn’t afford to deal with another moving target - and we took two approaches. The first approach was dynamic programming for mapping properties to model objects, by leveraging what is arguably one of the greatest characteristics of the language (along with its performance for general purpose native applications). The other approach was dabbling in reactive programming, more specifically the use of promises, which are very common in newer languages but are a somewhat foreign concept in Objective-C. The usage of promises for chaining API calls was critical to accommodate change. At a later stage, the Android app also used the same strategy with RxJava. This allowed to move all business logic to a new abstraction layer and all its computational needs were placed in an Rx computational thread.
The second set of challenges was related to how e-commerce works. Firstly, it is extremely analytics oriented; secondly, user engagement through marketing campaigns to redirect users to a specific area (deep linking) is an absolutely crucial tool that needs to work flawlessly regardless of origin, e.g. push notification or HTTP link. For analytics, the major concern was to avoid polluting the code base responsible for the consumer-facing features and code scattering. To solve this we used Aspect-Oriented Programming in both apps. For deep linking, we implemented a navigation system flexible enough to be used by the content team, tackling all possible navigation scenarios inside the apps including different navigation structures depending if it’s a tablet or a phone.
Growth challenges
As we moved on from the initial app releases, we noticed that the metrics regarding user engagement and conversion were very good from an organic growth perspective. This lead to a new challenge - how to release native apps at a steady flow that would satisfy the need for churning out new features to our customers and make sure the codebase/architecture wouldn’t degrade and could be improved when necessary. A new release after every sprint seemed like the perfect sweet spot. We invested in two areas: CI and branch/fork management. Regarding CI we dropped Xcode Bots in iOS due to its flakiness and started using Team City along with fastlane tools which give us the flexibility to easily migrate to another CI tool; Android invested in Jenkins combined with the repository manager Artifactory to handle Gradle internal dependencies. Both solutions complied with all our every engineering need: reliable compiling and testing, fast deployment on devices for QA, reliable upload for the stores. Engineers regard branching/forking that is kept after a release as technical debt as it will inevitably lead to problematic merge conflicts in the future and a very inefficient use of engineer’s time. In order to solve this, every single line of code created during a sprint has to be merged at the end of the sprint. If it relates to an incomplete feature that is not customer ready the code must stay behind a client-side toggle that can only be removed in the next release. This allows engineers to implement code at their own pace, regardless of release schedule and eliminate the possibility of hard to solve merge conflicts.
As we started to have more teams on iOS working on different features, due to the diversification of strategy to tackle different geographic and consumer behaviours to grow key metrics, we had to deal with obstacles that are not common in app development since it’s usual to have 5 to 10 native mobile engineers. The solution found was modularisation of logical areas of the app. This brought us a much better control of mutability and changes across the app, less unexpected side-effects, smoother onboarding of new engineers, easier testing that increased reliability and confidence, common interface structure for each module and overall enforcing of a clean and intelligible architecture.
Making sure that performance metrics are being supervised was another big concern as the iOS teams grew in number. We started by generating reports every time the release pipeline was run to gather data regarding download and install size for each device type and cold (1st install) and warm app start time. In the future, we want to be able to produce information about graphical performance on the most important screens.
As Native Mobile grew the number of customers, we also started to have new markets thriving - China for instance - and customers in different markets have different expectations on user experience. Due to this, we decided to split the app - one for China, another for the rest of the world. The China team is focused on changing the customer experience (and subsequently the UI) and all iOS engineers (regardless of geography) can share and contribute to all SDKs/libraries (around 70). Even though product priorities are distinct, technical concerns and infrastructure are the same.
Future challenges
As we look to the future new hurdles are arising due to the native apps importance and team size. We aspire to make it easier and more fulfilling to build apps at Farfetch with our own tools. To reach this goal we’re working towards even better monitoring, e.g. graphical performance on the most important screens; wrap up modularization and open the possibility of having Farfetch lite versions, e.g. a sample app having the Homepage only, in order to speed up compiling time, reduce dependencies and allow for faster development; Component-driven UI for faster iteration when building and market test UI/UX, plus sharing the same tool for UI building between engineers and designers. This will be done using a specification (backed by JSON) that maps directly to the UI Kit defined by product designers. This way we will have a design specification along with its representation in JSON. This representation can be changed during development (without compiling) to see modifications on the device. Once this is stabilised the next step will be the implementation of Backend-Driven UI. This will be executed by using the aforementioned components and by building a system that allows for pushing the UI definition files to the backend; managing cache, versioning and graceful deprecation. This will allow for faster UI deployment, easier A/B testing, faster translation process and ability to have UI dependent on user attributes (e.g. country, benefit) defined on the server side.
Predicting the future
Niels Bohr once said "Prediction is very difficult, especially about the future”, with this caveat we would like to tell you what the future might hold. There is a lot of discussion going on if the desktop is doomed in e-commerce. Regardless of how customers reach online retailers (native apps, websites/PWAs, voice search) the mobile phone will be central. The tipping point for e-commerce that will probably make desktop a secondary channel will be when phones can provide more and better information about products and in a way that a large screen is no longer an advantage. This will result from merging Augmented Reality and Machine Learning. The former will provide customers with accurate, on-time, useful and comprehensible information, whereas the latter will guarantee that the information is adequate to that specific user.
Initial challenges
Mobile engineers had to tackle two different sets of challenges. The first was technical and the second was business related.
The first set was the need to release the iOS app in tandem with a moving API that was trying to accommodate years of development within a REST approach. This raised two challenges, keeping up with contract and business logic changes. The iOS app was being built in Objective-C - Swift was out but we couldn’t afford to deal with another moving target - and we took two approaches. The first approach was dynamic programming for mapping properties to model objects, by leveraging what is arguably one of the greatest characteristics of the language (along with its performance for general purpose native applications). The other approach was dabbling in reactive programming, more specifically the use of promises, which are very common in newer languages but are a somewhat foreign concept in Objective-C. The usage of promises for chaining API calls was critical to accommodate change. At a later stage, the Android app also used the same strategy with RxJava. This allowed to move all business logic to a new abstraction layer and all its computational needs were placed in an Rx computational thread.
The second set of challenges was related to how e-commerce works. Firstly, it is extremely analytics oriented; secondly, user engagement through marketing campaigns to redirect users to a specific area (deep linking) is an absolutely crucial tool that needs to work flawlessly regardless of origin, e.g. push notification or HTTP link. For analytics, the major concern was to avoid polluting the code base responsible for the consumer-facing features and code scattering. To solve this we used Aspect-Oriented Programming in both apps. For deep linking, we implemented a navigation system flexible enough to be used by the content team, tackling all possible navigation scenarios inside the apps including different navigation structures depending if it’s a tablet or a phone.
Growth challenges
As we moved on from the initial app releases, we noticed that the metrics regarding user engagement and conversion were very good from an organic growth perspective. This lead to a new challenge - how to release native apps at a steady flow that would satisfy the need for churning out new features to our customers and make sure the codebase/architecture wouldn’t degrade and could be improved when necessary. A new release after every sprint seemed like the perfect sweet spot. We invested in two areas: CI and branch/fork management. Regarding CI we dropped Xcode Bots in iOS due to its flakiness and started using Team City along with fastlane tools which give us the flexibility to easily migrate to another CI tool; Android invested in Jenkins combined with the repository manager Artifactory to handle Gradle internal dependencies. Both solutions complied with all our every engineering need: reliable compiling and testing, fast deployment on devices for QA, reliable upload for the stores. Engineers regard branching/forking that is kept after a release as technical debt as it will inevitably lead to problematic merge conflicts in the future and a very inefficient use of engineer’s time. In order to solve this, every single line of code created during a sprint has to be merged at the end of the sprint. If it relates to an incomplete feature that is not customer ready the code must stay behind a client-side toggle that can only be removed in the next release. This allows engineers to implement code at their own pace, regardless of release schedule and eliminate the possibility of hard to solve merge conflicts.
As we started to have more teams on iOS working on different features, due to the diversification of strategy to tackle different geographic and consumer behaviours to grow key metrics, we had to deal with obstacles that are not common in app development since it’s usual to have 5 to 10 native mobile engineers. The solution found was modularisation of logical areas of the app. This brought us a much better control of mutability and changes across the app, less unexpected side-effects, smoother onboarding of new engineers, easier testing that increased reliability and confidence, common interface structure for each module and overall enforcing of a clean and intelligible architecture.
Making sure that performance metrics are being supervised was another big concern as the iOS teams grew in number. We started by generating reports every time the release pipeline was run to gather data regarding download and install size for each device type and cold (1st install) and warm app start time. In the future, we want to be able to produce information about graphical performance on the most important screens.
As Native Mobile grew the number of customers, we also started to have new markets thriving - China for instance - and customers in different markets have different expectations on user experience. Due to this, we decided to split the app - one for China, another for the rest of the world. The China team is focused on changing the customer experience (and subsequently the UI) and all iOS engineers (regardless of geography) can share and contribute to all SDKs/libraries (around 70). Even though product priorities are distinct, technical concerns and infrastructure are the same.
Future challenges
As we look to the future new hurdles are arising due to the native apps importance and team size. We aspire to make it easier and more fulfilling to build apps at Farfetch with our own tools. To reach this goal we’re working towards even better monitoring, e.g. graphical performance on the most important screens; wrap up modularization and open the possibility of having Farfetch lite versions, e.g. a sample app having the Homepage only, in order to speed up compiling time, reduce dependencies and allow for faster development; Component-driven UI for faster iteration when building and market test UI/UX, plus sharing the same tool for UI building between engineers and designers. This will be done using a specification (backed by JSON) that maps directly to the UI Kit defined by product designers. This way we will have a design specification along with its representation in JSON. This representation can be changed during development (without compiling) to see modifications on the device. Once this is stabilised the next step will be the implementation of Backend-Driven UI. This will be executed by using the aforementioned components and by building a system that allows for pushing the UI definition files to the backend; managing cache, versioning and graceful deprecation. This will allow for faster UI deployment, easier A/B testing, faster translation process and ability to have UI dependent on user attributes (e.g. country, benefit) defined on the server side.
Predicting the future
Niels Bohr once said "Prediction is very difficult, especially about the future”, with this caveat we would like to tell you what the future might hold. There is a lot of discussion going on if the desktop is doomed in e-commerce. Regardless of how customers reach online retailers (native apps, websites/PWAs, voice search) the mobile phone will be central. The tipping point for e-commerce that will probably make desktop a secondary channel will be when phones can provide more and better information about products and in a way that a large screen is no longer an advantage. This will result from merging Augmented Reality and Machine Learning. The former will provide customers with accurate, on-time, useful and comprehensible information, whereas the latter will guarantee that the information is adequate to that specific user.