$30 off During Our Annual Pro Sale. View Details »

Tools of the Trade #DCNYC18

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.

Ty Smith

August 27, 2018
Tweet

More Decks by Ty Smith

Other Decks in Programming

Transcript

  1. Tools
    of the Trade
    Ty Smith
    @tsmith

    View Slide

  2. @tsmith

    View Slide

  3. @tsmith

    View Slide

  4. @tsmith

    View Slide

  5. @tsmith

    View Slide

  6. @tsmith

    View Slide

  7. @tsmith

    View Slide

  8. @tsmith

    View Slide

  9. @tsmith

    View Slide

  10. @tsmith

    View Slide

  11. @tsmith

    View Slide

  12. @tsmith

    View Slide

  13. @tsmith

    View Slide

  14. @tsmith

    View Slide

  15. @tsmith

    View Slide

  16. @tsmith

    View Slide

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

    View Slide

  18. MONO
    REPO
    @tsmith

    View Slide

  19. Pain Points
    — Dependency Hell
    — Architectural Silos
    — Long Build times
    @tsmith

    View Slide

  20. ABI Breaking change
    @tsmith

    View Slide

  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

    View Slide

  22. Monorepo
    ~/Uber/Android
    |-- .git
    |-- build.gradle
    |-- apps
    | |-- rider
    | |-- driver
    | |-- eats
    |-- features
    | |-- partner-funnel
    | |-- single-sign-on
    |-- libraries
    | |-- storage
    | |-- networking
    | |-- analytics
    @tsmith

    View Slide

  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

    View Slide

  24. Buck
    @tsmith | buckbuild.com

    View Slide

  25. Buck
    — Output based
    — Highly Parellelizable
    — Fast!
    !
    — IDE Integration
    @tsmith

    View Slide

  26. Buck
    @tsmith

    View Slide

  27. OKBuck
    A gradle plugin that lets developers utilize the Buck
    build system on a gradle project.
    @tsmith | github.com/uber/okbuck

    View Slide

  28. OKBuck
    plugins {
    id 'com.uber.okbuck' version '0.36.0'
    }
    @tsmith | github.com/uber/okbuck

    View Slide

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

    View Slide

  30. Exopackage
    @tsmith | buckbuild.com/article/exopackage.html

    View Slide

  31. @tsmith

    View Slide

  32. Submit Queue
    @tsmith

    View Slide

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

    View Slide

  34. @tsmith

    View Slide

  35. 99.99%
    reliability of core flows
    Enable global rollback of core flows to a guaranteed
    working state
    @tsmith

    View Slide

  36. Support growth for
    years
    Narrow and decouple functionality as much as possible
    @tsmith

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  40. Make
    Magic
    Performance second to none, graceful degradation on
    low-end devices and networks
    @tsmith

    View Slide

  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

    View Slide

  42. @tsmith | github.com/uber/ribs

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  52. Motif
    A simple DI API for Android / Java
    @tsmith | github.com/uber/motif

    View Slide

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

    View Slide

  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

    View Slide

  55. @tsmith

    View Slide

  56. Crumb
    An annotation processor for breadcrumbing metadata
    across compilation boundaries
    @tsmith | github.com/uber/crumb

    View Slide

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

    View Slide

  58. RIBs
    Plugins
    class FooPluginFactory :
    PluginFactory {
    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

    View Slide

  59. RIBs
    Plugins
    class BarPluginPoint : PluginPoint() {
    override val internalPluginFactories = listOf(FooPluginFactory())
    }
    @tsmith | github.com/uber/crumb

    View Slide

  60. RIBs
    Plugins
    val barPluginPoint = BarPluginPoint(abFramework)
    val BarPlugin : BarPlugin? = pluginPoint.getPlugin(intent)
    barPlugin?.doWork()
    @tsmith | github.com/uber/crumb

    View Slide

  61. @tsmith

    View Slide

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

    View Slide

  63. Autodispose
    observable
    .doStuff()
    .as(autoDisposable(this))
    .subscribe { ... }
    @tsmith | github.com/uber/autodispose

    View Slide

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

    View Slide

  65. Autodispose
    Extras!
    — Kotlin extensions
    — RX lifecycle extensions
    — Architecture Components adapter
    — Plugins
    — Errorprone checker
    @tsmith | github.com/uber/autodispose

    View Slide

  66. @tsmith

    View Slide

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

    View Slide

  68. Workflow
    — Step
    — ActionableItem
    — Workflow
    @tsmith

    View Slide

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

    View Slide

  70. Workflow
    class SingleSignOnWorkflow :
    HelixRootWorkflow() {
    override fun getSteps(rootActionableItem : AppRootActionableItem,
    deeplink : SingleSignOnDeepLink ) :
    Step {
    return rootActionableItem
    .onStep(waitUntilSignedIn())
    .onStep(GoToMain())
    .onStep(showFullScreenRib(singleSignOnRib(deeplink)))
    }
    }
    @tsmith | github.com/uber/RIBs/tree/master/android/libraries/rib-workflow

    View Slide

  71. Workflow
    override fun waitUntilSignedIn() :
    Step {
    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

    View Slide

  72. @tsmith

    View Slide

  73. @tsmith

    View Slide

  74. @tsmith

    View Slide

  75. Artist
    An artist creates views. Artist is a Gradle plugin that
    codegens a base set of Android Views.
    @tsmith | github.com/uber/artist

    View Slide

  76. Artist
    — Stencil: The View Generator
    — Trait: Reusable functionality
    @tsmith | github.com/uber/artist

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  82. Stylist


    <br/><item name="textSizeSmall">12dp</item><br/><item name="textSizeMedium">16dp</item><br/><item name="textSizeLarge">20dp</item><br/>
    <br/><item name="textSizeSmall">12dp</item><br/><item name="textSizeMedium">16dp</item><br/><item name="textSizeLarge">20dp</item><br/>

    @tsmith | github.com/uber/stylist

    View Slide

  83. @tsmith

    View Slide

  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

    View Slide

  85. Herald
    @tsmith | www.phacility.com/phabricator/herald

    View Slide

  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

    View Slide

  87. Errorprone
    public class MyActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    AndroidInjection.inject(this);
    }
    }
    @tsmith | errorprone.info

    View Slide

  88. Nullaway
    A tool to help eliminate NullPointerExceptions in your
    Java code
    @tsmith | github.com/uber/NullAway

    View Slide

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

    View Slide

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

    View Slide

  91. Nullaway
    static void log(@Nullable Object x) {
    if (x != null) {
    System.out.println(x.toString());
    }
    }
    @tsmith | github.com/uber/NullAway

    View Slide

  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

    View Slide

  93. RAVE
    public final class SampleFactory implements ValidatorFactory {
    @NonNull
    @Override
    public BaseValidator generateValidator() {
    return new SampleFactory_Generated_Validator();
    }
    }
    @tsmith | github.com/uber/RAVE

    View Slide

  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

    View Slide

  95. RAVE
    public void validateMyModel(SimpleModel myModel) {
    try {
    Rave.getInstance().validate(myModel);
    } catch (RaveException e) {
    // handle rave validation error.
    }
    }
    @tsmith

    View Slide

  96. @tsmith

    View Slide

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

    View Slide

  98. @tsmith

    View Slide

  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

    View Slide

  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

    View Slide

  101. Tools
    of the Trade
    Ty Smith
    @tsmith

    View Slide