Top Banner
Building robust apps: Lessons from functional programming and robotics
26

Building robust apps

Feb 09, 2017

Download

Documents

Charles Neveu
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Building robust apps

Building robust apps: Lessons from functional programming and robotics

Page 2: Building robust apps

Props to this guy

Page 3: Building robust apps

Also this guy

YOU ARE DOING IT COMPLETELY WRONG

The Language of the System on youtube

Page 4: Building robust apps

Match Android team❏ Dheeraj Malik❏ Aniruddh Bajirao❏ Abhilash Reddy❏ Aaron Dancygier❏ David Brady❏ Ramanand Reddi❏ Martin Anderson❏ Roshan Gupta❏ Mohit Bhatt❏ Wasim Ahmad❏ Charles Neveu

Page 5: Building robust apps

Legacy app

● Originally written by contractors● Grown by accretion● Behavior becoming non-deterministic● Adding new features becoming increasingly difficult● Whack-a-mole debugging cycle● Still crashing on users

Page 6: Building robust apps

Goals of the rewrite

● Eliminate crashes● Easy to add/modify/AB test functionality● Predictable behavior

Page 7: Building robust apps

Complications of Android Programming

Page 8: Building robust apps

Activity life-cycle❏ Activities can be visible, partially visible, or invisible

❏ UI operations can only be done by an activity when it is visible❏ Android apps are multi-threaded

❏ UI thread❏ Needs to be responsive: Everything that can be done on a

background thread should be❏ Background threads

❏ Place to do network routines, longish computations, etc.❏ UI operations can only be done on the UI thread❏ Network operations can never be done on UI thread

Page 9: Building robust apps

Crashing is not a good user experience❏ Any of these can cause an app to crash

❏ UI operations can only be done by an activity when it is visible❏ UI operations can only be done on the UI thread❏ Network operations can never be done on UI thread

❏ So just make sure that stuff runs on the right thread…❏ In general, no way to know what thread a piece of code will run on

❏ E.g. Activity class has runOnUIThread method but it takes a Runnable that can still call anything

❏ … And no activity does UI operations unless it is visible.❏ But visibility isn’t transactional.

Page 10: Building robust apps

It gets worse● Activities can be destroyed (in the lifecycle

sense) while things still hold pointers to them (in the GC sense)

● Plus all the usual multithreading issues● Activity-centric programming makes it worse

Page 11: Building robust apps

Activity-centric programming

● Is seeing the app as a set of Activities. In my experience, this is typically the way product managers see them.

● But an activity is just a container for and gateway to UI elements○ It’s how we gain access to textboxes, buttons, etc○ It changes state based on user input.

But it can be so much more!

Page 12: Building robust apps

Activity as God Object

Everybody’s listener!

Everybody’s thread manager!

Everybody’s Controller!

All kinds of state variables!

ENGULFS Business logic!

Tightly coupled to everything!

AND ONLY IT KNOWS WHERE TO GO NEXT!

Page 13: Building robust apps

Activities are tightly coupledclass ActivityA extends Activity {

if (x) startActivity(ActivityB);

if (y) startActivity(ActivityC);

if (z) startActivity(ActivityD);

Page 14: Building robust apps

An app looks something like this

Page 15: Building robust apps

When is a data structure not a data structure?

Page 16: Building robust apps

When is a data structure not a data structure?

When it is implicit in the code!

❏ Can’t analyze it❏ Can’t answer questions about it❏ Tightly coupled, violates need to know❏ Becomes nondeterministic

Page 17: Building robust apps

Sources of Complexity❏ Complexity that comes with Android

❏ Concurrency❏ Activity Lifecycle

❏ Problems we create for ourselves❏ God objects❏ Tight coupling❏ Structure is implicit in the code❏ No separate infrastructure

Page 18: Building robust apps

Out of the Tarpit*One plausible solution (not necessarily the optimal solution, or only solution, but steps in the right direction)

❏ Functional Dataflow architecture ❏ Publish/subscribe backbone (eventBus)❏ Pass immutable data instead of objects or control❏ Minimize internal state❏ Controllers are independent of activity lifecycle, i.e. always on❏ Transitions are handled by an explicit graph data structure

Out of the Tarpit, Ben Moseley and Peter Marks, Software Practice Advancement, 2006 http://shaffner.us/cs/papers/tarpit.pdf

Page 19: Building robust apps

Data processing structure

❏ Modules take inputs, produce outputs, with minimal internal state

❏ Activities ❏ Take user inputs, publish data outputs❏ Subscribe to data inputs, display output to user

❏ Controllers ❏ Subscribe to data, publish data❏ Independent of activity lifecycle

Page 20: Building robust apps

Publish/subscribe backbone (eventBus)❏ Pass immutable data

❏ Not objects, not control❏ Awkward in Java

❏ Completely decouples producers and consumers❏ Neither needs to know who (if anyone) is at the other end of the queue

❏ Avoids callback hell❏ Match app has no explicit callbacks except those required by library apis e.g. retrofit

❏ Concurrency through onEvent<thread> handlers❏ Match app has no AsyncTasks, no Runnables

❏ Activities only communicate over the bus❏ Activities register in onResume, unregister in onPause

❏ Mockless testing❏ Testing consists of sending and receiving messages

Page 21: Building robust apps

Reusable activities❏ Activities are usually one-off

❏ Tightly coupled to subsequent activities❏ Tightly coupled to backend calls❏ Tightly coupled to business logic

❏ Reusable activities ❏ Communicate via event bus

❏ Translate user input into data❏ post data to bus❏ Subscribe to updates

❏ Decoupled from subsequent activities❏ Flow information stored in separate graph data structure

❏ Decoupled from backend❏ No explicit api calls❏ No error handling

❏ Business logic resides in controller

Page 22: Building robust apps

Activity Flow Graph

❏ Graph data structure❏ Nodes (vertices) are activities❏ Links (edges) are directed transitions❏ Based on finite state machine data structure by Van

Gurp et al.*

*On the Implementation of Finite State Machines, Jilles Van Gurp & Jan Bosch, 3rd Annual IASTED International Conference Software Engineering and Applications October 6-8, 1999 - Scottsdale, Arizona, USA

Page 23: Building robust apps

A typical activity (DailyMatches) from our old app❏ Android lifecycle handlers❏ Fragment navigation❏ Intent handling/parsing/error-handling❏ Show/hide progress bar❏ Navigate to YoureInterested, TheyreInterested, MaybeInterested❏ Get counts AsyncTask

Page 24: Building robust apps

DailyMatches refactored❏ Android lifecycle handlers❏ fragment navigation❏ Intent handling/parsing/error-handling

❏ onEvent(DailyMatches); ❏ post(DailyMatchesRequest); ❏ Errors handled by separate common error manager

❏ show/hide progress bar ❏ Handled by whatever activity is on top

❏ navigate to YourInterested, TheyreInterested, MaybeInterested ❏ Explicit external graph

❏ get counts AsyncTask ❏ Handled by controller

Page 25: Building robust apps

Results● Completed on schedule● Number of crashes due to UI operations dropped to zero● A/B test development speed greatly increased● Improvement in all business metrics

Page 26: Building robust apps

Open issues❏ Superclass/subclass interactions

❏ If a superclass defines an event handler, every registered instance of every subclass will execute that handler (within the context of the instance’s state).

❏ Inline event handling vs. task handling.❏ In EventBus, if a handler can execute on the same thread, it is executed in-line. If handler A

posts an event handled by handler B, handler B may run before A completes.

❏ Sticky messages, or not? ❏ Sticky messages stay on the bus until replaced, and objects that subscribe to them will receive

them when they register on the bus. UI objects typically register in onResume, so a fragment’s event handler will be called every time it is resumes after dialog. Sticky messages can be removed, introducing order dependence.

❏ Multiple busses❏ We only used one bus. How does it affect development and maintenance to use multiple

busses?