76
Y U NO CRAFTSMAN Xavi Rigau & Paul Blundell

Y U NO CRAFTSMAN

Embed Size (px)

DESCRIPTION

Releasing a great app is more than having a unique idea. It takes teamwork, collaboration and the will to be the best. At Novoda we make awesomeness happen. This talk is about our process from coding dojos to group design and iterative sprint planning with our customers. We'll describe some of our best practices as well as some of the components that can make a good app great. This includes: - Day-to-day processes: pairing tennis, gif code reviews and toggling hidden features. - Work Environment: hack & tells, continuous communication & kicking ass at Tekken. - Releasing the app: polishing & quantifying can get you top of the class and not listening to Google can get you expelled. Finishing with some bonus Android coding tips and tricks and crazy AOSP anti-patterns.

Citation preview

Page 1: Y U NO CRAFTSMAN

Y U NO CRAFTSMANXavi Rigau & Paul Blundell

Page 2: Y U NO CRAFTSMAN

Who we are

@xrigau@blundell_apps

Page 3: Y U NO CRAFTSMAN

What is craftsmanship?

craftsmanshipˈkrɑːf(t)smənʃɪp/

the quality of design and work shown in something

Page 4: Y U NO CRAFTSMAN

Coder vs Craftsman

“Make a chair”

Page 5: Y U NO CRAFTSMAN

We’re gonna talk about...

- Improving day to day craftsmanship

- Creating a positive working environment

- The final few steps to awesomeness

- Opinionated Bonus material

Page 6: Y U NO CRAFTSMAN

Improving day to day craftsmanship

Page 7: Y U NO CRAFTSMAN

Whiteboard ideation

Page 8: Y U NO CRAFTSMAN

Brainstorming sessions

Page 9: Y U NO CRAFTSMAN

Helping non dev team members

WTF??

Page 10: Y U NO CRAFTSMAN

Pair programming

Page 11: Y U NO CRAFTSMAN

Pairing tennis

Page 12: Y U NO CRAFTSMAN

Driver & Navigator

MistakesDriver

Would make

MistakesNavigator

Would make

ActualMistakes

Page 13: Y U NO CRAFTSMAN

Best coding practices

Page 14: Y U NO CRAFTSMAN

Code reviews

Page 15: Y U NO CRAFTSMAN

Selfies & Gifs

https://github.com/thieman/github-selfies

Page 16: Y U NO CRAFTSMAN

Caring about the CI

Page 17: Y U NO CRAFTSMAN

Static analysis reports

Page 18: Y U NO CRAFTSMAN

Testing is caring

Page 19: Y U NO CRAFTSMAN

CI Game

LOL

Page 20: Y U NO CRAFTSMAN

Using the latest tools

Page 21: Y U NO CRAFTSMAN

Be “agile” build features the way that makes sense

Page 22: Y U NO CRAFTSMAN

YAGNI

Overengineering

You Ain’t Gonna Need It

GSD

Page 23: Y U NO CRAFTSMAN

Pragmatism

Page 24: Y U NO CRAFTSMAN

Creating a positive working environment

Page 25: Y U NO CRAFTSMAN

Continuous communication

Page 26: Y U NO CRAFTSMAN

Morning standups

Page 27: Y U NO CRAFTSMAN

Daily news

Page 28: Y U NO CRAFTSMAN

Hack & Tells

Page 29: Y U NO CRAFTSMAN

Dojos

Page 30: Y U NO CRAFTSMAN

Zero walls office

Page 31: Y U NO CRAFTSMAN

2 keyboards 2 mice per desk

Page 32: Y U NO CRAFTSMAN

Standing desks

Page 33: Y U NO CRAFTSMAN

Remote working

Page 34: Y U NO CRAFTSMAN

Remote working

Page 35: Y U NO CRAFTSMAN

Hal9000/Jukebox music

Page 36: Y U NO CRAFTSMAN

CI Alarm

Page 37: Y U NO CRAFTSMAN

Xbox downtime

Page 38: Y U NO CRAFTSMAN

PUB!

Page 39: Y U NO CRAFTSMAN

Hire the best (for you)

Page 40: Y U NO CRAFTSMAN

The final steps to awesomeness

Page 41: Y U NO CRAFTSMAN

That extra 5%

Page 42: Y U NO CRAFTSMAN

Optimise & leave the main thread alone

Page 43: Y U NO CRAFTSMAN

Strict Mode

private void initializeStrictMode() { if (BuildConfig.DEBUG) { ThreadPolicy threadPolicy = new ThreadPolicy.Builder() .detectAll() .penaltyLog() .penaltyDeath() .build();

StrictMode.setThreadPolicy(threadPolicy);

VmPolicy vmPolicy = new VmPolicy.Builder() .detectAll() .penaltyLog() .penaltyDeath() .build();

StrictMode.setVmPolicy(vmPolicy); }}

Page 44: Y U NO CRAFTSMAN

GPU Profiling

Page 45: Y U NO CRAFTSMAN

Show Overdraw

Page 46: Y U NO CRAFTSMAN

Polish the app

Page 47: Y U NO CRAFTSMAN

Animate all the things

Page 48: Y U NO CRAFTSMAN

User features

- Second screen / Chromecast

- Widget

- Wear

- Daydream

- LiveWallpaper

Page 49: Y U NO CRAFTSMAN

Behind the scenes

- Content provider

- Sync Adapter

- Deep linking / Web search deep linking

Page 50: Y U NO CRAFTSMAN

Listen for feedback

Page 51: Y U NO CRAFTSMAN

Measure the data

Page 52: Y U NO CRAFTSMAN

Measuring Tools

Splunk MINT (a.k.a. Bugsense)

Crashlytics

Page 53: Y U NO CRAFTSMAN

Follow the guidelines

Page 54: Y U NO CRAFTSMAN

What happens when you don’t follow the guidelines

Page 55: Y U NO CRAFTSMAN

Ensureyour app listing is legit

Page 56: Y U NO CRAFTSMAN

Ensureyour app content is legit

Page 57: Y U NO CRAFTSMAN

Debug screens

Page 58: Y U NO CRAFTSMAN

Gradle all the things

Page 59: Y U NO CRAFTSMAN

Build types

buildTypes { debug { versionName "${VERSION_NAME}_${GIT_SHA}" runProguard false signingConfig signingConfigs.debug buildConfigField "String", "BUGSENSE_KEY", 'BuildConfig.INVALID' buildConfigField "boolean", "AB_TEST", 'false' } qa { runProguard false signingConfig signingConfigs.debug buildConfigField "String", "BUGSENSE_KEY", '"disSecret"' } release { runProguard true signingConfig signingConfigs.release buildConfigField "String", "BUGSENSE_KEY", '"lolNotTellingYou"' }}

Page 60: Y U NO CRAFTSMAN

Versioning

Page 61: Y U NO CRAFTSMAN

Proper versioning

ext { GIT_SHA = gitSha() CI_BUILD_NUMBER = jenkinsBuildNumber() VERSION_CODE = 19101 // scheme: MINSDK-VERSION dd-ddd VERSION_NAME = "1.0.1"}

def jenkinsBuildNumber() { // Local builds will always trump jenkins return System.getenv().BUILD_NUMBER?.toInteger() ?: 9999}

def gitSha() { return 'git rev-parse --short HEAD'.execute().text.trim()}

// ...

versionCode "${VERSION_CODE}${CI_BUILD_NUMBER}" as IntegerversionName "${VERSION_NAME}_${GIT_SHA}"

Page 62: Y U NO CRAFTSMAN

OpinionatedBonus

Page 63: Y U NO CRAFTSMAN

The dark side of AOSP

try { mWallpaper = getCurrentWallpaperLocked(context);} catch (OutOfMemoryError e) { Log.w(TAG, "No memory load current wallpaper", e);}

try { BitmapFactory.Options options =new BitmapFactory.Options(); return BitmapFactory.decodeStream(is, null, options);} catch (OutOfMemoryError e) { Log.w(TAG, "Can't decode stream", e);}

https://android.googlesource.com in WallpaperManager.java

- line 263

Page 64: Y U NO CRAFTSMAN

Follow the examples - but not too closely

Page 65: Y U NO CRAFTSMAN

Patterns that work well for us

Page 66: Y U NO CRAFTSMAN

minSdkVersion 15

For more info:

https://developer.android.com/about/dashboards/

index.html

Page 67: Y U NO CRAFTSMAN

Activity lifecycle callbacks

public interface ActivityLifecycleCallbacks { void onActivityCreated(Activity activity, Bundle savedInstanceState); void onActivityStarted(Activity activity); void onActivityResumed(Activity activity);

void onActivityPaused(Activity activity); void onActivityStopped(Activity activity); void onActivityDestroyed(Activity activity);

void onActivitySaveInstanceState(Activity activity, Bundle outState);}

public class MyActivityLifecycleCallbacks implements Application.ActivityLifecycleCallbacks { … }

public class MyApplication extends Application { @Override public void onCreate() { super.onCreate(); registerActivityLifecycleCallbacks(new MyActivityLifecycleCallbacks()); }}

Page 68: Y U NO CRAFTSMAN

Harden! Do not crash out there

public class ReportingUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {

@Override public void uncaughtException(Thread thread, Throwable ex) { // Do necessary crash handling (fail gracefully)

crashlytics.reportCrash("user crash captured", ex); }

}

private void swallowExceptionsInRelease() { if (!BuildConfig.DEBUG) { Thread.UncaughtExceptionHandler handler = new ReportingUncaughtExceptionHandler(); Thread.currentThread().setUncaughtExceptionHandler(handler); }}

Page 69: Y U NO CRAFTSMAN

Harden! Do not crash out there

Page 70: Y U NO CRAFTSMAN

newInstance all the things!

public class WidgetImageLoader {

private final Retriever memoryRetriever; private final Retriever fileRetriever;

public static WidgetImageLoader newInstance(Context context) { Retriever memoryRetriever = MemoryRetriever.getInstance(); Retriever fileRetriever = FileRetriever.newInstance(context); return new WidgetImageLoader(memoryRetriever, fileRetriever); }

WidgetImageLoader(Retriever memoryRetriever, Retriever fileRetriever) { this.memoryRetriever = memoryRetriever; this.fileRetriever = fileRetriever; } }

Page 71: Y U NO CRAFTSMAN

Hexagonal architecture

cc. Alistair Cockburn

Page 72: Y U NO CRAFTSMAN

In summary

Page 73: Y U NO CRAFTSMAN
Page 74: Y U NO CRAFTSMAN

Questions?

@blundell_apps @xrigau

Page 75: Y U NO CRAFTSMAN

We are hiring

Liverpool, London, Berlin & New York

[email protected]

Page 76: Y U NO CRAFTSMAN

References & Attributionsnovoda.com

karenknowsbest.com

experttek.co.uk

memegenerator

giphy.com

cloudfront.net

wandisco.com

electronicproducts.com

http://alistair.cockburn.us

deadzebra.com

dribble.com Jovie Brett

smosh.com

Dilbert

developer.android.com

gradle.org

failauthority.com

hilariousgifs.com