Transcript
Page 1: "Architecting and testing large iOS apps: lessons from Facebook". Adam Ernst, Facebook
Page 2: "Architecting and testing large iOS apps: lessons from Facebook". Adam Ernst, Facebook

Adam Ernst

Architecting and Testing Large iOS Apps: Lessons from Facebook

Facebook

Page 3: "Architecting and testing large iOS apps: lessons from Facebook". Adam Ernst, Facebook

developers

Adam [email protected]

@adamjernstgithub.com/adamjernst

Testing Facebook for iOS

Page 4: "Architecting and testing large iOS apps: lessons from Facebook". Adam Ernst, Facebook

Agenda

▪ “Over the wall”: some history

▪Product Teams and the Core Team

▪Scheduling and Stabilizing Releases

▪How We Develop

▪Eating the Dogfood: Builds

▪A Culture of Unit Testing

▪Future of iOS at Facebook

Page 5: "Architecting and testing large iOS apps: lessons from Facebook". Adam Ernst, Facebook

History

Page 6: "Architecting and testing large iOS apps: lessons from Facebook". Adam Ernst, Facebook

Facebook for Mobile

▪Web deeply engrained in Facebook’s DNA

▪Use HTML!/Javascript within “wrapper” native app

▪Developed our own framework for advanced integration(image uploads, photo browsers, mixing native/web elements)

Page 7: "Architecting and testing large iOS apps: lessons from Facebook". Adam Ernst, Facebook

HTML as an app platform

▪What does it bring us?

One CodebaseInstant Updates A/B Testing

Page 8: "Architecting and testing large iOS apps: lessons from Facebook". Adam Ernst, Facebook

HTML as an app platform

Page 9: "Architecting and testing large iOS apps: lessons from Facebook". Adam Ernst, Facebook

A few engineers in a room

▪Facebook for iOS !." began as an experiment

Page 10: "Architecting and testing large iOS apps: lessons from Facebook". Adam Ernst, Facebook

A few engineers in a room

▪Facebook for iOS !." began as an experiment

▪Could we achieve better results with native code?

Page 11: "Architecting and testing large iOS apps: lessons from Facebook". Adam Ernst, Facebook

A few engineers in a room

▪Facebook for iOS !." began as an experiment

▪Could we achieve better results with native code?

▪High barrier: requires rewriting every part of Facebook’s mobile UI!

Page 12: "Architecting and testing large iOS apps: lessons from Facebook". Adam Ernst, Facebook

A few engineers in a room

▪Facebook for iOS !." began as an experiment

▪Could we achieve better results with native code?

▪High barrier: requires rewriting every part of Facebook’s mobile UI!

▪Couldn’t disrupt company for something that might not ship(At the time, few iOS engineers at Facebook anyway)

Page 13: "Architecting and testing large iOS apps: lessons from Facebook". Adam Ernst, Facebook

A few engineers in a room

▪Facebook for iOS !." began as an experiment

▪Could we achieve better results with native code?

▪High barrier: requires rewriting every part of Facebook’s mobile UI!

▪Couldn’t disrupt company for something that might not ship(At the time, few iOS engineers at Facebook anyway)

▪Sequester some engineers in a war room, and have themwrite the product from top to bottom

Page 14: "Architecting and testing large iOS apps: lessons from Facebook". Adam Ernst, Facebook

Reaction

▪Press loved it

▪More importantly…

▪ Perceived Speed way up

▪ User Ratings way up

▪#x Speed increase!

Page 15: "Architecting and testing large iOS apps: lessons from Facebook". Adam Ernst, Facebook

Reaction

▪Press loved it

▪More importantly…

▪ Perceived Speed way up

▪ User Ratings way up

▪#x Speed increase!

Page 16: "Architecting and testing large iOS apps: lessons from Facebook". Adam Ernst, Facebook

Team

Page 17: "Architecting and testing large iOS apps: lessons from Facebook". Adam Ernst, Facebook

A multi-app ecosystem

Page 18: "Architecting and testing large iOS apps: lessons from Facebook". Adam Ernst, Facebook

A multi-app ecosystem

Page 19: "Architecting and testing large iOS apps: lessons from Facebook". Adam Ernst, Facebook

The Team Sandwich

Release Team: Release & Stabilization ProcessRelease Team: Release & Stabilization ProcessRelease Team: Release & Stabilization ProcessRelease Team: Release & Stabilization ProcessRelease Team: Release & Stabilization Process

Photos Feed Search Places Messages

Product Infrastructure Team: Shared LibrariesProduct Infrastructure Team: Shared LibrariesProduct Infrastructure Team: Shared LibrariesProduct Infrastructure Team: Shared LibrariesProduct Infrastructure Team: Shared Libraries

Page 20: "Architecting and testing large iOS apps: lessons from Facebook". Adam Ernst, Facebook

Product Cycle

Page 21: "Architecting and testing large iOS apps: lessons from Facebook". Adam Ernst, Facebook
Page 22: "Architecting and testing large iOS apps: lessons from Facebook". Adam Ernst, Facebook
Page 23: "Architecting and testing large iOS apps: lessons from Facebook". Adam Ernst, Facebook

Fixed Release Cycle

▪Waiting for all features in a release to be “done” slowed us down, and we want to move fast

▪So, ship an update every X weeks

▪ … no matter what

▪Popularized by Mozilla

Page 24: "Architecting and testing large iOS apps: lessons from Facebook". Adam Ernst, Facebook

Building features with an “off switch”

▪Every feature must be built with a way to turn it off

▪ If a feature destabilizes the build or isn’t complete, turn it off and try again next time

▪#defines – or runtime switches (preferred)

Page 25: "Architecting and testing large iOS apps: lessons from Facebook". Adam Ernst, Facebook

Building a new “Like Bar”

Page 26: "Architecting and testing large iOS apps: lessons from Facebook". Adam Ernst, Facebook

Building a new “Like Bar”

Page 27: "Architecting and testing large iOS apps: lessons from Facebook". Adam Ernst, Facebook

Development

Page 28: "Architecting and testing large iOS apps: lessons from Facebook". Adam Ernst, Facebook
Page 29: "Architecting and testing large iOS apps: lessons from Facebook". Adam Ernst, Facebook
Page 30: "Architecting and testing large iOS apps: lessons from Facebook". Adam Ernst, Facebook

Phabricator

Text

Page 31: "Architecting and testing large iOS apps: lessons from Facebook". Adam Ernst, Facebook

Phabricator

Text

Page 32: "Architecting and testing large iOS apps: lessons from Facebook". Adam Ernst, Facebook

Phabricator

Text

Page 33: "Architecting and testing large iOS apps: lessons from Facebook". Adam Ernst, Facebook

Phabricator

Text

Page 34: "Architecting and testing large iOS apps: lessons from Facebook". Adam Ernst, Facebook

Phabricator

Text

Page 35: "Architecting and testing large iOS apps: lessons from Facebook". Adam Ernst, Facebook
Page 36: "Architecting and testing large iOS apps: lessons from Facebook". Adam Ernst, Facebook
Page 37: "Architecting and testing large iOS apps: lessons from Facebook". Adam Ernst, Facebook

Check code

▪ ‘arc lint’

▪Set up rules to catch common mistakes

▪Examples:

▪ Enforce style guidelines

▪ Warn against using certain symbols

▪ Check for common pattern mistakes, and fix them!

Page 38: "Architecting and testing large iOS apps: lessons from Facebook". Adam Ernst, Facebook

@implementation SampleClass

- (void)setupAccessibility{ self.myView.accessibilityTraits = UIAccessibilityTraitHeader;}

- (void)dealloc{ [_myString dealloc]; [super dealloc];}

@end

Page 39: "Architecting and testing large iOS apps: lessons from Facebook". Adam Ernst, Facebook
Page 40: "Architecting and testing large iOS apps: lessons from Facebook". Adam Ernst, Facebook
Page 41: "Architecting and testing large iOS apps: lessons from Facebook". Adam Ernst, Facebook

RegEx Lint Rule

AST Lint Rule

NEW

clang

AST

Page 42: "Architecting and testing large iOS apps: lessons from Facebook". Adam Ernst, Facebook

AST Lint Rules

▪Regular expressions have false positives and negatives

▪ Dot notation vs braces

▪ Even mentioning forbidden API in a comment triggers rule!

▪AST lint rules can “understand the code”

Page 43: "Architecting and testing large iOS apps: lessons from Facebook". Adam Ernst, Facebook

Verify changes: Buildbot

▪Continuous integration

▪Distributed across multiple build machines

▪Sanity check:

▪ Do all projects still build?

▪ Do all unit tests still pass?

▪Emails engineer, and updates Phabricator on failure

Page 44: "Architecting and testing large iOS apps: lessons from Facebook". Adam Ernst, Facebook

Static Analyzer Remote Runs

Page 45: "Architecting and testing large iOS apps: lessons from Facebook". Adam Ernst, Facebook

Buildbot failure email

Page 46: "Architecting and testing large iOS apps: lessons from Facebook". Adam Ernst, Facebook

Builds

Page 47: "Architecting and testing large iOS apps: lessons from Facebook". Adam Ernst, Facebook

Multiple Builds

▪Use different bundle IDs and icons for different types of builds:

▪ Development

▪ Daily Build

▪ App Store release

▪ Special branches

▪Burn your build number into your icon

Page 48: "Architecting and testing large iOS apps: lessons from Facebook". Adam Ernst, Facebook
Page 49: "Architecting and testing large iOS apps: lessons from Facebook". Adam Ernst, Facebook

Branching

▪Two concurrent branches:

▪Master

▪ Engineers make progress on future features

▪ All changes checked in here first

▪ …including bug fixes for Releases

▪Release

▪ Once verified in Master…

▪ …“Release team” pulls them into Release branch

Page 50: "Architecting and testing large iOS apps: lessons from Facebook". Adam Ernst, Facebook

Testing

Page 51: "Architecting and testing large iOS apps: lessons from Facebook". Adam Ernst, Facebook

Testing is important to Facebook

▪Not in Facebook’s culture:

▪ SDEs “in Test”

▪ Large QA departments

▪Definitely in Facebook culture:

▪ High quality, reliable user experience

▪We believe in developer-authored unit tests

Page 52: "Architecting and testing large iOS apps: lessons from Facebook". Adam Ernst, Facebook

Snapshot Unit Tests

typedef NS_ENUM(NSUInteger, FBActionStyle) { FBActionStyleUndefined = 0, FBActionStyleDefault = 1, FBActionStyleProminent = 2, FBActionStyleSubdued = 3,}

static NSArray *FBActionGetStyles() { return @[@"DEFAULT", @"PROMINENT", @"SUBDUED"];}

NSString *FBActionStringFromStyleValue(FBActionStyle styleValue) { NSArray *styles = FBActionGetStyles();

if (styleValue < [styles count]) { return [styles objectAtIndex:styleValue]; } else { return nil; }}

typedef NS_ENUM(NSUInteger, FBActionStyle) { FBActionStyleUndefined = 0, FBActionStyleDefault = 1, FBActionStyleProminent = 2, FBActionStyleSubdued = 3,}

NSString *FBActionStringFromStyleValue(FBActionStyle styleValue) { switch (styleValue) { case FBActionStyleDefault: return @”DEFAULT”; case FBActionStyleProminent: return @”PROMINENT”; case FBActionStyleSubdued: return @”SUBDUED”; } return nil;}

Old Code: New Code:

Page 53: "Architecting and testing large iOS apps: lessons from Facebook". Adam Ernst, Facebook

Snapshot Test Case Failures

2013-09-24 17:59:01.743 FBAppKitTestHost[44975:a0b] If you have Kaleidoscope installed you can run this command to see an image diff:ksdiff "/Users/adamjernst/Library/Application Support/iPhone Simulator/7.0/Applications/D85DEF94-79B2-49AB-ABAA-093044D754CF/tmp/FBMegaphoneViewSnapshotTests/[email protected]" "/Users/adamjernst/Library/Application Support/iPhone Simulator/7.0/Applications/D85DEF94-79B2-49AB-ABAA-093044D754CF/tmp/FBMegaphoneViewSnapshotTests/[email protected]"/Users/adamjernst/Documents/fbobjc/Libraries/FBAppKit/FBAppKitTests/FBMegaphoneViewSnapshotTests.m:276: SenTestFailureException: "comparisonSuccess__" should be true. Snapshot comparison failed: Error Domain=FBTestSnapshotControllerErrorDomain Code=4 "Images different" UserInfo=0x7b660230 {NSLocalizedDescription=Images different}:

273 FBMegaphoneStoryView *view = [FBMegaphoneStoryView viewForMegaphone:megaphone target:nil frame:CGRectMake(0, 0, 768, 320)];274 NSString *newIdentifier = identifier.length ? [NSString stringWithFormat:@"%@_pad", identifier] : @"pad";275 [view sizeToFit];276 FBSnapshotVerifyView(view, newIdentifier); ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

github.com/facebook/ios-snapshot-test-case

NEW

Page 54: "Architecting and testing large iOS apps: lessons from Facebook". Adam Ernst, Facebook

Watchdog Timer

Main Thread work work work work workwork

Page 55: "Architecting and testing large iOS apps: lessons from Facebook". Adam Ernst, Facebook

Watchdog Timer

Main Thread work work work work workwork

Watchdog Thread

ping ac

k

ping ac

k

ping ac

k

ping ac

k

ping ac

k

ping

Uh oh! No ack.Log backtrace.

Page 56: "Architecting and testing large iOS apps: lessons from Facebook". Adam Ernst, Facebook

Watchdog Timer: How it Works

▪Max-priority thread pings main thread every X seconds

▪ If main thread doesn’t respond in time…

▪ Freeze the main thread

▪ Get the backtrace to see what code is running

▪ Log it to our servers for analysis

github.com/facebook/ios-watchdog-timer

NEW

Page 57: "Architecting and testing large iOS apps: lessons from Facebook". Adam Ernst, Facebook

Rage Shake

▪Shake the device to report a bug

▪Captures:

▪ Screen shot (with annotations!)

▪ Network logs

▪ Last crash log

▪ Last x-seconds of logging

▪ Dumps the view hierarchy

▪Automatically files a bug and sends it to Facebook

Page 58: "Architecting and testing large iOS apps: lessons from Facebook". Adam Ernst, Facebook

Internal Settings

▪Toggle features on and off

▪Change parameters

▪Trigger events

Page 59: "Architecting and testing large iOS apps: lessons from Facebook". Adam Ernst, Facebook

Future of iOS @ Facebook


Top Related