IT’S NOT ENOUGH FOR CODE TO WORK.
Robert C. Martin
Slide 4
Slide 4 text
@MOLSJEROEN
BUILDING A CONNECTED PRODUCT
Slide 5
Slide 5 text
@MOLSJEROEN
BUILDING A CONNECTED PRODUCT
Slide 6
Slide 6 text
@MOLSJEROEN
BUILDING A CONNECTED PRODUCT
Slide 7
Slide 7 text
@MOLSJEROEN
BUILDING A CONNECTED PRODUCT
Slide 8
Slide 8 text
@MOLSJEROEN
BUILDING A CONNECTED PRODUCT
Slide 9
Slide 9 text
@MOLSJEROEN
BUILDING A CONNECTED PRODUCT
Slide 10
Slide 10 text
@MOLSJEROEN
BUILDING A CONNECTED PRODUCT
Slide 11
Slide 11 text
@MOLSJEROEN
BUILDING A CONNECTED PRODUCT
Slide 12
Slide 12 text
@MOLSJEROEN
BUILDING A CONNECTED PRODUCT
Slide 13
Slide 13 text
@MOLSJEROEN
BUILDING A CONNECTED PRODUCT
?
Slide 14
Slide 14 text
@MOLSJEROEN
CHALLENGES
1. Easy to use (and test)
▸ Wi-Fi setup
▸ Prepare for bad weather
2. Performant
▸ Fast discovery
▸ Maximise control speed
3. Scalable
▸ Lessons learned
Slide 15
Slide 15 text
@MOLSJEROEN
CHALLENGES
1. Easy to use (and test)
▸ Wi-Fi setup
▸ Prepare for bad weather
2. Performant
▸ Fast discovery
▸ Maximise control speed
3. Scalable
▸ Lessons learned
@MOLSJEROEN
WI-FI SETUP - CHALLENGES
▸ Typing Wi-Fi passwords
▸ Network switching is fragile
▸ phone/tablet refuses to connect
▸ OS routes request over 4G
▸ Extremely slow feedback
▸ Users have to leave app on iOS
▸ Many bad weather scenarios
Slide 35
Slide 35 text
@MOLSJEROEN
WI-FI SETUP - CHALLENGES
▸ 1
Slide 36
Slide 36 text
IF WI-FI SETUP FAILS,
YOUR PRODUCT WILL BE RETURNED
Yours truly
Slide 37
Slide 37 text
@MOLSJEROEN
ANDROID 5.0+
▸ Behaviour change in Android 5.0
▸ Wi-Fi without internet
▸ Before: all requests sent over Wi-Fi
▸ After: all requests routed over 3G/4G
▸ Done for all, no subnet matching
Slide 38
Slide 38 text
@MOLSJEROEN
ANDROID 5.0+ - SINGLE REQUEST OVER WI-FI
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public void singleRequestOverWifi(Context context) {
NetworkRequest.Builder request = new NetworkRequest.Builder();
request.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
ConnectivityManager connMan = getConnectivityManager();
connMan.registerNetworkCallback(request.build(), new NetworkCallback(){
@Override
public void onAvailable(Network network) {
// Do network request
}
});
}
Slide 39
Slide 39 text
@MOLSJEROEN
ANDROID 5.0+ - SINGLE REQUEST OVER WI-FI
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public void singleRequestOverWifi(Context context) {
NetworkRequest.Builder request = new NetworkRequest.Builder();
request.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
ConnectivityManager connMan = getConnectivityManager();
connMan.registerNetworkCallback(request.build(), new NetworkCallback(){
@Override
public void onAvailable(Network network) {
// Do network request
}
});
}
Slide 40
Slide 40 text
@MOLSJEROEN
ANDROID 5.0+ - SINGLE REQUEST OVER WI-FI
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public void singleRequestOverWifi(Context context) {
NetworkRequest.Builder request = new NetworkRequest.Builder();
request.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
ConnectivityManager connMan = getConnectivityManager();
connMan.registerNetworkCallback(request.build(), new NetworkCallback(){
@Override
public void onAvailable(Network network) {
// Do network request
}
});
}
Slide 41
Slide 41 text
@MOLSJEROEN
ANDROID 5.0+ - SINGLE REQUEST OVER WI-FI
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public void singleRequestOverWifi(Context context) {
NetworkRequest.Builder request = new NetworkRequest.Builder();
request.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
ConnectivityManager connMan = getConnectivityManager();
connMan.registerNetworkCallback(request.build(), new NetworkCallback(){
@Override
public void onAvailable(Network network) {
// Do network request
}
});
}
Slide 42
Slide 42 text
@MOLSJEROEN
ANDROID 5.0+ - ALL REQUESTS OVER WI-FI
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public void allRequestsOverWifi(Context context) {
NetworkRequest.Builder request = new NetworkRequest.Builder();
request.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
final ConnectivityManager connMan = getConnectivityManager();
connMan.registerNetworkCallback(request.build(), new NetworkCallback() {
@Override
public void onAvailable(Network network) {
connMan.bindProcessToNetwork(network);
// Do network requests
} }); }
Slide 43
Slide 43 text
@MOLSJEROEN
ANDROID 5.0+ - ALL REQUESTS OVER WI-FI
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public void allRequestsOverWifi(Context context) {
NetworkRequest.Builder request = new NetworkRequest.Builder();
request.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
final ConnectivityManager connMan = getConnectivityManager();
connMan.registerNetworkCallback(request.build(), new NetworkCallback() {
@Override
public void onAvailable(Network network) {
connMan.bindProcessToNetwork(network);
// Do network requests
} }); }
Slide 44
Slide 44 text
@MOLSJEROEN
ALTERNATIVES
▸ One product as ethernet bridge
▸ WPS
▸ Other mechanisms for communication
▸ e.g High frequency sound, …
▸ Still need to enter Wi-Fi password
▸ WAC (Homekit devices)
▸ …
Slide 45
Slide 45 text
@MOLSJEROEN
#PROTIP - RANDOM SETUP NETWORK NAME
ssid
SETUP
ssid
SETUP
ssid
SETUP
ssid
SETUP
????????
Slide 46
Slide 46 text
@MOLSJEROEN
#PROTIP - RANDOM SETUP NETWORK NAME
Slide 47
Slide 47 text
PREPARE FOR BAD
WEATHER
PART 1: EASY TO USE (AND TEST)
Slide 48
Slide 48 text
@MOLSJEROEN
GOOD WEATHER SCENARIOS
Slide 49
Slide 49 text
@MOLSJEROEN
BAD WEATHER SCENARIOS
Slide 50
Slide 50 text
@MOLSJEROEN
BAD WEATHER SCENARIOS
Slide 51
Slide 51 text
@MOLSJEROEN
BAD WEATHER SCENARIOS
Slide 52
Slide 52 text
@MOLSJEROEN
INTERESTING SCENARIOS
▸ Bridge (almost) full
▸ Network request failed for reason X
▸ Resource doesn’t exist
▸ Connection lost
▸ Authentication lost
▸ Dynamic state changes
▸ ….
=> nearly impossible to reproduce manually
@MOLSJEROEN
#PROTIP - DON'T USE THE WORD “DEVICE”
▸ Use product or appliance
Slide 57
Slide 57 text
@MOLSJEROEN
CHALLENGES
1. Easy to use (and test)
▸ Wi-Fi setup
▸ Prepare for bad weather
2. Performant
▸ Fast discovery
▸ Maximise control speed
3. Scalable
▸ Lessons learned
Slide 58
Slide 58 text
DISCOVERY
PART 2: PERFORMANT
Slide 59
Slide 59 text
@MOLSJEROEN
LOCAL DISCOVERY
▸ Multicast based
▸ Often Unicast responses
▸ SSDP, mDNS,…
Slide 60
Slide 60 text
@MOLSJEROEN
LOCAL DISCOVERY
▸ Multicast based
▸ Often Unicast responses
▸ SSDP, mDNS,…
Slide 61
Slide 61 text
@MOLSJEROEN
LOCAL DISCOVERY
▸ Multicast based
▸ Often Unicast responses
▸ SSDP, mDNS,…
Slide 62
Slide 62 text
@MOLSJEROEN
LOCAL DISCOVERY
▸ Multicast based
▸ Often Unicast responses
▸ SSDP, mDNS,…
▸ Alternatives
▸ IP scan
▸ Manual IP
▸ Proprietary product IP server
Slide 63
Slide 63 text
@MOLSJEROEN
LOCAL DISCOVERY - IP SERVER
https://www.meethue.com/api/nupnp
[ {
"id":"001788fffe100491",
“internalipaddress":"192.168.2.23"
}, {
"id":"001788fffe09a168",
“internalipaddress":"192.168.88.252"
} ]
Slide 64
Slide 64 text
@MOLSJEROEN
REMOTE DISCOVERY
▸ Web service
▸ product announces
▸ interface to get products
▸ Security
▸ Local setup first required
Slide 65
Slide 65 text
@MOLSJEROEN
OPTIMISING DISCOVERY
Slide 66
Slide 66 text
@MOLSJEROEN
OPTIMISING DISCOVERY
1. Discover local and remote
2. Connect local and remote
▸ ~5 - 10 seconds
▸ Empty UI when no products found
Slide 67
Slide 67 text
@MOLSJEROEN
OPTIMISING DISCOVERY - REMEMBER PRODUCTS
1. Products from database
2. Discover local and remote
3. Connect local and remote
▸ ~5 - 10 seconds
▸ Disconnected device when not found
▸ Need for deleting devices
Slide 68
Slide 68 text
@MOLSJEROEN
OPTIMISING DISCOVERY - REMEMBER STATE
1. Products & last state from database
2. Discover local and remote
3. Connect local and remote
4. Update UI with actual state
▸ ~3 - 4 seconds
▸ UI instantly ready
▸ Control possible after discovery
Slide 69
Slide 69 text
@MOLSJEROEN
OPTIMISING DISCOVERY - REMEMBER CONNECTION INFO
1. Products, last state & connection info from database
2. Attempt local and remote connection
3. Fallback discovery
4. Update UI with actual state
▸ ~0 seconds
▸ UI & Control instantly ready
▸ Remember IP address and last SSID
Slide 70
Slide 70 text
@MOLSJEROEN
#PROTIP - SIMPLIFY DEVELOPMENT BY LIMITING STATE TRANSITIONS
▸ Transition local to remote connection always via disconnected
Slide 71
Slide 71 text
MAXIMISE CONTROL
SPEED
PART 2: PERFORMANT
Slide 72
Slide 72 text
@MOLSJEROEN
CONTROL SPEED
1. Always prefer local connection
2. Optimise local connection
3. Reduce amount of requests
4. Instant UI
▸ Implicit cost savings
▸ Remote connection usually not critical
Slide 73
Slide 73 text
@MOLSJEROEN
1. PREFER LOCAL CONNECTION
▸ Inherently faster
▸ Cost benefit
▸ Force connection
@MOLSJEROEN
3. REDUCE # REQUESTS
private UpdateRequest updateRequest = new UpdateRequest() {
@Override
public String getEndpoint() {
return "/lights";
}
@Override
public Map getAttributes() {
synchronized (pendingAttr) {
Map copy = new HashMap<>(pendingAttr);
pendingAttr.clear();
return copy;
}
}};
Slide 83
Slide 83 text
@MOLSJEROEN
3. REDUCE # REQUESTS
private UpdateRequest updateRequest = new UpdateRequest() {
@Override
public String getEndpoint() {
return "/lights";
}
@Override
public Map getAttributes() {
synchronized (pendingAttr) {
Map copy = new HashMap<>(pendingAttr);
pendingAttr.clear();
return copy;
}
}};
Slide 84
Slide 84 text
@MOLSJEROEN
3. REDUCE # REQUESTS
private UpdateRequest updateRequest = new UpdateRequest() {
@Override
public String getEndpoint() {
return "/lights";
}
@Override
public Map getAttributes() {
synchronized (pendingAttr) {
Map copy = new HashMap<>(pendingAttr);
pendingAttr.clear();
return copy;
}
}};
Slide 85
Slide 85 text
@MOLSJEROEN
3. REDUCE # REQUESTS
private class CommunicationStrategy {
private Set queue = new LinkedHashSet<>();
public void doRequest(UpdateRequest updateRequest) {
queue.add(updateRequest);
// logic to trigger requests
}
}
Slide 86
Slide 86 text
@MOLSJEROEN
3. REDUCE # REQUESTS
private class CommunicationStrategy {
private Set queue = new LinkedHashSet<>();
public void doRequest(UpdateRequest updateRequest) {
queue.add(updateRequest);
// logic to trigger requests
}
}
Slide 87
Slide 87 text
@MOLSJEROEN
3. REDUCE # REQUESTS
private class CommunicationStrategy {
private Set queue = new LinkedHashSet<>();
public void doRequest(UpdateRequest updateRequest) {
queue.add(updateRequest);
// logic to trigger requests
}
}
Slide 88
Slide 88 text
@MOLSJEROEN
4. INSTANT UI
Slide 89
Slide 89 text
@MOLSJEROEN
#PROTIP - NEVER ASSUME, ALWAYS MEASURE
Slide 90
Slide 90 text
@MOLSJEROEN
CHALLENGES
1. Easy to use (and test)
▸ Wi-Fi setup
▸ Prepare for bad weather
2. Performant
▸ Fast discovery
▸ Maximise control speed
3. Scalable
▸ Lessons learned
Slide 91
Slide 91 text
LESSONS LEARNED
PART 3: SCALABLE
Slide 92
Slide 92 text
@MOLSJEROEN
DEVELOPING A SYSTEM
0
0.5
1
1.5
2
2.5
3
3.5
4
4.5
1 2 3 4 5 6 7 8 9 10 11 12
New Features
Maintenance
Slide 93
Slide 93 text
@MOLSJEROEN
DEVELOPING A SYSTEM
0
0.5
1
1.5
2
2.5
3
3.5
4
4.5
1 2 3 4 5 6 7 8 9 10 11 12
New Features
Maintenance
Slide 94
Slide 94 text
@MOLSJEROEN
DEVELOPING A SYSTEM
0
0.5
1
1.5
2
2.5
3
3.5
4
4.5
1 2 3 4 5 6 7 8 9 10 11 12
New Features
Maintenance
Slide 95
Slide 95 text
@MOLSJEROEN
DEVELOPING A SYSTEM
0
0.5
1
1.5
2
2.5
3
3.5
4
4.5
1 2 3 4 5 6 7 8 9 10 11 12
New Features
Maintenance
Slide 96
Slide 96 text
@MOLSJEROEN
DEVELOPING A SYSTEM
0
0.5
1
1.5
2
2.5
3
3.5
4
4.5
1 2 3 4 5 6 7 8 9 10 11 12
New Features
Maintenance
Slide 97
Slide 97 text
@MOLSJEROEN
DEVELOPING A SYSTEM
0
0.5
1
1.5
2
2.5
3
3.5
4
4.5
1 2 3 4 5 6 7 8 9 10 11 12
New Features
Maintenance
Slide 98
Slide 98 text
THE TRUE COST OF SOFTWARE IS IN ITS
MAINTENANCE
unknown
Slide 99
Slide 99 text
@MOLSJEROEN
MAINTENANCE
▸ Big focus on automated testing
▸ Need for good architecture
▸ Over-engineering has a huge cost
▸ Avoid Hype driven development
▸ Think well before introducing new features
Slide 100
Slide 100 text
@MOLSJEROEN
#FAILUREHALLOFFAME
▸ Detect working internet connection (Wifi, no internet)
▸ Instrumentation tests
▸ Lock orientation to portrait
▸ Nested fragments
▸ Broadcasts as callbacks
▸ Post( new Runnable(…) )
▸ Null checks
▸ ….
RELATED BLOGPOSTS
Slide 101
Slide 101 text
WRAP UP
Slide 102
Slide 102 text
IN SOFTWARE DESIGN, OFTEN THE
CONSEQUENCES OF YOUR DECISIONS
DON'T BECOME APPARENT FOR YEARS
Kent Beck
Slide 103
Slide 103 text
@MOLSJEROEN
CONCLUSION
▸ Developing connected products is hard
▸ Developing systems is even harder
▸ Maximise local connections
▸ Keep things simple for users
▸ Strong focus on automated testing
▸ Proper architecture
Slide 104
Slide 104 text
@MOLSJEROEN
IMAGE CREDITS
• Welcome image http://www.store.meethue.com/media/catalog/product/cache/1/
feature_img_7/9df78eab33525d08d6e5fb8d27136e95/e/x/expand_your_ecosystem_1.jpg
• Wi-fi setup https://images.philips.com/is/image/PhilipsConsumer/AW3000_10-MI1-global-001?
$jpglarge$&wid=1250
• Good vs bad weather https://images.philips.com/is/image/PhilipsConsumer/FC8830_82-U1P-global-001?
$jpglarge$&wid=1250
• Discovery https://s.blogcdn.com/slideshows/images/slides/403/398/9/S4033989/slug/l/philips-hue-motion-
sensor-wall-attached-1.jpg
• Control speed http://www.philips.com/consumerfiles/newscenter/main/shared/assets/de/Downloadablefile/
press/elektro_hausgeraete/20140716_Philips_Air_AC4072_11_Lifestyle_4.jpg
• Developing a system http://www.philips.com/consumerfiles/newscenter/main/standard/resources/corporate/
press/2014/IFA2014/Saeco-GranBaristo-Avanti_image-5.jpg
• Warp up http://www.philips.com/consumerfiles/newscenter/main/standard/resources/corporate/press/2014/
IFA2014/Hue-Beyond-lifestyle_all-three.jpg