In December 2018, our team was asked to develop a new Android and iOS experience for buying and selling vehicles on eBay, catered to auto enthusiasts. We were given autonomy to research, design and build the eBay Motors App as we saw fit. However, we needed to go from concept to both app stores in less than a year, and it had to include a feature set that existing eBay users had come to expect. We were both excited and a little intimidated by this opportunity, and we wanted to exceed expectations.
Our team had been building native apps together for many years. We knew what we were capable of, and we also knew that this would be a huge undertaking. Building two separate apps with a small team would be nearly impossible if we stuck to traditional native development.
In the past, we had researched cross-platform SDKs and had been left unimpressed. Fortunately, just weeks before our team was given its assignment, Flutter 1.0 was released, and it approached the cross-platform problem in a promising new way. Our team had been following the development and preview releases of Flutter, and we were well aware of the enthusiasm that was growing around the technology. The first stable release, as well as Google’s sound roadmap, suggested that Flutter might well be the solution for our team to deliver on both platforms in a compelling way under our aggressive deadlines. On the surface, Flutter certainly seemed to approach UI development in a way that addressed many of our concerns.
Still, we knew going all-in on an unfamiliar technology was a risky proposition. Thankfully, our product manager Richard Sipe and designer Thai Dang needed time to develop a product vision, so we had a bit of leeway to properly evaluate if Flutter was the right fit. We scheduled team workshops to learn about Flutter and the Dart language. We approached this with a healthy amount of skepticism and bent over backwards to identify areas where Flutter might not meet our needs. To our delight, the technology stood up to each of our challenges. Before long, even the most skeptical engineers were excited.
We found that Flutter enabled us to solve interesting problems faster than we anticipated. The development experience was far more enjoyable and we could quickly build a user experience that was very consistent between iOS and Android. Our team wanted to make a high-quality product and we knew that automated testing was an integral requirement. To our surprise, Flutter’s testing story was among the best we had seen. After several weeks of working with Flutter, we were confident it was our best chance to hit our goals on time.
Fast forward nineteen months, our team was able to meet every deadline. Our first beta was in our CEO’s hands within three months of receiving our first product requirements, and a few months later we released our app to the public. We are excited to be the team that positioned eBay among the first enterprise companies to use Flutter in a production app. Our experience has demonstrated how Flutter provides speed and agility, even with all the overhead inherent with an enterprise product. That said, it was not easy. Our team worked hard and dedicated itself to being successful. Along the way, we uncovered unexpected benefits that continue to pay dividends as we have added new features to the eBay Motors App. Let’s get into some of the details.
One Does Not Simply Accelerate
The first challenge was to scale and onboard the rest of our team. We knew we had to hit the ground running once our product requirements started rolling in. No one had prior experience with Dart and Flutter. Everyone spent time learning the new stack, and went through the excellent introductory course at AppBrewery.
The harder, but more valuable learning was developing our team working agreement. We had to take two teams, from different backgrounds with different approaches to solving problems, and bring them together into one cohesive unit. This was an arduous process that caused us to shed many of our prior beliefs about developing apps, and forced us to re-evaluate what we truly valued in building quality software. We had fierce discussions about design patterns and architecture, automated testing, and the guard rails that were necessary for us to move quickly, and innovate, without locking us into implementation decisions too early. We can’t overstate how valuable this upfront process was. It allowed us to truly start building this app as one team, and gave us a consistent voice in working with Product.
As we began building our app in earnest, we were amazed at the amount of code sharing Flutter enabled us to achieve. Today, our repository consists of:
- 98.3% Dart code (~220k lines of Dart)
- 1.1% Scripts, CI, various Automation Tools for our development lifecycle
- .6% split across Kotlin, Java, Swift and Objective-C
This level of cross-platform code sharing far exceeded our expectations. The reality is we rarely spend time thinking about or working on platform specific integration, and instead are able to focus almost exclusively on building product features. Early on, we expected to jump through hoops as we needed to access native capabilities. What we discovered was that it was trivial to integrate with device APIs to enable use of the camera, video capture, machine learning, geolocation, secure storage, and other device capabilities. The Flutter ecosystem provides great plugins for these services. On rare occasions, we wrote our own plugins to encapsulate native 3rd party SDKs. These tasks were approached with uncertainty, but they were all completed in hours.
By being able to spend most of our time fully immersed in Flutter code, we realized the holy grail of cross-platform development: everything was truly “write-once.” This held true throughout the entire stack. Everything in the UI from layout, navigation, animation, and localization was shared. All of the business logic, domain models and network stack were shared. All of the table stakes like analytics, crash logging and accessibility features were implemented with shared code. We even got to share a single CI pipeline! Writing software in this fashion was a huge accelerator and allowed us to deliver new features very quickly.
One of the benefits and surprises of this high degree of code sharing was the impact it had on our test automation. Flutter’s out-of-the-box testing support outperformed our expectations. We’ve been especially impressed with how deeply we can automate the verification of our app, from complex UI interactions to the finer details of an animation. This is a testament to Flutter’s architecture in that it fully controls the pixels that are rendered across all devices and platforms, but it is also because testing is a first class concept in the framework. You can read more about our testing experience with Flutter in our prior blog post.
As we discovered the power of Flutter’s testing support, we agreed to a lofty goal: all production code must have 100% code coverage. While intimidating at first, we learned this was very attainable, and enforced the rule with automation in our pull requests. That automation is still in place today and has never wavered. This upfront discipline and our trust in Flutter’s test framework gave us the confidence to deliver quickly without fear of regression. It is common to build and test some features without ever manually testing on a device. We gained so much confidence in our test suite that our quality engineers started writing production code and became full-time Flutter engineers.
The benefits of code sharing are not unique to Flutter, although we would argue that Flutter delivers on the cross-platform promise more successfully than its competitors. However, one of our biggest surprises was that, cross-platform benefits aside, developing in Flutter was far superior to traditional native development. We informally surveyed the team to help validate our decision to use Flutter, and the responses showed the majority of the team believed developing in Flutter was over twice as fast as developing for their prior platform. Perhaps more importantly, 100% of those surveyed enjoyed Flutter development more than iOS or Android. As it turns out, happy developers tend to be more engaged and productive.
There are so many other reasons our decision to choose Flutter enabled us to move quickly. We worked alongside Product and Design as a single team, with the shared mentality we were building a single app. Requirements rarely diverged between platforms, and entire classes of work such as managing platform specific backlogs were eliminated. The reduced overhead impacted more than just the engineering team. In many cases, we were able to replace paper prototypes with functioning prototypes for user testing. We did not need to create unique designs based on each platform. Prebuilt Material and Cupertino widgets allowed us to prefer good defaults and create custom user experiences only when needed. This resulted in a consistent end user experience on every device without sacrificing the specific platform mechanics that eBay customers have come to rely on. Designs could then be iterated on in real-time with stateful hot reload, a Flutter feature that allows developers to quickly reload the code on a running device without changing the running state of the app.
These unique and interesting changes to the way we work have resulted in increased confidence. We ship a new version of our app on Android and iOS at the same time almost every week. There is never any unintentional feature drift between the two platforms. Time savings layered on top of each other has allowed us to be nimble and quick. For example, in the past, we might be concerned if we rolled out the same feature in both the Android and iOS versions of an app, and our analytics showed the feature was used significantly more on one platform versus the other. In these instances, our first assumption was that the platform with the underused feature contained a programming error. However, with Flutter, the implementations on iOS and Android are identical. We don’t waste time and energy searching for programming errors that don’t exist.
The team has continued to add features to the eBay Motor App at a rapid pace, such as a live-chat feature and an escrow account to securely transfer funds to the seller.
Flutter has not only met our expectations — it has dramatically exceeded them. This has been much more than a technology choice. In many ways, Flutter has changed and enhanced the way our team operates, making its members demonstrably more productive and happier in their day-to-day work. With speed and agility quickly becoming the new normal, development teams would be well served to look into Flutter for production apps of any size.