Building for Developers - Droidcon Italy 2019

Building for Developers - Droidcon Italy 2019

Many teams are designed so that engineers can take an abstract view of the customer and focus on implementation. But when the customers are other engineers, that entire dynamic is thrown on its head.

In this talk, Ty will walk you through the best practices of building great products for developers, from team processes to the technical delivery.

Topics covered will include: Building customer personas and product strategies, Developer Experience research and analytics, customer empathy and support structures, API Design and usability, testing, code distribution, and creating great documentation.

7a3baf2e1158e358885cdf7e89b9aa55?s=128

Ty Smith

April 04, 2019
Tweet

Transcript

  1. DESIGNING FOR DEVELOPERS Ty Smith uber.github.io @tsmith

  2. Building for Developers Ty Smith uber.github.io @tsmith

  3. USER EXPERIENCE @tsmith

  4. USER DEVELOPER EXPERIENCE @tsmith

  5. Developer Experience (DX) - Overiew — Developer Product Research —

    Developer Usability Focuses — Library Quality Standards — Best Practices for Releases — Managing Communities @tsmith
  6. Product Research @tsmith

  7. Standard product workflow @tsmith

  8. Developer Tool workflow @tsmith

  9. People Need Focus @tsmith | https://twitter.com/marcos_placona/status/882487720597237760

  10. Expertise Bias @tsmith

  11. Product's role in a developer world @tsmith

  12. Finding the right job for your product @tsmith | https://hbr.org/2016/09/know-your-customers-jobs-to-be-done

  13. Finding the right job for your product @tsmith | https://hbr.org/2016/09/know-your-customers-jobs-to-be-done

  14. Finding the right job for your product Crash Reporting -Stability

    -Time to build features -Insight into instability @tsmith | CC-BY-2.0 - Bill Ward | https://hbr.org/2016/09/know-your-customers-jobs-to-be-done
  15. Developer Customers @tsmith

  16. Developer Demographics @tsmith | Search Engine Guide

  17. Developer Personas Name Role Company Frank CS Student University Betty

    Founder Seed Startup João Engineer New Market Series A Paul Product Manager Hypergrowth Series C Sharon Staff Engineer Public SV Company Hannah Consultant Enterprise Company @tsmith | https://wiki.mozilla.org/DeveloperExperience/Personas
  18. Developer Research — Watch them integrate — Dig into expectations

    — Survey @tsmith
  19. Success Objectives — Developer Productivity — Developer Happiness — Developer

    Trust @tsmith
  20. @tsmith

  21. Usability @tsmith | CC-BY-2.0 - Sherman Paggi

  22. Autodispose Automatic binding+disposal of RxJava 2 streams. @tsmith | https://github.com/uber/AutoDispose

  23. Easy to Integrate dependencies { implementation 'com.uber.autodispose:autodispose:x.y.z' } observable .doStuff()

    .as(autoDisposable(this)) .subscribe(s -> ...) @tsmith | https://github.com/uber/AutoDispose
  24. Easy to Integrate dependencies { implementation 'com.uber.autodispose:autodispose:x.y.z' } observable .doStuff()

    .as(autoDisposable(this)) .subscribe(s -> ...) @tsmith | https://github.com/uber/AutoDispose
  25. Concise observable .doStuff() .as(autoDisposable(this)) .subscribe(s -> ...) @tsmith | https://github.com/uber/AutoDispose

  26. Intuitive //-------------------Vanilla RXJava---------------- fun doStuff() { subcription = observable.doStuff().subscribe(s ->

    ...) } override fun onDestroy() = subscription.unsubscribe() //-------------------Autodispose------------------- observable.doStuff() .as(autoDisposable(this)) .subscribe(s -> ...) @tsmith | https://github.com/uber/AutoDispose
  27. Intuitive //-------------------Vanilla RXJava---------------- fun doStuff() { subcription = observable.doStuff().subscribe(s ->

    ...) } override fun onDestroy() = subscription.unsubscribe() //-------------------Autodispose------------------- observable.doStuff() .as(autoDisposable(this)) .subscribe(s -> ...) @tsmith | https://github.com/uber/AutoDispose
  28. Consistency public interface ScopeHandler { @CheckReturnValue Function<Flowable, FlowableSubscribeProxy> forFlowable(); @CheckReturnValue

    Function<Observable, ObservableSubscribeProxy> forObservable(); @CheckReturnValue Function<Maybe, MaybeSubscribeProxy> forMaybe(); @CheckReturnValue Function<Single, SingleSubscribeProxy> forSingle(); @CheckReturnValue Function<Completable, CompletableSubscribeProxy> forCompletable(); } @tsmith | https://github.com/uber/AutoDispose
  29. Composition public interface ObservableSubscribeProxy<T> { Disposable subscribe(); Disposable subscribe(Consumer<? super

    T> onNext); ... TestObserver<T> test(); ... } @tsmith | https://github.com/uber/AutoDispose
  30. Resilient and Defensive @Override public ObservableSubscribeProxy<T> apply(final Observable<T> upstream) {

    return new ObservableSubscribeProxy<T>() { @Override public Disposable subscribe() { return new AutoDisposeObservable<>(upstream, scope).subscribe(); } ... }; } @tsmith | https://github.com/uber/AutoDispose
  31. Flexibility public interface LifecycleScopeProvider<E> { Observable<E> lifecycle(); Function<E, E> correspondingEvents();

    E peekLifecycle(); } @tsmith | https://github.com/uber/AutoDispose
  32. Flexibility dependencies { implementation 'com.uber.autodispose:autodispose-android:x.y.z' implementation 'com.uber.autodispose:autodispose-android-archcomponents:x.y.z' implementation 'com.uber.autodispose:autodispose-kotlin:x.y.z' implementation

    'com.uber.autodispose:autodispose-rxlifecycle:x.y.z' } @tsmith | https://github.com/uber/AutoDispose
  33. Testability dependencies { implementation 'com.uber.autodispose:autodispose-android-archcomponents:x.y.z' testImplementation 'com.uber.autodispose:autodispose-android-archcomponents-test:x.y.z' } TestLifecycleOwner lifecycle

    = TestLifecycleOwner.create(); subject.as(autoDisposable(from(lifecycle))).subscribe(o); lifecycle.emit(Lifecycle.Event.ON_CREATE); ... lifecycle.emit(Lifecycle.Event.ON_DESTROY); subject.onNext(1); o.assertNoMoreEvents(); @tsmith | https://github.com/uber/AutoDispose
  34. Debugability public final class AutoDisposePlugins { public static void lockdown()

    { lockdown = true; } public static void setOutsideLifecycleHandler( @Nullable Consumer<? super OutsideLifecycleException> handler) { if (lockdown) { throw new IllegalStateException("Plugins can't be changed anymore"); } outsideLifecycleHandler = handler; } } @tsmith | https://github.com/uber/AutoDispose
  35. Documentation /** * Entry point for auto-disposing streams from a

    {@link ScopeProvider}. * <p> * Example usage: * <pre><code> * Observable.just(1) * .as(AutoDispose.<Integer>autoDisposable(scope)) * .subscribe(...) * </code></pre> * * @param provider the target scope provider * @param <T> the stream type. * @return an {@link AutoDisposeConverter} to transform with operators like * {@link Observable#as(ObservableConverter)} */ public static <T> AutoDisposeConverter<T> autoDisposable(final ScopeProvider provider) {...} @tsmith | https://github.com/uber/AutoDispose
  36. Example Code @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_main); // Using automatic disposal, this should determine that the correct time to // dispose is onDestroy (the opposite of onCreate). Observable.interval(1, TimeUnit.SECONDS) .doOnDispose((value) -> { Log.i(TAG, "Disposing subscription from onCreate()")) }) .as(autoDisposable(from(this))) .subscribe((value) -> { Log.i(TAG, "Started in onCreate(), running until onDestroy(): " + num); }); } @tsmith | https://github.com/uber/AutoDispose
  37. @tsmith

  38. Quality Primum non nocere @tsmith

  39. Quality Stability @tsmith

  40. Performance @tsmith

  41. Size @tsmith| source: https://www.youtube.com/watch?v=CVQ8cg_WIwY

  42. Data usage @tsmith

  43. Transitive Dependencies @tsmith

  44. @tsmith

  45. Releases @tsmith

  46. Semantic Versioning - X.Y.Z Decimal Use Example X - Major

    API Change Changed Public Method Name Y _ Minor Feature Added New Functionality Z - Patch Fix Fixed Crash @tsmith
  47. Changelogs @tsmith

  48. Deprecation — Be thoughtful about introducing new APIs — Respect

    semantic versioning — Mark methods as deprecated — Set expected timeline or version for removal — Incentivize updates @tsmith
  49. Deprecation /** * Extension that proxies to [Observable.as] + [AutoDispose.autoDisposable]

    */ @Deprecated( level = ERROR, message = "Replaced with autoDisposable() to match top level APIs.", replaceWith = ReplaceWith("autoDisposable(scope)", "com.uber.autodispose.kotlin.autoDisposable") ) @CheckReturnValue inline fun <T> Observable<T>.autoDisposeWith(scope: Maybe<*>): ObservableSubscribeProxy<T> = this.`as`(AutoDispose.autoDisposable(scope)) @tsmith | https://github.com/uber/AutoDispose
  50. Community @tsmith | CC-BY-4.0 - Billie Grace Ward | http://uk.droidcon.com/skillscasts/11019-open-source-and-building-communities

  51. Issues @tsmith

  52. Contributors @tsmith

  53. DX - Wrap up — Developer Product Research — Developer

    Usability Focuses — Library Quality Standards — Best practices for Releases — Managing Communities @tsmith
  54. BUILDING FOR DEVELOPERS Ty Smith uber.github.io @tsmith