Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Developing Beautiful, Measurable Software

Developing Beautiful, Measurable Software

Presented November 28, 2014 at SymfonyCon Madrid: https://joind.in/talk/view/12953

Presented October 9, 2014 at Symfony Live NYC: https://joind.in/talk/view/12195

Presented September 13, 2014 at Madison PHP: https://joind.in/talk/view/11738

Presented August 19, 2014 at ShorePHP: https://joind.in/talk/view/11659

Presented April 25, 2014 at Lone Star PHP: http://joind.in/talk/view/10808

Reveal.js presentation published at: http://jmikola.github.io/slides/beautiful_measurable_software/

Jeremy Mikola

November 28, 2014
Tweet

More Decks by Jeremy Mikola

Other Decks in Programming

Transcript

  1. DEVELOPING BEAUTIFUL,
    MEASURABLE SOFTWARE
    Jeremy Mikola
    jmikola

    View Slide

  2. Agenda
    Training
    Metrics
    Tools

    View Slide

  3. Training

    View Slide

  4. View Slide

  5. The core concepts behind good design are
    well understood. Cohesion, loose coupling,
    no redundancy, encapsulation, testability,
    readability, and focus. Yet it’s hard to put
    those concepts into practice.

    — Jeff Bay in Object Calisthenics

    View Slide

  6. Object Calisthenics
    Object-oriented programming

    View Slide

  7. Object Calisthenics
    /ˌkalәsˈTHeniks/ – noun
    gymnastic exercises to achieve bodily
    fitness and grace of movement.
    early 19th century: from Greek kallos (beauty) + sthenos (strength)

    View Slide

  8. These are exercises,
    not hard and fast rules.

    View Slide

  9. Only one level of
    indentation per method

    View Slide

  10. Only one level of indentation per method
    f
    u
    n
    c
    t
    i
    o
    n m
    i
    g
    r
    a
    t
    e
    S
    u
    p
    p
    l
    i
    e
    r
    s
    (
    \
    M
    o
    n
    g
    o
    C
    o
    l
    l
    e
    c
    t
    i
    o
    n $
    s
    u
    p
    p
    l
    i
    e
    r
    C
    o
    l
    l
    e
    c
    t
    i
    o
    n
    )
    {
    $
    t
    y
    p
    e
    M
    a
    p = [
    1 =
    > '
    b
    i
    l
    l
    i
    n
    g
    A
    d
    d
    r
    e
    s
    s
    '
    ,
    2 =
    > '
    r
    e
    t
    u
    r
    n
    s
    A
    d
    d
    r
    e
    s
    s
    '
    ,
    3 =
    > '
    s
    h
    i
    p
    p
    i
    n
    g
    A
    d
    d
    r
    e
    s
    s
    '
    ,
    ]
    ;
    f
    o
    r
    e
    a
    c
    h (
    $
    s
    u
    p
    p
    l
    i
    e
    r
    C
    o
    l
    l
    e
    c
    t
    i
    o
    n
    -
    >
    f
    i
    n
    d
    (
    )
    -
    >
    s
    n
    a
    p
    s
    h
    o
    t
    (
    ) a
    s $
    s
    u
    p
    p
    l
    i
    e
    r
    ) {
    $
    n
    e
    w
    O
    b
    j = [
    '
    $
    u
    n
    s
    e
    t
    ' =
    > [
    '
    a
    d
    d
    r
    e
    s
    s
    e
    s
    ' =
    > 1
    ]
    ]
    ;
    /
    / $
    s
    e
    t o
    t
    h
    e
    r f
    i
    e
    l
    d
    s

    i
    f (
    i
    s
    s
    e
    t
    (
    $
    s
    u
    p
    p
    l
    i
    e
    r
    [
    '
    a
    d
    d
    r
    e
    s
    s
    e
    s
    '
    ]
    )
    ) {
    f
    o
    r
    e
    a
    c
    h (
    $
    s
    u
    p
    p
    l
    i
    e
    r
    [
    '
    a
    d
    d
    r
    e
    s
    s
    e
    s
    '
    ] a
    s $
    a
    d
    d
    r
    e
    s
    s
    ) {
    $
    n
    e
    w
    O
    b
    j
    [
    '
    $
    s
    e
    t
    '
    ]
    [
    $
    t
    y
    p
    e
    M
    a
    p
    [
    $
    a
    d
    d
    r
    e
    s
    s
    [
    '
    s
    u
    b
    t
    y
    p
    e
    '
    ]
    ]
    ] = [
    '
    n
    a
    m
    e
    ' =
    > i
    s
    s
    e
    t
    (
    $
    a
    d
    d
    r
    e
    s
    s
    [
    '
    n
    a
    m
    e
    '
    ]
    ) ? $
    a
    d
    d
    r
    e
    s
    s
    [
    '
    n
    a
    m
    e
    '
    ] : n
    u
    l
    l
    ,
    '
    a
    d
    d
    r
    e
    s
    s
    1
    ' =
    > i
    s
    s
    e
    t
    (
    $
    a
    d
    d
    r
    e
    s
    s
    [
    '
    a
    d
    d
    r
    e
    s
    s
    1
    '
    ]
    ) ? $
    a
    d
    d
    r
    e
    s
    s
    [
    '
    a
    d
    d
    r
    e
    s
    s
    1
    '
    ] : n
    u
    l
    l
    ,
    '
    a
    d
    d
    r
    e
    s
    s
    2
    ' =
    > i
    s
    s
    e
    t
    (
    $
    a
    d
    d
    r
    e
    s
    s
    [
    '
    a
    d
    d
    r
    e
    s
    s
    2
    '
    ]
    ) ? $
    a
    d
    d
    r
    e
    s
    s
    [
    '
    a
    d
    d
    r
    e
    s
    s
    2
    '
    ] : n
    u
    l
    l
    ,
    '
    c
    i
    t
    y
    ' =
    > i
    s
    s
    e
    t
    (
    $
    a
    d
    d
    r
    e
    s
    s
    [
    '
    c
    i
    t
    y
    '
    ]
    ) ? $
    a
    d
    d
    r
    e
    s
    s
    [
    '
    c
    i
    t
    y
    '
    ] : n
    u
    l
    l
    ,
    '
    s
    t
    a
    t
    e
    ' =
    > i
    s
    s
    e
    t
    (
    $
    a
    d
    d
    r
    e
    s
    s
    [
    '
    s
    t
    a
    t
    e
    '
    ]
    ) ? $
    a
    d
    d
    r
    e
    s
    s
    [
    '
    s
    t
    a
    t
    e
    '
    ] : n
    u
    l
    l
    ,
    '
    z
    i
    p
    ' =
    > i
    s
    s
    e
    t
    (
    $
    a
    d
    d
    r
    e
    s
    s
    [
    '
    p
    o
    s
    t
    a
    l
    C
    o
    d
    e
    '
    ]
    ) ? $
    a
    d
    d
    r
    e
    s
    s
    [
    '
    p
    o
    s
    t
    a
    l
    C
    o
    d
    e
    '
    ] : n
    u
    l
    l
    ,
    ]
    ;
    }
    }
    $
    s
    u
    p
    p
    l
    i
    e
    r
    s
    -
    >
    u
    p
    d
    a
    t
    e
    (
    [
    '
    _
    i
    d
    ' =
    > $
    s
    u
    p
    p
    l
    i
    e
    r
    [
    '
    _
    i
    d
    '
    ]
    ]
    , $
    n
    e
    w
    O
    b
    j
    )
    ;
    }
    }

    View Slide

  11. Only one level of indentation per method
    Extract Method
    You have a code fragment that can be
    grouped together. Turn the fragment into a
    method whose name explains the purpose of
    the method.

    — Martin Fowler in Refactoring

    View Slide

  12. Only one level of indentation per method
    f
    u
    n
    c
    t
    i
    o
    n c
    r
    e
    a
    t
    e
    S
    u
    p
    p
    l
    i
    e
    r
    A
    d
    d
    r
    e
    s
    s
    S
    e
    t
    (
    $
    a
    d
    d
    r
    e
    s
    s
    e
    s
    )
    {
    $
    t
    y
    p
    e
    M
    a
    p = [
    1 =
    > '
    b
    i
    l
    l
    i
    n
    g
    A
    d
    d
    r
    e
    s
    s
    '
    ,
    2 =
    > '
    r
    e
    t
    u
    r
    n
    s
    A
    d
    d
    r
    e
    s
    s
    '
    ,
    3 =
    > '
    s
    h
    i
    p
    p
    i
    n
    g
    A
    d
    d
    r
    e
    s
    s
    '
    ,
    ]
    ;
    $
    s
    e
    t = [
    ]
    ;
    f
    o
    r
    e
    a
    c
    h (
    $
    a
    d
    d
    r
    e
    s
    s
    e
    s a
    s $
    a
    d
    d
    r
    e
    s
    s
    ) {
    $
    s
    e
    t
    [
    $
    t
    y
    p
    e
    M
    a
    p
    [
    $
    a
    d
    d
    r
    e
    s
    s
    [
    '
    s
    u
    b
    t
    y
    p
    e
    '
    ]
    ]
    ] = [
    '
    n
    a
    m
    e
    ' =
    > i
    s
    s
    e
    t
    (
    $
    a
    d
    d
    r
    e
    s
    s
    [
    '
    n
    a
    m
    e
    '
    ]
    ) ? $
    a
    d
    d
    r
    e
    s
    s
    [
    '
    n
    a
    m
    e
    '
    ] : n
    u
    l
    l
    ,
    '
    a
    d
    d
    r
    e
    s
    s
    1
    ' =
    > i
    s
    s
    e
    t
    (
    $
    a
    d
    d
    r
    e
    s
    s
    [
    '
    a
    d
    d
    r
    e
    s
    s
    1
    '
    ]
    ) ? $
    a
    d
    d
    r
    e
    s
    s
    [
    '
    a
    d
    d
    r
    e
    s
    s
    1
    '
    ] : n
    u
    l
    l
    ,
    '
    a
    d
    d
    r
    e
    s
    s
    2
    ' =
    > i
    s
    s
    e
    t
    (
    $
    a
    d
    d
    r
    e
    s
    s
    [
    '
    a
    d
    d
    r
    e
    s
    s
    2
    '
    ]
    ) ? $
    a
    d
    d
    r
    e
    s
    s
    [
    '
    a
    d
    d
    r
    e
    s
    s
    2
    '
    ] : n
    u
    l
    l
    ,
    '
    c
    i
    t
    y
    ' =
    > i
    s
    s
    e
    t
    (
    $
    a
    d
    d
    r
    e
    s
    s
    [
    '
    c
    i
    t
    y
    '
    ]
    ) ? $
    a
    d
    d
    r
    e
    s
    s
    [
    '
    c
    i
    t
    y
    '
    ] : n
    u
    l
    l
    ,
    '
    s
    t
    a
    t
    e
    ' =
    > i
    s
    s
    e
    t
    (
    $
    a
    d
    d
    r
    e
    s
    s
    [
    '
    s
    t
    a
    t
    e
    '
    ]
    ) ? $
    a
    d
    d
    r
    e
    s
    s
    [
    '
    s
    t
    a
    t
    e
    '
    ] : n
    u
    l
    l
    ,
    '
    z
    i
    p
    ' =
    > i
    s
    s
    e
    t
    (
    $
    a
    d
    d
    r
    e
    s
    s
    [
    '
    p
    o
    s
    t
    a
    l
    C
    o
    d
    e
    '
    ]
    ) ? $
    a
    d
    d
    r
    e
    s
    s
    [
    '
    p
    o
    s
    t
    a
    l
    C
    o
    d
    e
    '
    ] : n
    u
    l
    l
    ,
    ]
    ;
    }
    r
    e
    t
    u
    r
    n $
    s
    e
    t
    ;
    }

    View Slide

  13. Only one level of indentation per method
    f
    u
    n
    c
    t
    i
    o
    n m
    i
    g
    r
    a
    t
    e
    S
    u
    p
    p
    l
    i
    e
    r
    s
    (
    \
    M
    o
    n
    g
    o
    C
    o
    l
    l
    e
    c
    t
    i
    o
    n $
    s
    u
    p
    p
    l
    i
    e
    r
    C
    o
    l
    l
    e
    c
    t
    i
    o
    n
    )
    {
    f
    o
    r
    e
    a
    c
    h (
    $
    s
    u
    p
    p
    l
    i
    e
    r
    C
    o
    l
    l
    e
    c
    t
    i
    o
    n
    -
    >
    f
    i
    n
    d
    (
    )
    -
    >
    s
    n
    a
    p
    s
    h
    o
    t
    (
    ) a
    s $
    s
    u
    p
    p
    l
    i
    e
    r
    ) {
    $
    n
    e
    w
    O
    b
    j = [
    '
    $
    s
    e
    t
    ' =
    > [
    ]
    ,
    '
    $
    u
    n
    s
    e
    t
    ' =
    > [
    '
    a
    d
    d
    r
    e
    s
    s
    e
    s
    ' =
    > 1
    ]
    ,
    ]
    ;
    /
    / $
    s
    e
    t o
    t
    h
    e
    r f
    i
    e
    l
    d
    s

    i
    f (
    i
    s
    s
    e
    t
    (
    $
    s
    u
    p
    p
    l
    i
    e
    r
    [
    '
    a
    d
    d
    r
    e
    s
    s
    e
    s
    '
    ]
    )
    ) {
    $
    n
    e
    w
    O
    b
    j
    [
    '
    $
    s
    e
    t
    '
    ] = a
    r
    r
    a
    y
    _
    m
    e
    r
    g
    e
    (
    $
    n
    e
    w
    O
    b
    j
    [
    '
    $
    s
    e
    t
    '
    ]
    ,
    c
    r
    e
    a
    t
    e
    S
    u
    p
    p
    l
    i
    e
    r
    A
    d
    d
    r
    e
    s
    s
    S
    e
    t
    (
    $
    s
    u
    p
    p
    l
    i
    e
    r
    [
    '
    a
    d
    d
    r
    e
    s
    s
    e
    s
    '
    ]
    )
    )
    ;
    }
    $
    s
    u
    p
    p
    l
    i
    e
    r
    s
    -
    >
    u
    p
    d
    a
    t
    e
    (
    [
    '
    _
    i
    d
    ' =
    > $
    s
    u
    p
    p
    l
    i
    e
    r
    [
    '
    _
    i
    d
    '
    ]
    ]
    , $
    n
    e
    w
    O
    b
    j
    )
    ;
    }
    }

    View Slide

  14. Only one level of indentation per method
    f
    u
    n
    c
    t
    i
    o
    n m
    i
    g
    r
    a
    t
    e
    S
    u
    p
    p
    l
    i
    e
    r
    s
    (
    \
    M
    o
    n
    g
    o
    C
    o
    l
    l
    e
    c
    t
    i
    o
    n $
    s
    u
    p
    p
    l
    i
    e
    r
    C
    o
    l
    l
    e
    c
    t
    i
    o
    n
    )
    {
    $
    c
    u
    r
    s
    o
    r = $
    s
    u
    p
    p
    l
    i
    e
    r
    C
    o
    l
    l
    e
    c
    t
    i
    o
    n
    -
    >
    f
    i
    n
    d
    (
    [
    '
    $
    e
    x
    i
    s
    t
    s
    ' =
    > [
    '
    a
    d
    d
    r
    e
    s
    s
    e
    s
    ' =
    > 1
    ]
    ]
    )
    -
    >
    s
    n
    a
    p
    s
    h
    o
    t
    ;
    f
    o
    r
    e
    a
    c
    h (
    $
    c
    u
    r
    s
    o
    r a
    s $
    s
    u
    p
    p
    l
    i
    e
    r
    ) {
    $
    n
    e
    w
    O
    b
    j = [
    '
    $
    s
    e
    t
    ' =
    > [
    ]
    ,
    '
    $
    u
    n
    s
    e
    t
    ' =
    > [
    '
    a
    d
    d
    r
    e
    s
    s
    e
    s
    ' =
    > 1
    ]
    ,
    ]
    ;
    /
    / $
    s
    e
    t o
    t
    h
    e
    r f
    i
    e
    l
    d
    s

    $
    n
    e
    w
    O
    b
    j
    [
    '
    $
    s
    e
    t
    '
    ] = a
    r
    r
    a
    y
    _
    m
    e
    r
    g
    e
    (
    $
    n
    e
    w
    O
    b
    j
    [
    '
    $
    s
    e
    t
    '
    ]
    ,
    c
    r
    e
    a
    t
    e
    S
    u
    p
    p
    l
    i
    e
    r
    A
    d
    d
    r
    e
    s
    s
    S
    e
    t
    (
    $
    s
    u
    p
    p
    l
    i
    e
    r
    [
    '
    a
    d
    d
    r
    e
    s
    s
    e
    s
    '
    ]
    )
    )
    ;
    $
    s
    u
    p
    p
    l
    i
    e
    r
    s
    -
    >
    u
    p
    d
    a
    t
    e
    (
    [
    '
    _
    i
    d
    ' =
    > $
    s
    u
    p
    p
    l
    i
    e
    r
    [
    '
    _
    i
    d
    '
    ]
    ]
    , $
    n
    e
    w
    O
    b
    j
    )
    ;
    }
    }

    View Slide

  15. Avoid using e
    l
    s
    e

    View Slide

  16. Avoid using e
    l
    s
    e
    p
    u
    b
    l
    i
    c f
    u
    n
    c
    t
    i
    o
    n a
    d
    d
    P
    r
    o
    d
    u
    c
    t
    (
    $
    s
    l
    u
    g
    , R
    e
    q
    u
    e
    s
    t $
    r
    e
    q
    u
    e
    s
    t
    )
    {
    $
    p
    r
    o
    d
    u
    c
    t = $
    t
    h
    i
    s
    -
    >
    p
    r
    o
    d
    u
    c
    t
    R
    e
    p
    o
    s
    i
    t
    o
    r
    y
    -
    >
    f
    i
    n
    d
    O
    n
    e
    B
    y
    S
    l
    u
    g
    (
    $
    s
    l
    u
    g
    )
    ;
    i
    f (
    $
    p
    r
    o
    d
    u
    c
    t !
    =
    = n
    u
    l
    l
    ) {
    $
    f
    o
    r
    m = $
    t
    h
    i
    s
    -
    >
    f
    o
    r
    m
    F
    a
    c
    t
    o
    r
    y
    -
    >
    c
    r
    e
    a
    t
    e
    (
    '
    a
    d
    d
    _
    p
    r
    o
    d
    u
    c
    t
    '
    )
    ;
    $
    f
    o
    r
    m
    -
    >
    h
    a
    n
    d
    l
    e
    R
    e
    q
    u
    e
    s
    t
    (
    $
    r
    e
    q
    u
    e
    s
    t
    )
    ;
    i
    f (
    $
    f
    o
    r
    m
    -
    >
    i
    s
    V
    a
    l
    i
    d
    (
    )
    ) {
    i
    f (
    $
    r
    e
    q
    u
    e
    s
    t
    -
    >
    g
    e
    t
    (
    '
    w
    i
    s
    h
    l
    i
    s
    t
    '
    , f
    a
    l
    s
    e
    )
    ) {
    $
    c
    o
    n
    t
    r
    o
    l
    l
    e
    r = '
    w
    i
    s
    h
    l
    i
    s
    t
    .
    c
    o
    n
    t
    r
    o
    l
    l
    e
    r
    :
    a
    d
    d
    P
    r
    o
    d
    u
    c
    t
    '
    ;
    } e
    l
    s
    e {
    $
    c
    o
    n
    t
    r
    o
    l
    l
    e
    r = '
    c
    a
    r
    t
    .
    c
    o
    n
    t
    r
    o
    l
    l
    e
    r
    :
    a
    d
    d
    P
    r
    o
    d
    u
    c
    t
    '
    ;
    }
    r
    e
    t
    u
    r
    n $
    t
    h
    i
    s
    -
    >
    f
    o
    r
    w
    a
    r
    d
    (
    $
    c
    o
    n
    t
    r
    o
    l
    l
    e
    r
    , [
    '
    i
    d
    ' =
    > $
    p
    r
    o
    d
    u
    c
    t
    -
    >
    g
    e
    t
    I
    d
    (
    )
    ,
    ]
    )
    ;
    } e
    l
    s
    e {
    r
    e
    t
    u
    r
    n $
    t
    h
    i
    s
    -
    >
    r
    e
    n
    d
    e
    r
    (
    '
    p
    r
    o
    d
    u
    c
    t
    /
    a
    d
    d
    .
    h
    t
    m
    l
    .
    t
    w
    i
    g
    '
    , [
    '
    f
    o
    r
    m
    ' =
    > $
    f
    o
    r
    m
    -
    >
    c
    r
    e
    a
    t
    e
    V
    i
    e
    w
    (
    )
    ,
    '
    p
    r
    o
    d
    u
    c
    t
    ' =
    > $
    p
    r
    o
    d
    u
    c
    t
    ,
    ]
    )
    ;
    }
    } e
    l
    s
    e {
    t
    h
    r
    o
    w n
    e
    w N
    o
    t
    F
    o
    u
    n
    d
    H
    t
    t
    p
    E
    x
    c
    e
    p
    t
    i
    o
    n
    (
    )
    ;
    }
    }

    View Slide

  17. Avoid using e
    l
    s
    e
    p
    u
    b
    l
    i
    c f
    u
    n
    c
    t
    i
    o
    n a
    d
    d
    P
    r
    o
    d
    u
    c
    t
    (
    $
    s
    l
    u
    g
    , R
    e
    q
    u
    e
    s
    t $
    r
    e
    q
    u
    e
    s
    t
    )
    {
    $
    p
    r
    o
    d
    u
    c
    t = $
    t
    h
    i
    s
    -
    >
    p
    r
    o
    d
    u
    c
    t
    R
    e
    p
    o
    s
    i
    t
    o
    r
    y
    -
    >
    f
    i
    n
    d
    O
    n
    e
    B
    y
    S
    l
    u
    g
    (
    $
    s
    l
    u
    g
    )
    ;
    i
    f (
    $
    p
    r
    o
    d
    u
    c
    t =
    =
    = n
    u
    l
    l
    ) {
    t
    h
    r
    o
    w n
    e
    w N
    o
    t
    F
    o
    u
    n
    d
    H
    t
    t
    p
    E
    x
    c
    e
    p
    t
    i
    o
    n
    (
    )
    ;
    }
    $
    f
    o
    r
    m = $
    t
    h
    i
    s
    -
    >
    f
    o
    r
    m
    F
    a
    c
    t
    o
    r
    y
    -
    >
    c
    r
    e
    a
    t
    e
    (
    '
    a
    d
    d
    _
    p
    r
    o
    d
    u
    c
    t
    '
    )
    ;
    $
    f
    o
    r
    m
    -
    >
    h
    a
    n
    d
    l
    e
    R
    e
    q
    u
    e
    s
    t
    (
    $
    r
    e
    q
    u
    e
    s
    t
    )
    ;
    i
    f ( ! $
    f
    o
    r
    m
    -
    >
    i
    s
    V
    a
    l
    i
    d
    (
    )
    ) {
    r
    e
    t
    u
    r
    n $
    t
    h
    i
    s
    -
    >
    r
    e
    n
    d
    e
    r
    (
    '
    p
    r
    o
    d
    u
    c
    t
    /
    a
    d
    d
    .
    h
    t
    m
    l
    .
    t
    w
    i
    g
    '
    , [
    '
    f
    o
    r
    m
    ' =
    > $
    f
    o
    r
    m
    -
    >
    c
    r
    e
    a
    t
    e
    V
    i
    e
    w
    (
    )
    ,
    '
    p
    r
    o
    d
    u
    c
    t
    ' =
    > $
    p
    r
    o
    d
    u
    c
    t
    ,
    ]
    )
    ;
    }
    $
    c
    o
    n
    t
    r
    o
    l
    l
    e
    r = $
    r
    e
    q
    u
    e
    s
    t
    -
    >
    g
    e
    t
    (
    '
    w
    i
    s
    h
    l
    i
    s
    t
    '
    , f
    a
    l
    s
    e
    )
    ? '
    w
    i
    s
    h
    l
    i
    s
    t
    .
    c
    o
    n
    t
    r
    o
    l
    l
    e
    r
    :
    a
    d
    d
    P
    r
    o
    d
    u
    c
    t
    '
    : '
    c
    a
    r
    t
    .
    c
    o
    n
    t
    r
    o
    l
    l
    e
    r
    :
    a
    d
    d
    P
    r
    o
    d
    u
    c
    t
    '
    r
    e
    t
    u
    r
    n $
    t
    h
    i
    s
    -
    >
    f
    o
    r
    w
    a
    r
    d
    (
    $
    c
    o
    n
    t
    r
    o
    l
    l
    e
    r
    , [
    '
    i
    d
    ' =
    > $
    p
    r
    o
    d
    u
    c
    t
    -
    >
    g
    e
    t
    I
    d
    (
    )
    ,
    ]
    )
    ;
    }

    View Slide

  18. Wrap primitives and strings

    View Slide

  19. Wrap primitives and strings
    But PHP objects are expensive!
    Avoid
    (i.e. using primitives to represent domain ideas)
    primitive obsession
    Replace with
    (i.e. immutable entities with some behavior)
    value objects

    View Slide

  20. One dot per line
    T_OBJECT_OPERATOR

    View Slide

  21. One dot per line
    Law of Demeter
    (Principle of Least Knowledge)
    Only talk to friends
    Don’t circumvent encapsulation
    and are still OK
    Method chaining fluent APIs

    View Slide

  22. One dot per line
    $
    u
    s
    e
    r = $
    t
    h
    i
    s
    -
    >
    c
    o
    n
    t
    a
    i
    n
    e
    r
    -
    >
    g
    e
    t
    (
    '
    s
    e
    c
    u
    r
    i
    t
    y
    .
    c
    o
    n
    t
    e
    x
    t
    '
    )
    -
    >
    g
    e
    t
    T
    o
    k
    e
    n
    (
    )
    -
    >
    g
    e
    t
    U
    s
    e
    r
    (
    )
    ;
    $
    i
    m
    a
    g
    e = $
    u
    s
    e
    r
    -
    >
    g
    e
    t
    P
    r
    o
    f
    i
    l
    e
    (
    )
    -
    >
    g
    e
    t
    A
    v
    a
    t
    a
    r
    (
    )
    -
    >
    g
    e
    t
    U
    r
    l
    (
    )
    ;
    vs.
    $
    u
    s
    e
    r = $
    t
    h
    i
    s
    -
    >
    g
    e
    t
    U
    s
    e
    r
    (
    )
    ;
    $
    i
    m
    a
    g
    e = $
    u
    s
    e
    r
    -
    >
    g
    e
    t
    A
    v
    a
    t
    a
    r
    U
    r
    l
    (
    )
    ;

    View Slide

  23. Don’t Do not abbreviate

    View Slide

  24. Do not abbreviate
    Why do you want to abbreviate?
    Repeating a name too frequently
    hints at code duplication.
    Method names being too long
    hints at misplaced responsibility.

    View Slide

  25. Keep your entities small

    View Slide

  26. Keep your entities small
    <100 LOC per class
    10 methods per class
    15 classes per package
    The goal is readability and maintainability

    View Slide

  27. Limit classes to five
    member variables

    View Slide

  28. Limit classes to five member variables
    Decompose objects with many attributes
    into a hierarchy of collaborating objects
    Higher cohesion and better encapsulation

    View Slide

  29. First class collections

    View Slide

  30. First class collections
    If a class contains a collection,
    it should not be doing much else.
    Behaviors for the collection have a home
    (e.g. combining, filtering, mapping)

    View Slide

  31. First class collections
    Doctrine\Common\Collections\ArrayCollection
    _
    _
    c
    o
    n
    s
    t
    r
    u
    c
    t
    (
    a
    r
    r
    a
    y $
    e
    l
    e
    m
    e
    n
    t
    s
    )
    c
    o
    u
    n
    t
    (
    )
    g
    e
    t
    I
    t
    e
    r
    a
    t
    o
    r
    (
    )
    s
    l
    i
    c
    e
    (
    $
    o
    f
    f
    s
    e
    t
    , $
    l
    e
    n
    g
    t
    h
    )
    m
    a
    t
    c
    h
    i
    n
    g
    (
    C
    r
    i
    t
    e
    r
    i
    a $
    c
    r
    i
    t
    e
    r
    i
    a
    )
    e
    x
    i
    s
    t
    s
    (
    C
    l
    o
    s
    u
    r
    e $
    p
    )
    f
    i
    l
    t
    e
    r
    (
    C
    l
    o
    s
    u
    r
    e $
    p
    )
    f
    o
    r
    A
    l
    l
    (
    C
    l
    o
    s
    u
    r
    e $
    p
    )
    m
    a
    p
    (
    C
    l
    o
    s
    u
    r
    e $
    p
    )
    p
    a
    r
    t
    i
    t
    i
    o
    n
    (
    C
    l
    o
    s
    u
    r
    e $
    p
    )
    Collection interface extends
    Countable, IteratorAggregate, ArrayAccess

    View Slide

  32. No getters and setters
    for class properties

    View Slide

  33. No getters and setters for class properties
    Strong encapsulation boundaries force
    behaviors to be placed in the object model
    Decisions based on an object’s state
    should be made within that object

    View Slide

  34. No getters and setters for class properties
    Tell, don’t ask
    Procedural code gets information then
    makes decisions. Object-oriented code tells
    objects to do things.

    — Alec Sharp in Smalltalk by Example

    View Slide

  35. Use getters and setters for class properties
    Dependency injection
    Post-constructor (e.g. interface injection)
    Encapsulating transformations
    Decorator pattern
    (e.g. Doctrine Proxy objects)

    View Slide

  36. No static methods
    or utility classes

    View Slide

  37. No static methods or utility classes
    Classes without state have
    no identity, no raison d'être
    They manipulate data from other objects
    and frequently violate encapsulation

    View Slide

  38. Metrics

    View Slide

  39. View Slide

  40. What else can we measure?
    Source lines of code
    Code coverage
    Bug density
    Cohesion and coupling
    Cyclomatic complexity
    Function points
    Custom design constraints

    View Slide

  41. Cyclomatic Complexity

    View Slide

  42. Cyclomatic Complexity
    Number of decision points in a function
    (e.g. i
    f
    , w
    h
    i
    l
    e
    , f
    o
    r
    , c
    a
    s
    e
    )
    General indication of program complexity
    Spoiler alert: positive correlation with defects

    View Slide

  43. Cyclomatic Complexity
    p
    u
    b
    l
    i
    c f
    u
    n
    c
    t
    i
    o
    n f
    o
    o
    (
    $
    a
    , $
    b
    )
    {
    i
    f (
    $
    a
    ) {
    i
    f (
    $
    b
    ) {
    e
    c
    h
    o '
    a a
    n
    d b
    '
    ;
    } e
    l
    s
    e {
    e
    c
    h
    o '
    a
    '
    ;
    }
    } e
    l
    s
    e
    i
    f (
    $
    b
    ) {
    e
    c
    h
    o '
    b
    '
    ;
    } e
    l
    s
    e {
    e
    c
    h
    o '
    n
    e
    i
    t
    h
    e
    r
    '
    ;
    }
    }
    One entry point and three i
    f conditions: CC = 4

    View Slide

  44. Cyclomatic Complexity
    CC Complexity
    1–4 Low
    5–7 Moderate
    8–10 High
    11+ Very high

    View Slide

  45. NPath Complexity

    View Slide

  46. NPath Complexity
    Number of unique execution paths in a function
    Alternatively, the number of tests
    required for full branch coverage

    View Slide

  47. NPath Complexity
    p
    u
    b
    l
    i
    c f
    u
    n
    c
    t
    i
    o
    n f
    o
    o
    (
    $
    a
    , $
    b
    )
    {
    i
    f (
    $
    a
    ) {
    i
    f (
    $
    b
    ) {
    e
    c
    h
    o '
    a a
    n
    d b
    '
    ;
    } e
    l
    s
    e {
    e
    c
    h
    o '
    a
    '
    ;
    }
    } e
    l
    s
    e
    i
    f (
    $
    b
    ) {
    e
    c
    h
    o '
    b
    '
    ;
    } e
    l
    s
    e {
    e
    c
    h
    o '
    n
    e
    i
    t
    h
    e
    r
    '
    ;
    }
    }
    CC = 4 and NPath = 4

    View Slide

  48. NPath Complexity
    p
    u
    b
    l
    i
    c f
    u
    n
    c
    t
    i
    o
    n f
    o
    o
    (
    $
    a
    , $
    b
    , $
    c
    )
    {
    i
    f (
    $
    a
    ) {
    i
    f (
    $
    b
    ) {
    e
    c
    h
    o '
    a a
    n
    d b
    '
    ;
    } e
    l
    s
    e {
    e
    c
    h
    o '
    a
    '
    ;
    }
    } e
    l
    s
    e
    i
    f (
    $
    b
    ) {
    e
    c
    h
    o '
    b
    '
    ;
    } e
    l
    s
    e {
    e
    c
    h
    o '
    n
    e
    i
    t
    h
    e
    r
    '
    ;
    }
    i
    f (
    $
    c
    ) {
    e
    c
    h
    o '
    c
    '
    ;
    }
    }
    CC += 1 and NPath ×= 2

    View Slide

  49. NPath Complexity
    p
    u
    b
    l
    i
    c f
    u
    n
    c
    t
    i
    o
    n f
    o
    o
    (
    $
    a
    , $
    b
    , $
    c
    , $
    d
    )
    {
    i
    f (
    $
    a
    ) {
    e
    c
    h
    o '
    a
    '
    ;
    }
    i
    f (
    $
    b
    ) {
    e
    c
    h
    o '
    b
    '
    ;
    }
    i
    f (
    $
    c
    ) {
    e
    c
    h
    o '
    c
    '
    ;
    }
    i
    f (
    $
    d
    ) {
    e
    c
    h
    o '
    d
    '
    ;
    }
    }
    CC = 5 and NPath = 16!
    Worst case: NPath = 2(CC - 1)

    View Slide

  50. Tools

    View Slide

  51. PHP Quality Assurance Toolchain
    by Sebastian Bergmann
    PHPUnit
    PHPLOC
    PHP Copy/Paste Detector
    PHP Dead Code Detector
    Behat
    PHP_Depend
    PHP Mess Detector
    PHP_CodeSniffer

    View Slide

  52. by Sebastian Bergmann
    PHPLOC
    Measures and analyzes a project
    LOC stats per class, method
    Cyclomatic complexity
    Dependencies on methods, attributes
    Code structure

    View Slide

  53. by Greg Sherwood
    PHP_CodeSniffer
    $ p
    h
    p
    c
    s /
    p
    a
    t
    h
    /
    t
    o
    /
    c
    o
    d
    e
    /
    F
    I
    L
    E
    : /
    p
    a
    t
    h
    /
    t
    o
    /
    c
    o
    d
    e
    /
    e
    x
    a
    m
    p
    l
    e
    .
    p
    h
    p
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    F
    O
    U
    N
    D 4 E
    R
    R
    O
    R
    (
    S
    ) A
    F
    F
    E
    C
    T
    I
    N
    G 4 L
    I
    N
    E
    (
    S
    )
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    2 | E
    R
    R
    O
    R | M
    i
    s
    s
    i
    n
    g f
    i
    l
    e d
    o
    c c
    o
    m
    m
    e
    n
    t
    4
    7 | E
    R
    R
    O
    R | L
    i
    n
    e n
    o
    t i
    n
    d
    e
    n
    t
    e
    d c
    o
    r
    r
    e
    c
    t
    l
    y
    ; e
    x
    p
    e
    c
    t
    e
    d 4 s
    p
    a
    c
    e
    s b
    u
    t f
    o
    u
    n
    d 1
    5
    1 | E
    R
    R
    O
    R | M
    i
    s
    s
    i
    n
    g f
    u
    n
    c
    t
    i
    o
    n d
    o
    c c
    o
    m
    m
    e
    n
    t
    8
    8 | E
    R
    R
    O
    R | L
    i
    n
    e n
    o
    t i
    n
    d
    e
    n
    t
    e
    d c
    o
    r
    r
    e
    c
    t
    l
    y
    ; e
    x
    p
    e
    c
    t
    e
    d 9 s
    p
    a
    c
    e
    s b
    u
    t f
    o
    u
    n
    d 6
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    Profiles for PSR-1 and PSR-2 included
    Works well with PHP Coding Standards Fixer

    View Slide

  54. by Manuel Pichler
    PHP_Depend
    Code analysis and metrics
    NPath and cyclomatic complexity
    Weighted method count
    Coupling analysis
    Code rank
    Dozens of additional metrics

    View Slide

  55. by Manuel Pichler
    PHP_Depend
    Abstraction Instability Chart

    View Slide

  56. by Manuel Pichler
    PHP Mess Detector
    Frontend for PHP_Depend
    Code size, complexity
    Design (syntax, classes)
    Unused code
    Naming conventions

    View Slide

  57. by Manuel Pichler
    PHP Mess Detector

    View Slide

  58. by Johannes Schmitt
    PHP Analyzer
    Code analysis and fixing
    Type inference
    Suspicious, unused code
    Deprecated functionality
    Coupling and cohesion
    Framework-specific checks

    View Slide

  59. Continuous
    Integration

    View Slide

  60. Continuous Integration is practice where
    members of a team integrate their work
    frequently. Each integration is verified by an
    automated build (including test) to detect
    integration errors as quickly as possible.

    — Martin Fowler in Continuous Integration

    View Slide

  61. Continuous Integration
    by Sebastian Bergmann
    jenkins-php.org

    View Slide

  62. Continuous Integration
    Travis CI Scrutinizer CI

    View Slide

  63. by Johannes Schmitt
    Scrutinizer CI
    Automated code analysis
    Code rating
    Tracking changes/stability
    External code coverage
    Automated fixes
    Refactoring tips

    View Slide

  64. by Johannes Schmitt
    Scrutinizer CI

    View Slide

  65. A Final Thought

    View Slide

  66. There are only two types of code, code that
    delivers business value, and code that
    doesn’t. The cleanest code that doesn’t
    deliver value is still crap.

    — Anthony Ferrara in Beyond Clean Code

    View Slide

  67. Beyond Clean Code
    Good Business Value Poor Business Value
    Clean Code Excellent Bad
    Dirty Code Good Garbage

    View Slide

  68. THANKS!
    by Rafael Dohms
    by Guilherme Blanco
    by William Durand
    by Anthony Ferrara
    Your Code Sucks, Let’s Fix It!
    Object Calisthenics Applied to PHP
    Object Calisthenics
    Development by the Numbers

    View Slide

  69. Image Credits
    by
    http://xkcd.com/844/
    http://xkcd.com/292/
    http://imagery.pragprog.com/products/105/twa.jpg
    http://icons8.com http://www.visualpharm.com/

    View Slide