Tools of the Trade #DCNYC18

7a3baf2e1158e358885cdf7e89b9aa55?s=47 Ty Smith
August 27, 2018

Tools of the Trade #DCNYC18

With all the great resources and libraries available, building a new mobile app is easier than it’s ever been! But when your users come to expect your product to be as reliable as running water at a global scale, you’ll need to go above and beyond what’s currently available.

Join Ty as he walks down memory lane to cover the history of the Uber app development process. He’ll cover how it grew, the pain points discovered and lessons learned, and the tooling developed and open-sourced to scale to millions of users served by hundreds of contributors across many apps.

Topics covered will include: Buck, Uber’s architecture solution, RIBs, Proper Experimentation, Continuous integration, Code Verification and Static Analysis, and the company processes needed to facilitate a large app.

7a3baf2e1158e358885cdf7e89b9aa55?s=128

Ty Smith

August 27, 2018
Tweet

Transcript

  1. Tools of the Trade Ty Smith @tsmith

  2. @tsmith

  3. @tsmith

  4. @tsmith

  5. @tsmith

  6. @tsmith

  7. @tsmith

  8. @tsmith

  9. @tsmith

  10. @tsmith

  11. @tsmith

  12. @tsmith

  13. @tsmith

  14. @tsmith

  15. @tsmith

  16. @tsmith

  17. Topics — Monorepo — Architecture — Libraries and Tools @tsmith

  18. MONO REPO @tsmith

  19. Pain Points — Dependency Hell — Architectural Silos — Long

    Build times @tsmith
  20. ABI Breaking change @tsmith

  21. Many Repos Depedency hell ~/Uber/rider-android/build.gradle dependencies { implementation "com.uber:partner-funnel:1.2.3" implementation

    "com.uber:single-sign-on:0.7.0" implementation "com.uber:storage:2.1.0" implementation "com.uber:networking:2.3.1" implementation "com.uber:analytics:1.9.0" } ~/Uber/partner-funnel-android/build.gradle dependencies { implementation "com.uber:storage:2.2.0" implementation "com.uber:networking:2.0.0" implementation "com.uber:analytics:1.3.5" } @tsmith
  22. Monorepo ~/Uber/Android |-- .git |-- build.gradle |-- apps | |--

    rider | |-- driver | |-- eats |-- features | |-- partner-funnel | |-- single-sign-on |-- libraries | |-- storage | |-- networking | |-- analytics @tsmith
  23. Monorepo ~/Uber/android/apps/rider/build.gradle dependencies { implementation project(":features:partner-funnel") implementation project(":features:single-sign-on") implementation project(":libraries:storage")

    implementation project(":libraries:networking") implementation project(":libraries:analytics") } ~/Uber/android/features/partner-funnel/build.gradle dependencies { implementation project(":libraries:storage") implementation project(":libraries:networking") implementation project(":libraries:analytics") } @tsmith
  24. Buck @tsmith | buckbuild.com

  25. Buck — Output based — Highly Parellelizable — Fast! !

    — IDE Integration @tsmith
  26. Buck @tsmith

  27. OKBuck A gradle plugin that lets developers utilize the Buck

    build system on a gradle project. @tsmith | github.com/uber/okbuck
  28. OKBuck plugins { id 'com.uber.okbuck' version '0.36.0' } @tsmith |

    github.com/uber/okbuck
  29. OKBuck ./gradlew :buckWrapper //List targets ./buckw list //Build Target ./buckw

    build <target> @tsmith | github.com/uber/okbuck
  30. Exopackage @tsmith | buckbuild.com/article/exopackage.html

  31. @tsmith

  32. Submit Queue @tsmith

  33. Architecture @tsmith | Photo by Rich Niewiroski Jr. | CC

    BY 2.5
  34. @tsmith

  35. 99.99% reliability of core flows Enable global rollback of core

    flows to a guaranteed working state @tsmith
  36. Support growth for years Narrow and decouple functionality as much

    as possible @tsmith
  37. Rails for Design and Code Guidelines for both architecture and

    design @tsmith
  38. Monitoring is a First-Class Citizen Automatic analytics, logging, debugging, and

    tracing @tsmith
  39. De-risk Experimentation Application framework with plugin API @tsmith

  40. Make Magic Performance second to none, graceful degradation on low-end

    devices and networks @tsmith
  41. MVC MVVM VIPER Fat View Controller Fat View Controller View

    based logic separation Locked view & business tree Locked view & business tree Locked view & business tree @tsmith
  42. @tsmith | github.com/uber/ribs

  43. RIBs @tsmith | github.com/uber/ribs

  44. RIBs @tsmith | github.com/uber/ribs

  45. RIBs @tsmith | github.com/uber/ribs

  46. RIBs @tsmith | github.com/uber/ribs

  47. RIBs @tsmith | github.com/uber/ribs

  48. RIBs @tsmith | github.com/uber/ribs

  49. RIBs Communication @tsmith | github.com/uber/ribs

  50. Libraries @tsmith | Photo by DAVID ILIFF | CC BY-SA

    3.0
  51. RIBs Intellij Plugin @tsmith | github.com/uber/RIBs/tree/master/android/tooling/rib-intellij-plugin

  52. Motif A simple DI API for Android / Java @tsmith

    | github.com/uber/motif
  53. Motif @motif.Scope interface MainScope { ChildScope child(); @motif.Objects class Objects

    { @Expose Database database() { return new Database(); } } } @tsmith | github.com/uber/motif
  54. Motif @motif.Scope interface ChildScope { ChildController controller(); @motif.Objects class Objects

    { ChildView view() { return new ChildView(); } ChildController controller(Database database, ChildView view) { return new ChildController(database, view); } } } @tsmith | github.com/uber/motif
  55. @tsmith

  56. Crumb An annotation processor for breadcrumbing metadata across compilation boundaries

    @tsmith | github.com/uber/crumb
  57. Crumb @tsmith | github.com/uber/crumb

  58. RIBs Plugins class FooPluginFactory : PluginFactory<Intent, FooPlugin> { override fun

    pluginSwitch() = FOO_PLUGIN_SWITCH override fun isApplicable(intent : Intent) = intent.hasExtra("bar") override fun createNewPlugin(intent : Intent) = FooPlugin(intent) } @tsmith | github.com/uber/crumb
  59. RIBs Plugins class BarPluginPoint : PluginPoint<Intent, BarPlugin>() { override val

    internalPluginFactories = listOf(FooPluginFactory()) } @tsmith | github.com/uber/crumb
  60. RIBs Plugins val barPluginPoint = BarPluginPoint(abFramework) val BarPlugin : BarPlugin?

    = pluginPoint.getPlugin(intent) barPlugin?.doWork() @tsmith | github.com/uber/crumb
  61. @tsmith

  62. Autodispose Automatic binding+disposal of RxJava 2 streams. @tsmith | github.com/uber/autodispose

  63. Autodispose observable .doStuff() .as(autoDisposable(this)) .subscribe { ... } @tsmith |

    github.com/uber/autodispose
  64. Autodispose — Maybe — LifecycleScopeProvider — ScopeProvider @tsmith | github.com/uber/autodispose

  65. Autodispose Extras! — Kotlin extensions — RX lifecycle extensions —

    Architecture Components adapter — Plugins — Errorprone checker @tsmith | github.com/uber/autodispose
  66. @tsmith

  67. Workflow Represent a workflow of reactive steps @tsmith | github.com/uber/RIBs/tree/master/android/libraries/rib-workflow

  68. Workflow — Step — ActionableItem — Workflow @tsmith

  69. Workflow @tsmith | github.com/uber/RIBs/tree/master/android/libraries/rib-workflow

  70. Workflow class SingleSignOnWorkflow : HelixRootWorkflow<Step.NoValue, SingleSignOnDeepLink>() { override fun getSteps(rootActionableItem

    : AppRootActionableItem, deeplink : SingleSignOnDeepLink ) : Step<Step.NoValue, SingleSignOnActionableItem> { return rootActionableItem .onStep(waitUntilSignedIn()) .onStep(GoToMain()) .onStep(showFullScreenRib(singleSignOnRib(deeplink))) } } @tsmith | github.com/uber/RIBs/tree/master/android/libraries/rib-workflow
  71. Workflow override fun waitUntilSignedIn() : Step<Step.NoValue, RootSignedInActionableItem> { return Step.from(

    mutableSession .getAuthStateAsync() .filter { it is AuthState.AuthStateLoggedIn } .take(1) .map { Step.Data.toActionableItem(this) } .singleOrError()) } @tsmith | github.com/uber/RIBs/tree/master/android/libraries/rib-workflow
  72. @tsmith

  73. @tsmith

  74. @tsmith

  75. Artist An artist creates views. Artist is a Gradle plugin

    that codegens a base set of Android Views. @tsmith | github.com/uber/artist
  76. Artist — Stencil: The View Generator — Trait: Reusable functionality

    @tsmith | github.com/uber/artist
  77. Artist class VisibilityTrait : Trait { override fun generateFor( type:

    Builder, initMethod: MethodSpec.Builder, rClass: ClassName, baseType: String) { listOf("visible", "invisible", "gone") .forEach { type.addMethod(createVisibilityConvenienceMethod(it)) } } private fun createVisibilityConvenienceMethod(type: String): MethodSpec { return MethodSpec.methodBuilder("is${type.capitalize()}") .addModifiers(Modifier.PUBLIC) .returns(TypeName.BOOLEAN) .addStatement("return getVisibility() == \$T.${type.toUpperCase()}", TypeNames.Android.View) .build() } } @tsmith | github.com/uber/artist
  78. Artist class SwitchStencil : ViewStencil( extendedType = "android.support.v7.widget.SwitchCompat", constructorCount =

    3, defaultAttrRes = "switchStyle", addedTraits = VisibilityTrait::class.java) { override fun name() = "FooSwitch" } @tsmith | github.com/uber/artist
  79. Artist public class FooSwitch extends SwitchCompat { // Constructors //

    protected init method - provided in every stencil public boolean isVisible() { return getVisibility() == View.VISIBLE; } public boolean isGone() { return getVisibility() == View.GONE; } public boolean isInvisible() { return getVisibility() == View.INVISIBLE; } } @tsmith | github.com/uber/artist
  80. Stylist A stylist creates cool styles. Stylist is a Gradle

    plugin that codegens a base set of Android XML themes. @tsmith | github.com/uber/stylist
  81. Stylist @AutoService(ThemeStencilProvider::class) class SampleThemeStencilProvider : ThemeStencilProvider { private val textSizes

    = StyleItemGroup( StyleItem("textSizeSmall", "12dp"), StyleItem("textSizeMedium","16dp"), StyleItem("textSizeLarge", "20dp") ) override fun stencils() = linkedSetOf( ThemeStencil("Theme.Sample.Dark", "Theme.AppCompat"), ThemeStencil("Theme.Sample.Light", "Theme.AppCompat.Light") ) override fun globalStyleItemGroups() = linkedSetOf( textSizes ) } @tsmith | github.com/uber/stylist
  82. Stylist <?xml version="1.0" encoding="utf-8" standalone="no"?> <resources> <style name="Theme.Sample.Dark" parent="Theme.AppCompat"> <item

    name="textSizeSmall">12dp</item> <item name="textSizeMedium">16dp</item> <item name="textSizeLarge">20dp</item> </style> <style name="Theme.Sample.Light" parent="Theme.AppCompat.Light"> <item name="textSizeSmall">12dp</item> <item name="textSizeMedium">16dp</item> <item name="textSizeLarge">20dp</item> </style> </resources> @tsmith | github.com/uber/stylist
  83. @tsmith

  84. Lint class SwitchCompatUsageXmlDetector : ResourceXmlDetector() { override val applicableElements =

    listOf("android.support.v7.widget.SwitchCompat") override fun visitElement(context:XmlContext, element : Element) : override { context.report( Issue.create("SwitchCompatUsage", "Don't use base view SwitchCompat", "Use Provided FooSwitch", Category.CORRECTNESS, 6, Severity.ERROR, Implementation(XmlUViewUsageDetector::class.java, Scope.RESOURCE_FILE_SCOPE)), element, context.getLocation(element), LINT_ERROR_MESSAGE) } } @tsmith
  85. Herald @tsmith | www.phacility.com/phabricator/herald

  86. "I call it my billion-dollar mistake. It was the invention

    of the null reference" - Sir Charles Antony Richard Hoare @tsmith | github.com/uber/NullAway
  87. Errorprone public class MyActivity extends Activity { @Override public void

    onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); AndroidInjection.inject(this); } } @tsmith | errorprone.info
  88. Nullaway A tool to help eliminate NullPointerExceptions in your Java

    code @tsmith | github.com/uber/NullAway
  89. Nullaway static void log(Object x) { System.out.println(x.toString()); } static void

    foo() { log(null); } @tsmith | github.com/uber/NullAway
  90. Nullaway warning: [NullAway] passing @Nullable parameter 'null' where @NonNull is

    required log(null); ^ @tsmith | github.com/uber/NullAway
  91. Nullaway static void log(@Nullable Object x) { if (x !=

    null) { System.out.println(x.toString()); } } @tsmith | github.com/uber/NullAway
  92. RAVE Runtime Annotation Validation Engine Ensure models adhere to the

    set of expectations that are described by their annotations @tsmith | github.com/uber/RAVE
  93. RAVE public final class SampleFactory implements ValidatorFactory { @NonNull @Override

    public BaseValidator generateValidator() { return new SampleFactory_Generated_Validator(); } } @tsmith | github.com/uber/RAVE
  94. RAVE @Validated(factory = SampleFactory.class) public class SimpleMatchModel { private String

    match; private static final String MATCHED = "Matched"; private static final String NOT_MATCHED = "NotMatched"; @StringDef({MATCHED, NOT_MATCHED}) @Retention(RetentionPolicy.SOURCE) @interface StringVals { } @NonNull @StringVals public String getMatch() { return match; } } @tsmith | github.com/uber/RAVE
  95. RAVE public void validateMyModel(SimpleModel myModel) { try { Rave.getInstance().validate(myModel); }

    catch (RaveException e) { // handle rave validation error. } } @tsmith
  96. @tsmith

  97. Thri!y https://github.com/Microsoft/thrifty Wire https://github.com/square/wire @tsmith

  98. @tsmith

  99. Uber Open Source github.com/uber/artist github.com/uber/autodispose github.com/uber/crumb github.com/uber/motif github.com/uber/nullaway github.com/uber/okbuck github.com/uber/rave

    github.com/uber/ribs github.com/uber/stylist @tsmith
  100. Resources Architecture rewrite - t.uber.com/ribs-1 Dependency hierarchies - t.uber.com/ribs-2 RIBs

    - t.uber.com/ribs-3 Plugins - t.uber.com/ribs-4 Monorepo - t.uber.com/monorepo-1 @tsmith
  101. Tools of the Trade Ty Smith @tsmith