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

ADDC 2018 - Ty Smith: Building for Developers

ADDC 2018 - Ty Smith: Building for Developers

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 it's 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.

Watch the video: https://www.youtube.com/watch?v=1cuhWSZqDCM
More about the talk, authors & slides: https://addconf.com/2018/schedule/building-for-developers/
Read about the conference: https://addconf.com

Ty Smith

July 04, 2018

More Decks by Ty Smith

Other Decks in Programming


  1. Overview — 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 Emerging 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)) //<--Here's the magic. .subscribe(s -> ...) @tsmith | https://github.com/uber/AutoDispose
  5. 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
  6. Consistency public interface ScopeHandler { @CheckReturnValue <T> Function<Flowable<? extends T>,

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

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

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

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