Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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.

Ty Smith

April 04, 2019
Tweet

More Decks by Ty Smith

Other Decks in Programming

Transcript

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

    Developer Usability Focuses — Library Quality Standards — Best Practices for Releases — Managing Communities @tsmith
  2. 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
  3. 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
  4. 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
  5. 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
  6. 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
  7. 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
  8. 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
  9. Composition public interface ObservableSubscribeProxy<T> { Disposable subscribe(); Disposable subscribe(Consumer<? super

    T> onNext); ... TestObserver<T> test(); ... } @tsmith | https://github.com/uber/AutoDispose
  10. 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
  11. 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
  12. 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
  13. 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
  14. 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
  15. 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
  16. Deprecation — Be thoughtful about introducing new APIs — Respect

    semantic versioning — Mark methods as deprecated — Set expected timeline or version for removal — Incentivize updates @tsmith
  17. 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
  18. DX - Wrap up — Developer Product Research — Developer

    Usability Focuses — Library Quality Standards — Best practices for Releases — Managing Communities @tsmith