112_wrirting_easytochangecode

Embed Size (px)

Citation preview

  • 8/12/2019 112_wrirting_easytochangecode

    1/207

    These are confidential sessionsplease refrain from streaming, blogging, or taking pictures

    Your second-most important goal as a developer

    Session 112

    Ken KociendaPrincipal Engineer, iOS Apps and Frameworks

    1

  • 8/12/2019 112_wrirting_easytochangecode

    2/207

    This is a talk about writing code

    2

  • 8/12/2019 112_wrirting_easytochangecode

    3/207

    Easy to read

    learn

    understand

    maintain

    change

    3

  • 8/12/2019 112_wrirting_easytochangecode

    4/207

    Easy to change software

    4

  • 8/12/2019 112_wrirting_easytochangecode

    5/207

    Your second-most important goal

    5

  • 8/12/2019 112_wrirting_easytochangecode

    6/207

    What is your most important goal?

    6

  • 8/12/2019 112_wrirting_easytochangecode

    7/207

    Ship products!

    7

  • 8/12/2019 112_wrirting_easytochangecode

    8/207

    This is how we think at Apple

    8

  • 8/12/2019 112_wrirting_easytochangecode

    9/207

    Over 30 iOS releases since 2007

    9

  • 8/12/2019 112_wrirting_easytochangecode

    10/207

    Releases are complicated

    Legal

    Marketing

    Bug fixesNew app features

    Improving existing features

    Changing priorities

    Competition

    Tight schedules

    Too few people

    Testing

    New hardware

    New OS features

    Work with other companies

    Too many people

    App Store submission

    10

  • 8/12/2019 112_wrirting_easytochangecode

    11/207

    Help you make change easier

    11

  • 8/12/2019 112_wrirting_easytochangecode

    12/207

    You always change your software

    12

  • 8/12/2019 112_wrirting_easytochangecode

    13/207

    Bug fixes

    Adding new features

    Enhancing existing features

    Changing code someone else wrote

    Changing code you wrote six months ago

    13

  • 8/12/2019 112_wrirting_easytochangecode

    14/207

    General conventions

    14

  • 8/12/2019 112_wrirting_easytochangecode

    15/207

    Mac and iOS conventions

    15

  • 8/12/2019 112_wrirting_easytochangecode

    16/207

  • 8/12/2019 112_wrirting_easytochangecode

    17/207

    Style

    Stories

    Laziness

    Hygiene

    Notifications

    Optimization

    Dependencies

    Mixing

    Expectations

    Wrap up

    17

  • 8/12/2019 112_wrirting_easytochangecode

    18/207

  • 8/12/2019 112_wrirting_easytochangecode

    19/207

    Coding conventions

    19

  • 8/12/2019 112_wrirting_easytochangecode

    20/207

    Brace style for if-else

    Parenthesis Style

    Leading underscores

    Code indenting

    CapitalizationStyle (i.e. capitalization_style)

    20

  • 8/12/2019 112_wrirting_easytochangecode

    21/207

    Local consistency is important

    21

  • 8/12/2019 112_wrirting_easytochangecode

    22/207

    The beginnings of style

    22

  • 8/12/2019 112_wrirting_easytochangecode

    23/207

    Style goes deeper

    23

  • 8/12/2019 112_wrirting_easytochangecode

    24/207

    Matthew Arnold

    24

  • 8/12/2019 112_wrirting_easytochangecode

    25/207

    Clarity

    25

  • 8/12/2019 112_wrirting_easytochangecode

    26/207

    Clear writing is easier to understand

    26

  • 8/12/2019 112_wrirting_easytochangecode

    27/207

    Clear code is easier to change

    27

  • 8/12/2019 112_wrirting_easytochangecode

    28/207

    Elements of a clear coding style?

    28

  • 8/12/2019 112_wrirting_easytochangecode

    29/207

    !

    !

    Good names

    Common idioms

    29

  • 8/12/2019 112_wrirting_easytochangecode

    30/207

  • 8/12/2019 112_wrirting_easytochangecode

    31/207

    NSString *searchString = [self _searchString];BOOL searchStringIsNotEmpty = [searchString length] != 0;

    if (searchStringIsNotEmpty) { [self _findBanner]->findString(searchString,

    shouldBeep ? BeepOnFailure: DoNotBeepOnFailure);}

    31

  • 8/12/2019 112_wrirting_easytochangecode

    32/207

    NSString *searchString = [self _searchString];

    BOOL searchStringIsNotEmpty = [searchString length] != 0;

    if (searchStringIsNotEmpty) { [self _findBanner]->findString(searchString,

    shouldBeep ? YES: NO);}

    32

  • 8/12/2019 112_wrirting_easytochangecode

    33/207

  • 8/12/2019 112_wrirting_easytochangecode

    34/207

  • 8/12/2019 112_wrirting_easytochangecode

    35/207

    Hard to know what they mean

    - (void)stopMagnifying:(BOOL)animated;

    35

  • 8/12/2019 112_wrirting_easytochangecode

    36/207

    Hard to know what they mean

    - (void)stopMagnifyingAnimated:(BOOL)animated;

    36

  • 8/12/2019 112_wrirting_easytochangecode

    37/207

    Good names are descriptive

    37

  • 8/12/2019 112_wrirting_easytochangecode

    38/207

    !

    !

    Good names

    Common idioms

    38

  • 8/12/2019 112_wrirting_easytochangecode

    39/207

    !

    !

    Good names

    Common idioms

    39

  • 8/12/2019 112_wrirting_easytochangecode

    40/207

    Hard to know what they mean

    [_rightView setAlpha:![[_temporary text] length] ? 1.0 : 0.0];

    40

  • 8/12/2019 112_wrirting_easytochangecode

    41/207

    Count square brackets?

    41

  • 8/12/2019 112_wrirting_easytochangecode

    42/207

    Hard to know what they mean

    [_rightView setAlpha:![[_temporary text] length] ? 1.0 : 0.0];

    42

  • 8/12/2019 112_wrirting_easytochangecode

    43/207

    Be clear!

    BOOL textIsEmpty = [_temporary.text length] == 0;float alpha = textIsEmpty ? 1.0 : 0.0;[_rightView setAlpha:alpha];

    43

  • 8/12/2019 112_wrirting_easytochangecode

    44/207

    Read and understand quickly

    44

  • 8/12/2019 112_wrirting_easytochangecode

    45/207

    Design patterns

    45

  • 8/12/2019 112_wrirting_easytochangecode

    46/207

  • 8/12/2019 112_wrirting_easytochangecode

    47/207

    Patterns used in Apple frameworks

    MVC

    Target-action

    Delegation

    Autorelease

    View controller

    47

  • 8/12/2019 112_wrirting_easytochangecode

    48/207

    Idioms communicate at a high level

    48

  • 8/12/2019 112_wrirting_easytochangecode

    49/207

  • 8/12/2019 112_wrirting_easytochangecode

    50/207

    !

    !

    Good names

    Common idioms

    50

  • 8/12/2019 112_wrirting_easytochangecode

    51/207

    More than skin deep

    51

  • 8/12/2019 112_wrirting_easytochangecode

    52/207

    Now I understand

    52

  • 8/12/2019 112_wrirting_easytochangecode

    53/207

    Bug. Why?

    53

  • 8/12/2019 112_wrirting_easytochangecode

    54/207

    Did not anticipate

    Did not understand

    54

  • 8/12/2019 112_wrirting_easytochangecode

    55/207

    Debug

    55

  • 8/12/2019 112_wrirting_easytochangecode

    56/207

  • 8/12/2019 112_wrirting_easytochangecode

    57/207

    Step 1: Debugger

    57

  • 8/12/2019 112_wrirting_easytochangecode

    58/207

    What are you really looking for?

    58

  • 8/12/2019 112_wrirting_easytochangecode

    59/207

  • 8/12/2019 112_wrirting_easytochangecode

    60/207

    How could this bug happen?

    60

  • 8/12/2019 112_wrirting_easytochangecode

    61/207

    Brian Kernighan

    61

  • 8/12/2019 112_wrirting_easytochangecode

    62/207

    Debugging is understanding

    62

  • 8/12/2019 112_wrirting_easytochangecode

    63/207

    Debugging is not jiggling code

    63

  • 8/12/2019 112_wrirting_easytochangecode

    64/207

  • 8/12/2019 112_wrirting_easytochangecode

    65/207

    Why?

    [self foo];

    [self bar];

    65

  • 8/12/2019 112_wrirting_easytochangecode

    66/207

    Each bug fix should tell a story

    66

  • 8/12/2019 112_wrirting_easytochangecode

    67/207

    Investigate. Eureka!

    Tell someone before you code the fix

    67

  • 8/12/2019 112_wrirting_easytochangecode

    68/207

  • 8/12/2019 112_wrirting_easytochangecode

    69/207

    Write the story into your bug tracker

    69

  • 8/12/2019 112_wrirting_easytochangecode

    70/207

    Anticipate more

    Understand better

    70

  • 8/12/2019 112_wrirting_easytochangecode

    71/207

    Now I understand

    71

  • 8/12/2019 112_wrirting_easytochangecode

    72/207

    Wake me when it is over

    72

  • 8/12/2019 112_wrirting_easytochangecode

    73/207

    Lazy initialization

    73

  • 8/12/2019 112_wrirting_easytochangecode

    74/207

    It is good

    74

  • 8/12/2019 112_wrirting_easytochangecode

    75/207

    It is not magic

    75

  • 8/12/2019 112_wrirting_easytochangecode

    76/207

    Lazy initialization is common

    FooController *controller = [FooController sharedInstance];

    76

  • 8/12/2019 112_wrirting_easytochangecode

    77/207

    Lazy initialization is common

    @implementation FooController

    + (FooController *)sharedInstance

    { static dispatch_once_t once; static FooController *instance; dispatch_once(&once, ^{ instance = [[FooController alloc] init]; }); return instance;}

    @end

    77

  • 8/12/2019 112_wrirting_easytochangecode

    78/207

  • 8/12/2019 112_wrirting_easytochangecode

    79/207

    Lazy initialization is common

    @implementation BarController

    - (id)init

    { ... FooController*fooController = [FooController

    sharedInstance];...

    }

    @end

    79

  • 8/12/2019 112_wrirting_easytochangecode

    80/207

    Init storm

    80

  • 8/12/2019 112_wrirting_easytochangecode

    81/207

  • 8/12/2019 112_wrirting_easytochangecode

    82/207

    Long pause

    82

  • 8/12/2019 112_wrirting_easytochangecode

    83/207

    Order of initialization

    83

  • 8/12/2019 112_wrirting_easytochangecode

    84/207

    How many do you have?

    @implementation FooController

    + (FooController *)sharedInstance

    { static dispatch_once_t once; static FooController *instance; dispatch_once(&once, ^{ instance = [[FooController alloc] init]; }); return instance;}

    @end

    84

  • 8/12/2019 112_wrirting_easytochangecode

    85/207

    How many do you have?

    @implementation FooController

    + (FooController *)sharedInstance

    { static FooController *instance; if (!instance) instance = [[FooController alloc] init]; return instance;}

    @end

    85

  • 8/12/2019 112_wrirting_easytochangecode

    86/207

    Multiple instantiation of singleton

    86

  • 8/12/2019 112_wrirting_easytochangecode

    87/207

    Mess

    87

  • 8/12/2019 112_wrirting_easytochangecode

    88/207

    Think through lazy initialization

    88

  • 8/12/2019 112_wrirting_easytochangecode

    89/207

    No silver bullets

    89

  • 8/12/2019 112_wrirting_easytochangecode

    90/207

    Lightweight alloc at program start

    Better singleton decomposition

    90

  • 8/12/2019 112_wrirting_easytochangecode

    91/207

    Alternative accessor patterns

    91

  • 8/12/2019 112_wrirting_easytochangecode

    92/207

    Create or not?

    @interface FooController

    + (FooController *)sharedInstance; // will create

    + (FooController *)activeInstance; // wont create+ (FooController *)sharedInstance createIfNeeded:(BOOL)createIfNeeded;

    @end

    92

  • 8/12/2019 112_wrirting_easytochangecode

    93/207

    Wake me when it is over

    93

  • 8/12/2019 112_wrirting_easytochangecode

    94/207

    You make the mess you clean it up!

    94

  • 8/12/2019 112_wrirting_easytochangecode

    95/207

    Good hygiene takes effort

    95

  • 8/12/2019 112_wrirting_easytochangecode

    96/207

    E.B. White

    96

  • 8/12/2019 112_wrirting_easytochangecode

    97/207

    Do not throw away code

    97

  • 8/12/2019 112_wrirting_easytochangecode

    98/207

    Conflict?

    98

  • 8/12/2019 112_wrirting_easytochangecode

    99/207

    Changes are part of a process

    99

  • 8/12/2019 112_wrirting_easytochangecode

    100/207

    Your top priority should be to ship

    100

  • 8/12/2019 112_wrirting_easytochangecode

    101/207

    Do not rewrite refactor

    101

  • 8/12/2019 112_wrirting_easytochangecode

    102/207

    Refactoring

    Keep functionality, but change form

    102

  • 8/12/2019 112_wrirting_easytochangecode

    103/207

    What about cruft?

    103

  • 8/12/2019 112_wrirting_easytochangecode

    104/207

  • 8/12/2019 112_wrirting_easytochangecode

    105/207

    What is genuine cruft?

    105

  • 8/12/2019 112_wrirting_easytochangecode

    106/207

    Dead code

    Comments which no longer apply

    There is no number three

    106

  • 8/12/2019 112_wrirting_easytochangecode

    107/207

    Use compiler for dead code checks

    107

  • 8/12/2019 112_wrirting_easytochangecode

    108/207

    Delete or check old comments

    108

  • 8/12/2019 112_wrirting_easytochangecode

    109/207

  • 8/12/2019 112_wrirting_easytochangecode

    110/207

    Size of change is important

    110

  • 8/12/2019 112_wrirting_easytochangecode

    111/207

    Small: clean up as you go

    111

  • 8/12/2019 112_wrirting_easytochangecode

    112/207

  • 8/12/2019 112_wrirting_easytochangecode

    113/207

    Large: need real planning

    113

  • 8/12/2019 112_wrirting_easytochangecode

    114/207

  • 8/12/2019 112_wrirting_easytochangecode

    115/207

    Test!

    115

  • 8/12/2019 112_wrirting_easytochangecode

    116/207

    You make the mess you clean it up!

    116

  • 8/12/2019 112_wrirting_easytochangecode

    117/207

  • 8/12/2019 112_wrirting_easytochangecode

    118/207

    goto

    118

  • 8/12/2019 112_wrirting_easytochangecode

    119/207

    Notifications are a glorified goto

    119

  • 8/12/2019 112_wrirting_easytochangecode

    120/207

    You do not even say where to go!

    120

  • 8/12/2019 112_wrirting_easytochangecode

    121/207

    You can go to more than one place!

    121

  • 8/12/2019 112_wrirting_easytochangecode

    122/207

  • 8/12/2019 112_wrirting_easytochangecode

    123/207

    Non-deterministic behavior

    Callbacks are unordered

    123

  • 8/12/2019 112_wrirting_easytochangecode

    124/207

    Notifications can complicate change

    124

  • 8/12/2019 112_wrirting_easytochangecode

    125/207

    Not all bad

    125

  • 8/12/2019 112_wrirting_easytochangecode

    126/207

    Notifications promote loose coupling

    126

  • 8/12/2019 112_wrirting_easytochangecode

    127/207

    Model/View/Controller (MVC)

    CoreData

    127

  • 8/12/2019 112_wrirting_easytochangecode

    128/207

    will/did

    128

  • 8/12/2019 112_wrirting_easytochangecode

    129/207

    You should know about endpoints

    129

  • 8/12/2019 112_wrirting_easytochangecode

    130/207

    Think twice about other uses

    130

  • 8/12/2019 112_wrirting_easytochangecode

    131/207

    Code can be too loosely coupled

    131

  • 8/12/2019 112_wrirting_easytochangecode

    132/207

  • 8/12/2019 112_wrirting_easytochangecode

    133/207

    goto NextTopic;

    133

  • 8/12/2019 112_wrirting_easytochangecode

    134/207

    The 3% solution

    134

  • 8/12/2019 112_wrirting_easytochangecode

    135/207

  • 8/12/2019 112_wrirting_easytochangecode

    136/207

    97% of the time

    100% - 97% = 3%

    136

  • 8/12/2019 112_wrirting_easytochangecode

    137/207

    Which 3% to worry about?

    137

  • 8/12/2019 112_wrirting_easytochangecode

    138/207

    Memory allocation

    View creation

    Drawing

    Questionable algorithms Questionable data structures

    I/O

    Blocking on information

    Unnecessary work

    New work you just added

    138

  • 8/12/2019 112_wrirting_easytochangecode

    139/207

    Optimize when you have measured

    139

  • 8/12/2019 112_wrirting_easytochangecode

    140/207

  • 8/12/2019 112_wrirting_easytochangecode

    141/207

    Optimize when you understand

    141

  • 8/12/2019 112_wrirting_easytochangecode

    142/207

  • 8/12/2019 112_wrirting_easytochangecode

    143/207

    Optimize slowestand oldest3%

    143

  • 8/12/2019 112_wrirting_easytochangecode

    144/207

  • 8/12/2019 112_wrirting_easytochangecode

    145/207

  • 8/12/2019 112_wrirting_easytochangecode

    146/207

    Change, test, measure, optimize

    146

  • 8/12/2019 112_wrirting_easytochangecode

    147/207

    The 3% solution

    147

  • 8/12/2019 112_wrirting_easytochangecode

    148/207

  • 8/12/2019 112_wrirting_easytochangecode

    149/207

    Implications of change

    149

  • 8/12/2019 112_wrirting_easytochangecode

    150/207

  • 8/12/2019 112_wrirting_easytochangecode

    151/207

    !

    !

    Inheritance trees

    Call graphs

    151

  • 8/12/2019 112_wrirting_easytochangecode

    152/207

    !

    !

    Inheritance trees

    Call graphs

    152

  • 8/12/2019 112_wrirting_easytochangecode

    153/207

  • 8/12/2019 112_wrirting_easytochangecode

    154/207

    Avoid layers of overridden methods

    154

  • 8/12/2019 112_wrirting_easytochangecode

    155/207

    Use delegation

    155

  • 8/12/2019 112_wrirting_easytochangecode

    156/207

    Customize by calling another object

    Keeps conceptual overhead small

    Vary customization at runtime as needed

    156

  • 8/12/2019 112_wrirting_easytochangecode

    157/207

    !

    !

    Inheritance trees

    Call graphs

    157

  • 8/12/2019 112_wrirting_easytochangecode

    158/207

    Smaller is better

    158

  • 8/12/2019 112_wrirting_easytochangecode

    159/207

  • 8/12/2019 112_wrirting_easytochangecode

    160/207

  • 8/12/2019 112_wrirting_easytochangecode

    161/207

    Strive for unidirectional calling

    161

  • 8/12/2019 112_wrirting_easytochangecode

    162/207

    We are all friends here

    162

  • 8/12/2019 112_wrirting_easytochangecode

    163/207

    We are all friends here

    163

  • 8/12/2019 112_wrirting_easytochangecode

    164/207

    We are all friends here

    164

  • 8/12/2019 112_wrirting_easytochangecode

    165/207

  • 8/12/2019 112_wrirting_easytochangecode

    166/207

    Rethink relationship

    166

  • 8/12/2019 112_wrirting_easytochangecode

    167/207

    !

    !

    Inheritance trees

    Call graphs

    167

  • 8/12/2019 112_wrirting_easytochangecode

    168/207

  • 8/12/2019 112_wrirting_easytochangecode

    169/207

    Purity of essence (OPE)

    169

  • 8/12/2019 112_wrirting_easytochangecode

    170/207

    Model/View/Controller (MVC)

    170

  • 8/12/2019 112_wrirting_easytochangecode

    171/207

    Do not mix model and view changes

    171

  • 8/12/2019 112_wrirting_easytochangecode

    172/207

    Do not mix different things

    172

  • 8/12/2019 112_wrirting_easytochangecode

    173/207

  • 8/12/2019 112_wrirting_easytochangecode

    174/207

    Algorithms and data sources

    174

  • 8/12/2019 112_wrirting_easytochangecode

    175/207

  • 8/12/2019 112_wrirting_easytochangecode

    176/207

    UX and an interface paradigm

    176

  • 8/12/2019 112_wrirting_easytochangecode

    177/207

    Is this mixing too much?

    - (void)setEditing:(BOOL)editing animated:(BOOL)animated;

    177

  • 8/12/2019 112_wrirting_easytochangecode

    178/207

    Hard-code animations?

    178

  • 8/12/2019 112_wrirting_easytochangecode

    179/207

    Multitasking gestures

    179

  • 8/12/2019 112_wrirting_easytochangecode

    180/207

    We change how the system works

    180

  • 8/12/2019 112_wrirting_easytochangecode

    181/207

    App launch without animating hard

    181

  • 8/12/2019 112_wrirting_easytochangecode

    182/207

    Do not mix different things

    182

  • 8/12/2019 112_wrirting_easytochangecode

    183/207

    Purity of essence (OPE)

    183

  • 8/12/2019 112_wrirting_easytochangecode

    184/207

    How do I work this thing?

    184

  • 8/12/2019 112_wrirting_easytochangecode

    185/207

    Bugs are often disappointments

    185

  • 8/12/2019 112_wrirting_easytochangecode

    186/207

    I expected A, you did B

    186

  • 8/12/2019 112_wrirting_easytochangecode

    187/207

  • 8/12/2019 112_wrirting_easytochangecode

    188/207

    Hard to use wrong

    188

  • 8/12/2019 112_wrirting_easytochangecode

    189/207

    Method arguments

    Assertions and early returns

    189

  • 8/12/2019 112_wrirting_easytochangecode

    190/207

    This will never work

    // UIActionSheet.m

    - (void)showInView:(UIView *)view{ NSParameterAssert(view != nil);

    ...}

    190

  • 8/12/2019 112_wrirting_easytochangecode

    191/207

  • 8/12/2019 112_wrirting_easytochangecode

    192/207

    What about ivars?

    192

  • 8/12/2019 112_wrirting_easytochangecode

    193/207

    Global variables are bad, right?

    193

  • 8/12/2019 112_wrirting_easytochangecode

    194/207

    Scope is too broad

    194

  • 8/12/2019 112_wrirting_easytochangecode

    195/207

    ivar scope also can be too broad

    195

  • 8/12/2019 112_wrirting_easytochangecode

    196/207

    As few as possible

    Simple life-cycles

    Avoid tight relationships

    Avoid letting non-setter methods change ivars

    196

  • 8/12/2019 112_wrirting_easytochangecode

    197/207

    Hard to manage ivar state?

    197

  • 8/12/2019 112_wrirting_easytochangecode

    198/207

    Use a state machine

    198

  • 8/12/2019 112_wrirting_easytochangecode

    199/207

    UIGestureRecognizers

    199

  • 8/12/2019 112_wrirting_easytochangecode

    200/207

    Multitasking gestures

    200

  • 8/12/2019 112_wrirting_easytochangecode

    201/207

  • 8/12/2019 112_wrirting_easytochangecode

    202/207

    Hard to use wrong

    202

  • 8/12/2019 112_wrirting_easytochangecode

    203/207

    How do I work this thing?

    203

  • 8/12/2019 112_wrirting_easytochangecode

    204/207

    Ten things to think about

    204

  • 8/12/2019 112_wrirting_easytochangecode

    205/207

    Ten things to think about

  • 8/12/2019 112_wrirting_easytochangecode

    206/207

    Ten things to think about

    6. Keep new code easy to change

    7. Optimize slowest and oldest code

    8. Limit dependencies

    9. Do not mix different things

    10. Make code that is hard to use wrong

    206

  • 8/12/2019 112_wrirting_easytochangecode

    207/207