y u no craftsman

Post on 02-Dec-2014

1.855 Views

Category:

Technology

0 Downloads

Preview:

Click to see full reader

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.

TRANSCRIPT

Y U NO CRAFTSMANXavi Rigau & Paul Blundell

Who we are

@xrigau@blundell_apps

What is craftsmanship?

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

the quality of design and work shown in something

Coder vs Craftsman

“Make a chair”

We’re gonna talk about...

- Improving day to day craftsmanship

- Creating a positive working environment

- The final few steps to awesomeness

- Opinionated Bonus material

Improving day to day craftsmanship

Whiteboard ideation

Brainstorming sessions

Helping non dev team members

WTF??

Pair programming

Pairing tennis

Driver & Navigator

MistakesDriver

Would make

MistakesNavigator

Would make

ActualMistakes

Best coding practices

Code reviews

Selfies & Gifs

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

Caring about the CI

Static analysis reports

Testing is caring

CI Game

LOL

Using the latest tools

Be “agile” build features the way that makes sense

YAGNI

Overengineering

You Ain’t Gonna Need It

GSD

Pragmatism

Creating a positive working environment

Continuous communication

Morning standups

Daily news

Hack & Tells

Dojos

Zero walls office

2 keyboards 2 mice per desk

Standing desks

Remote working

Remote working

Hal9000/Jukebox music

CI Alarm

Xbox downtime

PUB!

Hire the best (for you)

The final steps to awesomeness

That extra 5%

Optimise & leave the main thread alone

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); }}

GPU Profiling

Show Overdraw

Polish the app

Animate all the things

User features

- Second screen / Chromecast

- Widget

- Wear

- Daydream

- LiveWallpaper

Behind the scenes

- Content provider

- Sync Adapter

- Deep linking / Web search deep linking

Listen for feedback

Measure the data

Measuring Tools

Splunk MINT (a.k.a. Bugsense)

Crashlytics

Follow the guidelines

What happens when you don’t follow the guidelines

Ensureyour app listing is legit

Ensureyour app content is legit

Debug screens

Gradle all the things

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"' }}

Versioning

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}"

OpinionatedBonus

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

Follow the examples - but not too closely

Patterns that work well for us

minSdkVersion 15

For more info:

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

index.html

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()); }}

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); }}

Harden! Do not crash out there

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; } }

Hexagonal architecture

cc. Alistair Cockburn

In summary

Questions?

@blundell_apps @xrigau

We are hiring

Liverpool, London, Berlin & New York

info@novoda.com

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

top related