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

Agile Database Development

Agile Database Development

Hi, I'm David. I like to write database apps. Just as much as I like to write web apps. (Maybe more!) How? Not by relying on bolted-on, half-baked database integration tools like migrations, I'll tell you that!. Instead, I make extensive use of best-of-breed tools for source control ([Git](http://git-scm.org/)), database unit testing ([pgTAP](http://pgtap.org/)), and database change management and deployment ([Sqitch](http://sqitch.org/). If you'd like to get as much pleasure out of database development as you do application development, join me for this tutorial. We'll develop a sample application using the processes and tools I've come to depend on, and you'll find out whether they might work for you. Either way, I promise it will at least be an amusing use of your time.

[Originally presented at PGCon 2013](http://www.pgcon.org/2013/schedule/events/615.en.html).

David E. Wheeler

May 22, 2013
Tweet

More Decks by David E. Wheeler

Other Decks in Technology

Transcript

  1. Agile
    Database Development
    David E. Wheeler
    PGCon 2013
    Ottawa
    Text: Attribution-Noncommercial-Share Alike 3.0 United States:
    http:/
    /creativecommons.org/licenses/by-nc-sa/3.0/us/
    Images licensed independently and © Their respective owners.
    iovation
    1
    Wednesday, May 22, 13

    View full-size slide

  2. Agile
    Database Development
    David E. Wheeler
    PGCon 2013
    Ottawa
    Text: Attribution-Noncommercial-Share Alike 3.0 United States:
    http:/
    /creativecommons.org/licenses/by-nc-sa/3.0/us/
    Images licensed independently and © Their respective owners.

    iovation
    1
    Wednesday, May 22, 13

    View full-size slide

  3. David E. Wheeler
    PGCon 2013
    Ottawa
    Text: Attribution-Noncommercial-Share Alike 3.0 United States:
    http:/
    /creativecommons.org/licenses/by-nc-sa/3.0/us/
    Images licensed independently and © Their respective owners.
    1
    Wednesday, May 22, 13

    View full-size slide

  4. David E. Wheeler
    PGCon 2013
    Ottawa
    License: Attribution-Noncommercial-Share Alike 3.0 United
    States: http:/
    /creativecommons.org/licenses/by-nc-sa/3.0/us/
    2
    Wednesday, May 22, 13

    View full-size slide

  5. Build My VC-Funded App
    for Me for Free
    David E. Wheeler
    PGCon 2013
    Ottawa
    License: Attribution-Noncommercial-Share Alike 3.0 United
    States: http:/
    /creativecommons.org/licenses/by-nc-sa/3.0/us/

    CEO, Data Architect
    Agile Assholes, Inc.
    2
    Wednesday, May 22, 13

    View full-size slide

  6. This is Genius
    3
    Wednesday, May 22, 13

    View full-size slide

  7. This is Genius
    I had this idea
    3
    Wednesday, May 22, 13

    View full-size slide

  8. This is Genius
    I had this idea
    Social networking is HAWT
    3
    Wednesday, May 22, 13

    View full-size slide

  9. This is Genius
    I had this idea
    Social networking is HAWT
    Has been for waaaay too long
    3
    Wednesday, May 22, 13

    View full-size slide

  10. This is Genius
    I had this idea
    Social networking is HAWT
    Has been for waaaay too long
    The backlash is long overdue
    3
    Wednesday, May 22, 13

    View full-size slide

  11. This is Genius
    I had this idea
    Social networking is HAWT
    Has been for waaaay too long
    The backlash is long overdue
    Getting ahead of the curve
    3
    Wednesday, May 22, 13

    View full-size slide

  12. This is Genius
    I had this idea
    Social networking is HAWT
    Has been for waaaay too long
    The backlash is long overdue
    Getting ahead of the curve
    Introducing…
    3
    Wednesday, May 22, 13

    View full-size slide

  13. http:/
    /flic.kr/p/8j5gG8 © 2010 Strongrrl. All rights reserved. Used with permission.
    4
    Wednesday, May 22, 13

    View full-size slide

  14. antisocial network
    http:/
    /flic.kr/p/8j5gG8 © 2010 Strongrrl. All rights reserved. Used with permission.
    4
    Wednesday, May 22, 13

    View full-size slide

  15. How it Works
    antisocial network
    5
    Wednesday, May 22, 13

    View full-size slide

  16. How it Works
    Microblogging platform
    antisocial network
    5
    Wednesday, May 22, 13

    View full-size slide

  17. How it Works
    Microblogging platform
    Everyone follows you
    antisocial network
    5
    Wednesday, May 22, 13

    View full-size slide

  18. How it Works
    Microblogging platform
    Everyone follows you
    New users follow everyone
    antisocial network
    5
    Wednesday, May 22, 13

    View full-size slide

  19. How it Works
    Microblogging platform
    Everyone follows you
    New users follow everyone
    Goal: Alienate your followers
    antisocial network
    5
    Wednesday, May 22, 13

    View full-size slide

  20. How it Works
    Microblogging platform
    Everyone follows you
    New users follow everyone
    Goal: Alienate your followers
    Get them to unfollow you
    antisocial network
    5
    Wednesday, May 22, 13

    View full-size slide

  21. How it Works
    Microblogging platform
    Everyone follows you
    New users follow everyone
    Goal: Alienate your followers
    Get them to unfollow you
    Leaderboard: Those with fewest followers
    antisocial network
    5
    Wednesday, May 22, 13

    View full-size slide

  22. Your Task
    antisocial network
    6
    Wednesday, May 22, 13

    View full-size slide

  23. Your Task
    Create the database
    antisocial network
    6
    Wednesday, May 22, 13

    View full-size slide

  24. Your Task
    Create the database
    Use agile development to make it right
    antisocial network
    6
    Wednesday, May 22, 13

    View full-size slide

  25. Your Task
    Create the database
    Use agile development to make it right
    Contribute to VC-funded Corp
    antisocial network
    6
    Wednesday, May 22, 13

    View full-size slide

  26. Your Task
    Create the database
    Use agile development to make it right
    Contribute to VC-funded Corp
    Profit!
    antisocial network
    6
    Wednesday, May 22, 13

    View full-size slide

  27. Your Task
    Create the database
    Use agile development to make it right
    Contribute to VC-funded Corp
    Profit!
    For VC-funded Corp
    antisocial network
    6
    Wednesday, May 22, 13

    View full-size slide

  28. http:/
    /flic.kr/p/2honiQ © 2007 James Duncan Davidson. All rights reserved. Used with permission.
    7
    Wednesday, May 22, 13

    View full-size slide

  29. antisocial network
    8
    Wednesday, May 22, 13

    View full-size slide

  30. Agile
    WTF?
    antisocial network
    8
    Wednesday, May 22, 13

    View full-size slide

  31. We’ll get there
    antisocial network
    9
    Wednesday, May 22, 13

    View full-size slide

  32. But first…
    antisocial network
    10
    Wednesday, May 22, 13

    View full-size slide

  33. NDA
    antisocial network
    11
    Wednesday, May 22, 13

    View full-size slide

  34. Okay, now that that’s out
    of the way…
    antisocial network
    12
    Wednesday, May 22, 13

    View full-size slide

  35. Please organize into pairs
    antisocial network
    13
    Wednesday, May 22, 13

    View full-size slide

  36. Yes, that’s right
    antisocial network
    14
    Wednesday, May 22, 13

    View full-size slide

  37. You’re gonna do pair
    database programming
    antisocial network
    15
    Wednesday, May 22, 13

    View full-size slide

  38. App developers partner
    with DBAs
    antisocial network
    16
    Wednesday, May 22, 13

    View full-size slide

  39. I’m waiting…
    antisocial network
    17
    Wednesday, May 22, 13

    View full-size slide

  40. Job Requirements
    antisocial network
    18
    Wednesday, May 22, 13

    View full-size slide

  41. Job Requirements
    PostgreSQL
    http:/
    /postgresql.org/download
    antisocial network
    18
    Wednesday, May 22, 13

    View full-size slide

  42. Job Requirements
    PostgreSQL
    http:/
    /postgresql.org/download
    Git
    http:/
    /git-scm.com/
    antisocial network
    18
    Wednesday, May 22, 13

    View full-size slide

  43. Job Requirements
    PostgreSQL
    http:/
    /postgresql.org/download
    Git
    http:/
    /git-scm.com/
    Sqitch
    http:/
    /sqitch.org/
    antisocial network
    18
    Wednesday, May 22, 13

    View full-size slide

  44. Job Requirements
    PostgreSQL
    http:/
    /postgresql.org/download
    Git
    http:/
    /git-scm.com/
    Sqitch
    http:/
    /sqitch.org/
    pgTAP
    http:/
    /pgtap.org/
    antisocial network
    18
    Wednesday, May 22, 13

    View full-size slide

  45. Job Requirements
    PostgreSQL
    http:/
    /postgresql.org/download
    Git
    http:/
    /git-scm.com/
    Sqitch
    http:/
    /sqitch.org/
    pgTAP
    http:/
    /pgtap.org/
    pg_prove
    http:/
    /metacpan.org/module/pg_prove
    antisocial network
    18
    Wednesday, May 22, 13

    View full-size slide

  46. We good?
    antisocial network
    19
    Wednesday, May 22, 13

    View full-size slide

  47. Let’s do this thang.
    antisocial network
    20
    Wednesday, May 22, 13

    View full-size slide

  48. >
    Who Am I?
    21
    Wednesday, May 22, 13

    View full-size slide

  49. ""git"config"**global"user.name"'David"E."Wheeler'
    >
    >
    Who Am I?
    21
    Wednesday, May 22, 13

    View full-size slide

  50. ""git"config"**global"user.name"'David"E."Wheeler'
    >
    >
    Who Am I?
    21
    Wednesday, May 22, 13

    View full-size slide

  51. ""git"config"**global"user.name"'David"E."Wheeler'
    >
    ""git"config"**global"user.email"[email protected]
    >
    >
    Who Am I?
    21
    Wednesday, May 22, 13

    View full-size slide

  52. ""git"config"**global"user.name"'David"E."Wheeler'
    >
    ""git"config"**global"user.email"[email protected]
    >
    >
    Who Am I?
    21
    Wednesday, May 22, 13

    View full-size slide

  53. ""git"config"**global"user.name"'David"E."Wheeler'
    >
    ""git"config"**global"user.email"[email protected]
    >
    >
    Who Am I?
    Please use
    your own name
    and email.
    21
    Wednesday, May 22, 13

    View full-size slide

  54. ""git"config"**global"user.name"'David"E."Wheeler'
    >
    ""git"config"**global"user.email"[email protected]
    >
    >
    Who Am I?
    ""emacs"~/.gitconfig
    >
    21
    Wednesday, May 22, 13

    View full-size slide

  55. ~/.gitconfig
    ~/
    .gitconfig
    [user]
    "" name"="David"E."Wheeler
    """ email"="[email protected]
    22
    Wednesday, May 22, 13

    View full-size slide

  56. ~/.gitconfig
    ~/
    .gitconfig
    [user]
    "" name"="David"E."Wheeler
    """ email"="[email protected]
    Good for
    all projects
    22
    Wednesday, May 22, 13

    View full-size slide

  57. Create a Remote
    23
    Wednesday, May 22, 13

    View full-size slide

  58. Create a Remote
    Create Git project on GitHub
    23
    Wednesday, May 22, 13

    View full-size slide

  59. Create a Remote
    Create Git project on GitHub
    Or BitBucket
    23
    Wednesday, May 22, 13

    View full-size slide

  60. Create a Remote
    Create Git project on GitHub
    Or BitBucket
    Wherever you like
    23
    Wednesday, May 22, 13

    View full-size slide

  61. Create a Remote
    Create Git project on GitHub
    Or BitBucket
    Wherever you like
    Record remote URL
    23
    Wednesday, May 22, 13

    View full-size slide

  62. Create a Remote
    Create Git project on GitHub
    Or BitBucket
    Wherever you like
    Record remote URL
    23
    Wednesday, May 22, 13

    View full-size slide

  63. Or a Local Remote
    >
    24
    Wednesday, May 22, 13

    View full-size slide

  64. Or a Local Remote
    ""mkdir"flipr*remote
    >"cd"flipr*remote
    >
    >
    24
    Wednesday, May 22, 13

    View full-size slide

  65. Or a Local Remote
    ""mkdir"flipr*remote
    >"cd"flipr*remote
    >
    ""git"init"**bare
    Initialized"empty"Git"repository"in"flipr*remote/
    >
    >
    24
    Wednesday, May 22, 13

    View full-size slide

  66. Or a Local Remote
    ""mkdir"flipr*remote
    >"cd"flipr*remote
    >
    ""git"init"**bare
    Initialized"empty"Git"repository"in"flipr*remote/
    >
    >
    Remote URL is
    simply the path to
    this directory.
    24
    Wednesday, May 22, 13

    View full-size slide

  67. Or a Local Remote
    ""mkdir"flipr*remote
    >"cd"flipr*remote
    >
    ""git"init"**bare
    Initialized"empty"Git"repository"in"flipr*remote/
    >
    >
    ""cd"..
    >
    24
    Wednesday, May 22, 13

    View full-size slide

  68. Gitiup
    >
    25
    Wednesday, May 22, 13

    View full-size slide

  69. ""mkdir"flipr*db
    >"cd"flipr*db
    >"git"init
    Initialized"empty"Git"repository"in"flipr*db/.git/
    >
    Gitiup
    >
    25
    Wednesday, May 22, 13

    View full-size slide

  70. ""mkdir"flipr*db
    >"cd"flipr*db
    >"git"init
    Initialized"empty"Git"repository"in"flipr*db/.git/
    >
    Gitiup
    >
    25
    Wednesday, May 22, 13

    View full-size slide

  71. ""mkdir"flipr*db
    >"cd"flipr*db
    >"git"init
    Initialized"empty"Git"repository"in"flipr*db/.git/
    >
    Gitiup
    >
    ""echo"Flipr"Database"Project">"README.md
    >
    25
    Wednesday, May 22, 13

    View full-size slide

  72. ""mkdir"flipr*db
    >"cd"flipr*db
    >"git"init
    Initialized"empty"Git"repository"in"flipr*db/.git/
    >
    Gitiup
    >
    ""echo"Flipr"Database"Project">"README.md
    >
    ""git"add".
    >
    25
    Wednesday, May 22, 13

    View full-size slide

  73. ""mkdir"flipr*db
    >"cd"flipr*db
    >"git"init
    Initialized"empty"Git"repository"in"flipr*db/.git/
    >
    Gitiup
    >
    ""echo"Flipr"Database"Project">"README.md
    >
    ""git"add".
    >
    ""git"commit"*m"'Initialize"repo,"add"README.'
    [master"(root*commit)"2c4b510]"Initialize"repo,"add"README.
    1"file"changed,"1"insertion(+)
    "create"mode"100644"README.md
    >
    25
    Wednesday, May 22, 13

    View full-size slide

  74. ""mkdir"flipr*db
    >"cd"flipr*db
    >"git"init
    Initialized"empty"Git"repository"in"flipr*db/.git/
    >
    Gitiup
    >
    ""echo"Flipr"Database"Project">"README.md
    >
    ""git"add".
    >
    ""git"commit"*m"'Initialize"repo,"add"README.'
    [master"(root*commit)"2c4b510]"Initialize"repo,"add"README.
    1"file"changed,"1"insertion(+)
    "create"mode"100644"README.md
    >
    25
    Wednesday, May 22, 13

    View full-size slide

  75. ""mkdir"flipr*db
    >"cd"flipr*db
    >"git"init
    Initialized"empty"Git"repository"in"flipr*db/.git/
    >
    Gitiup
    >
    ""echo"Flipr"Database"Project">"README.md
    >
    ""git"add".
    >
    ""git"commit"*m"'Initialize"repo,"add"README.'
    [master"(root*commit)"2c4b510]"Initialize"repo,"add"README.
    1"file"changed,"1"insertion(+)
    "create"mode"100644"README.md
    >
    We have a
    Git repo.
    25
    Wednesday, May 22, 13

    View full-size slide

  76. >
    Origin
    26
    Wednesday, May 22, 13

    View full-size slide

  77. ""git"remote"add"origin"../flipr*remote
    >
    >
    Origin
    26
    Wednesday, May 22, 13

    View full-size slide

  78. ""git"remote"add"origin"../flipr*remote
    >
    >
    Origin
    Or remote URL
    26
    Wednesday, May 22, 13

    View full-size slide

  79. ""git"push"*u"origin"master
    Counting"objects:"3,"done.
    Writing"objects:"100%"(3/3),"260"bytes,"done.
    Total"3"(delta"0),"reused"0"(delta"0)
    To"../flipr*remote
    "*"[new"branch]""""""master"*>"master
    Branch"master"set"up"to"track"remote"branch"master"from"
    origin.
    >
    ""git"remote"add"origin"../flipr*remote
    >
    >
    Origin
    26
    Wednesday, May 22, 13

    View full-size slide

  80. ""git"push"*u"origin"master
    Counting"objects:"3,"done.
    Writing"objects:"100%"(3/3),"260"bytes,"done.
    Total"3"(delta"0),"reused"0"(delta"0)
    To"../flipr*remote
    "*"[new"branch]""""""master"*>"master
    Branch"master"set"up"to"track"remote"branch"master"from"
    origin.
    >
    ""git"remote"add"origin"../flipr*remote
    >
    >
    Origin
    26
    Wednesday, May 22, 13

    View full-size slide

  81. ""git"push"*u"origin"master
    Counting"objects:"3,"done.
    Writing"objects:"100%"(3/3),"260"bytes,"done.
    Total"3"(delta"0),"reused"0"(delta"0)
    To"../flipr*remote
    "*"[new"branch]""""""master"*>"master
    Branch"master"set"up"to"track"remote"branch"master"from"
    origin.
    >
    ""git"remote"add"origin"../flipr*remote
    >
    >
    Origin
    26
    Wednesday, May 22, 13

    View full-size slide

  82. ""git"push"*u"origin"master
    Counting"objects:"3,"done.
    Writing"objects:"100%"(3/3),"260"bytes,"done.
    Total"3"(delta"0),"reused"0"(delta"0)
    To"../flipr*remote
    "*"[new"branch]""""""master"*>"master
    Branch"master"set"up"to"track"remote"branch"master"from"
    origin.
    >
    ""git"remote"add"origin"../flipr*remote
    >
    >
    Origin
    26
    Wednesday, May 22, 13

    View full-size slide

  83. Swimming Upstream
    >
    27
    Wednesday, May 22, 13

    View full-size slide

  84. ""git"remote"add"upstream"\
    ""https://github.com/theory/agile*flipr.git
    >
    Swimming Upstream
    >
    27
    Wednesday, May 22, 13

    View full-size slide

  85. ""git"remote"add"upstream"\
    ""https://github.com/theory/agile*flipr.git
    >
    Swimming Upstream
    >
    ""git"fetch"upstream
    From"https://github.com/theory/agile*flipr
    "*"[new"branch]""""""flips""""""*>"upstream/flips
    "*"[new"branch]""""""master"""""*>"upstream/master
    "*"[new"branch]""""""userfuncs""*>"upstream/userfuncs
    "*"[new"branch]""""""users""""""*>"upstream/users
    "*"[new"tag]"""""""""insert_user"*>"insert_user
    "*"[new"tag]"""""""""appschema"*>"appschema
    "*"[new"tag]"""""""""v1.0.0*r1""*>"v1.0.0*r1
    "*"[new"tag]"""""""""reltag"""""*>"reltag
    "*"[new"tag]"""""""""insert_user2"*>"insert_user2
    >
    27
    Wednesday, May 22, 13

    View full-size slide

  86. ""git"remote"add"upstream"\
    ""https://github.com/theory/agile*flipr.git
    >
    Swimming Upstream
    >
    ""git"fetch"upstream
    From"https://github.com/theory/agile*flipr
    "*"[new"branch]""""""flips""""""*>"upstream/flips
    "*"[new"branch]""""""master"""""*>"upstream/master
    "*"[new"branch]""""""userfuncs""*>"upstream/userfuncs
    "*"[new"branch]""""""users""""""*>"upstream/users
    "*"[new"tag]"""""""""insert_user"*>"insert_user
    "*"[new"tag]"""""""""appschema"*>"appschema
    "*"[new"tag]"""""""""v1.0.0*r1""*>"v1.0.0*r1
    "*"[new"tag]"""""""""reltag"""""*>"reltag
    "*"[new"tag]"""""""""insert_user2"*>"insert_user2
    >
    For
    resetting
    later
    27
    Wednesday, May 22, 13

    View full-size slide

  87. Git?
    antisocial network
    28
    Wednesday, May 22, 13

    View full-size slide

  88. Git?
    Manage tree of files over time
    antisocial network
    28
    Wednesday, May 22, 13

    View full-size slide

  89. Git?
    Manage tree of files over time
    Distributed development
    antisocial network
    28
    Wednesday, May 22, 13

    View full-size slide

  90. Git?
    Manage tree of files over time
    Distributed development
    Commit changes locally
    antisocial network
    28
    Wednesday, May 22, 13

    View full-size slide

  91. Git?
    Manage tree of files over time
    Distributed development
    Commit changes locally
    Merge to remote (origin, upstream)
    antisocial network
    28
    Wednesday, May 22, 13

    View full-size slide

  92. Git?
    Manage tree of files over time
    Distributed development
    Commit changes locally
    Merge to remote (origin, upstream)
    Speedy, responsive
    antisocial network
    28
    Wednesday, May 22, 13

    View full-size slide

  93. Git?
    Manage tree of files over time
    Distributed development
    Commit changes locally
    Merge to remote (origin, upstream)
    Speedy, responsive
    Flexible, robust
    antisocial network
    28
    Wednesday, May 22, 13

    View full-size slide

  94. Why Git?
    antisocial network
    29
    Wednesday, May 22, 13

    View full-size slide

  95. Why Git?
    Anyone can clone
    antisocial network
    29
    Wednesday, May 22, 13

    View full-size slide

  96. Why Git?
    Anyone can clone
    Complete repository copy
    antisocial network
    29
    Wednesday, May 22, 13

    View full-size slide

  97. Why Git?
    Anyone can clone
    Complete repository copy
    Cheap branching
    antisocial network
    29
    Wednesday, May 22, 13

    View full-size slide

  98. Why Git?
    Anyone can clone
    Complete repository copy
    Cheap branching
    Make and test local changes
    antisocial network
    29
    Wednesday, May 22, 13

    View full-size slide

  99. Why Git?
    Anyone can clone
    Complete repository copy
    Cheap branching
    Make and test local changes
    Submit patches, pull requests
    antisocial network
    29
    Wednesday, May 22, 13

    View full-size slide

  100. Why Git?
    Anyone can clone
    Complete repository copy
    Cheap branching
    Make and test local changes
    Submit patches, pull requests
    Pull from upstream
    antisocial network
    29
    Wednesday, May 22, 13

    View full-size slide

  101. PiSHA1
    antisocial network
    30
    Wednesday, May 22, 13

    View full-size slide

  102. PiSHA1
    SHA1 ID for every object
    antisocial network
    30
    Wednesday, May 22, 13

    View full-size slide

  103. PiSHA1
    SHA1 ID for every object
    commit, tag, tree, blob
    antisocial network
    30
    Wednesday, May 22, 13

    View full-size slide

  104. PiSHA1
    SHA1 ID for every object
    commit, tag, tree, blob
    Hashed commit text includes:
    antisocial network
    30
    Wednesday, May 22, 13

    View full-size slide

  105. PiSHA1
    SHA1 ID for every object
    commit, tag, tree, blob
    Hashed commit text includes:
    tree ID
    antisocial network
    30
    Wednesday, May 22, 13

    View full-size slide

  106. PiSHA1
    SHA1 ID for every object
    commit, tag, tree, blob
    Hashed commit text includes:
    tree ID
    parent ID
    antisocial network
    30
    Wednesday, May 22, 13

    View full-size slide

  107. PiSHA1
    SHA1 ID for every object
    commit, tag, tree, blob
    Hashed commit text includes:
    tree ID
    parent ID
    author
    antisocial network
    30
    Wednesday, May 22, 13

    View full-size slide

  108. PiSHA1
    SHA1 ID for every object
    commit, tag, tree, blob
    Hashed commit text includes:
    tree ID
    parent ID
    author
    committer
    antisocial network
    30
    Wednesday, May 22, 13

    View full-size slide

  109. PiSHA1
    SHA1 ID for every object
    commit, tag, tree, blob
    Hashed commit text includes:
    tree ID
    parent ID
    author
    committer
    message
    antisocial network
    30
    Wednesday, May 22, 13

    View full-size slide

  110. >
    Making a hash of it
    31
    Wednesday, May 22, 13

    View full-size slide

  111. >
    ""git"cat*file"commit"HEAD
    tree"8b0955fd015782a26953e20d41db5fadbb347c14
    parent"0f38581c4d19313d910c2080b3470cae07f3694e
    author"David"Wheeler""1367971872"*0700
    committer"David"Wheeler""1367971872"*0700
    Get"through""Ship"it!".
    >
    Making a hash of it
    31
    Wednesday, May 22, 13

    View full-size slide

  112. >
    ""git"cat*file"commit"HEAD
    tree"8b0955fd015782a26953e20d41db5fadbb347c14
    parent"0f38581c4d19313d910c2080b3470cae07f3694e
    author"David"Wheeler""1367971872"*0700
    committer"David"Wheeler""1367971872"*0700
    Get"through""Ship"it!".
    >
    Making a hash of it
    31
    Wednesday, May 22, 13

    View full-size slide

  113. >
    ""git"cat*file"commit"HEAD
    tree"8b0955fd015782a26953e20d41db5fadbb347c14
    parent"0f38581c4d19313d910c2080b3470cae07f3694e
    author"David"Wheeler""1367971872"*0700
    committer"David"Wheeler""1367971872"*0700
    Get"through""Ship"it!".
    >
    Making a hash of it
    31
    Wednesday, May 22, 13

    View full-size slide

  114. >
    ""git"cat*file"commit"HEAD
    tree"8b0955fd015782a26953e20d41db5fadbb347c14
    parent"0f38581c4d19313d910c2080b3470cae07f3694e
    author"David"Wheeler""1367971872"*0700
    committer"David"Wheeler""1367971872"*0700
    Get"through""Ship"it!".
    >
    Making a hash of it
    31
    Wednesday, May 22, 13

    View full-size slide

  115. >
    ""git"cat*file"commit"HEAD
    tree"8b0955fd015782a26953e20d41db5fadbb347c14
    parent"0f38581c4d19313d910c2080b3470cae07f3694e
    author"David"Wheeler""1367971872"*0700
    committer"David"Wheeler""1367971872"*0700
    Get"through""Ship"it!".
    >
    Making a hash of it
    31
    Wednesday, May 22, 13

    View full-size slide

  116. >
    ""git"cat*file"commit"HEAD
    tree"8b0955fd015782a26953e20d41db5fadbb347c14
    parent"0f38581c4d19313d910c2080b3470cae07f3694e
    author"David"Wheeler""1367971872"*0700
    committer"David"Wheeler""1367971872"*0700
    Get"through""Ship"it!".
    >
    Making a hash of it
    31
    Wednesday, May 22, 13

    View full-size slide

  117. SHAzam!
    antisocial network
    32
    Wednesday, May 22, 13

    View full-size slide

  118. SHAzam!
    Each commit (except first) includes parent
    antisocial network
    32
    Wednesday, May 22, 13

    View full-size slide

  119. SHAzam!
    Each commit (except first) includes parent
    Can trace from any commit to the beginning
    antisocial network
    32
    Wednesday, May 22, 13

    View full-size slide

  120. SHAzam!
    Each commit (except first) includes parent
    Can trace from any commit to the beginning
    Tampering (corruption) detectable
    antisocial network
    32
    Wednesday, May 22, 13

    View full-size slide

  121. SHAzam!
    Each commit (except first) includes parent
    Can trace from any commit to the beginning
    Tampering (corruption) detectable
    Because the hash will be wrong
    antisocial network
    32
    Wednesday, May 22, 13

    View full-size slide

  122. SHAzam!
    Each commit (except first) includes parent
    Can trace from any commit to the beginning
    Tampering (corruption) detectable
    Because the hash will be wrong
    Linus Torvalds’s “greatest invention”
    http:/
    /perl.plover.com/yak/git/
    antisocial network
    32
    Wednesday, May 22, 13

    View full-size slide

  123. antisocial network
    Your Turn
    33
    Wednesday, May 22, 13

    View full-size slide

  124. antisocial network
    Your Turn
    Configure Git
    33
    Wednesday, May 22, 13

    View full-size slide

  125. antisocial network
    Your Turn
    Configure Git
    Initialize repository
    33
    Wednesday, May 22, 13

    View full-size slide

  126. antisocial network
    Your Turn
    Configure Git
    Initialize repository
    Add origin remote
    33
    Wednesday, May 22, 13

    View full-size slide

  127. antisocial network
    Your Turn
    Configure Git
    Initialize repository
    Add origin remote
    Add upstream remote
    33
    Wednesday, May 22, 13

    View full-size slide

  128. antisocial network
    Your Turn
    Configure Git
    Initialize repository
    Add origin remote
    Add upstream remote
    https:/
    /github.com/
    theory/agile-flipr.git
    33
    Wednesday, May 22, 13

    View full-size slide

  129. Who am I again?
    >
    34
    Wednesday, May 22, 13

    View full-size slide

  130. Who am I again?
    ""sqitch"config"**user"user.name"'David"E."Wheeler'
    >
    >
    34
    Wednesday, May 22, 13

    View full-size slide

  131. Who am I again?
    ""sqitch"config"**user"user.name"'David"E."Wheeler'
    >
    >
    34
    Wednesday, May 22, 13

    View full-size slide

  132. Who am I again?
    ""sqitch"config"**user"user.email"[email protected]
    >
    ""sqitch"config"**user"user.name"'David"E."Wheeler'
    >
    >
    34
    Wednesday, May 22, 13

    View full-size slide

  133. Who am I again?
    ""sqitch"config"**user"user.email"[email protected]
    >
    ""sqitch"config"**user"core.pg.client"/opt/local/pgsql/bin/psql
    >
    ""sqitch"config"**user"user.name"'David"E."Wheeler'
    >
    >
    34
    Wednesday, May 22, 13

    View full-size slide

  134. Who am I again?
    ""sqitch"config"**user"user.email"[email protected]
    >
    ""sqitch"config"**user"core.pg.client"/opt/local/pgsql/bin/psql
    >
    ""sqitch"config"**user"user.name"'David"E."Wheeler'
    >
    ""emacs"~/.sqitch/sqitch.conf
    >
    >
    34
    Wednesday, May 22, 13

    View full-size slide

  135. ~/.sqitch/sqitc
    ~/
    .sqitch/sqitch.conf
    [core""pg"]
    " client"="/opt/local/pgsql/bin/psql
    [user]
    "" name"="David"E."Wheeler
    """ email"="[email protected]
    35
    Wednesday, May 22, 13

    View full-size slide

  136. ~/.sqitch/sqitc
    ~/
    .sqitch/sqitch.conf
    [core""pg"]
    " client"="/opt/local/pgsql/bin/psql
    [user]
    "" name"="David"E."Wheeler
    """ email"="[email protected]
    Good for
    all projects
    35
    Wednesday, May 22, 13

    View full-size slide

  137. >
    Scratch that Sqitch
    36
    Wednesday, May 22, 13

    View full-size slide

  138. >"sqitch"**engine"pg"init"flipr"\
    ""**uri"file://../flipr*remote
    Created"sqitch.conf
    Created"sqitch.plan
    Created"deploy/
    Created"revert/
    Created"verify/
    >
    >
    Scratch that Sqitch
    36
    Wednesday, May 22, 13

    View full-size slide

  139. >"sqitch"**engine"pg"init"flipr"\
    ""**uri"file://../flipr*remote
    Created"sqitch.conf
    Created"sqitch.plan
    Created"deploy/
    Created"revert/
    Created"verify/
    >
    >
    Scratch that Sqitch
    36
    Wednesday, May 22, 13

    View full-size slide

  140. >"sqitch"**engine"pg"init"flipr"\
    ""**uri"file://../flipr*remote
    Created"sqitch.conf
    Created"sqitch.plan
    Created"deploy/
    Created"revert/
    Created"verify/
    >
    >
    Scratch that Sqitch
    Use remote URL
    36
    Wednesday, May 22, 13

    View full-size slide

  141. >"sqitch"**engine"pg"init"flipr"\
    ""**uri"file://../flipr*remote
    Created"sqitch.conf
    Created"sqitch.plan
    Created"deploy/
    Created"revert/
    Created"verify/
    >
    >
    Scratch that Sqitch
    36
    Wednesday, May 22, 13

    View full-size slide

  142. >"sqitch"**engine"pg"init"flipr"\
    ""**uri"file://../flipr*remote
    Created"sqitch.conf
    Created"sqitch.plan
    Created"deploy/
    Created"revert/
    Created"verify/
    >
    >
    Scratch that Sqitch
    36
    Wednesday, May 22, 13

    View full-size slide

  143. >"sqitch"**engine"pg"init"flipr"\
    ""**uri"file://../flipr*remote
    Created"sqitch.conf
    Created"sqitch.plan
    Created"deploy/
    Created"revert/
    Created"verify/
    >
    >
    Scratch that Sqitch
    36
    Wednesday, May 22, 13

    View full-size slide

  144. >"sqitch"**engine"pg"init"flipr"\
    ""**uri"file://../flipr*remote
    Created"sqitch.conf
    Created"sqitch.plan
    Created"deploy/
    Created"revert/
    Created"verify/
    >
    >
    Scratch that Sqitch
    36
    Wednesday, May 22, 13

    View full-size slide

  145. >"sqitch"**engine"pg"init"flipr"\
    ""**uri"file://../flipr*remote
    Created"sqitch.conf
    Created"sqitch.plan
    Created"deploy/
    Created"revert/
    Created"verify/
    >
    >
    Scratch that Sqitch
    36
    Wednesday, May 22, 13

    View full-size slide

  146. >"sqitch"**engine"pg"init"flipr"\
    ""**uri"file://../flipr*remote
    Created"sqitch.conf
    Created"sqitch.plan
    Created"deploy/
    Created"revert/
    Created"verify/
    >
    >
    Scratch that Sqitch
    ""emacs"sqitch.conf
    >
    36
    Wednesday, May 22, 13

    View full-size slide

  147. sqitch.conf
    sqitch.conf
    [core]
    " engine"="pg
    " #"plan_file"="sqitch.plan
    " #"top_dir"=".
    " #"deploy_dir"="deploy
    " #"revert_dir"="revert
    " #"verify_dir"="verify
    " #"extension"="sql
    #"[core""pg"]
    " #"client"="/usr/local/pgsql/bin/psql
    " #"username"="
    " #"password"="
    " #"db_name"="
    " #"host"="
    " #"port"="
    " #"sqitch_schema"="sqitch
    37
    Wednesday, May 22, 13

    View full-size slide

  148. sqitch.conf
    sqitch.conf
    [core]
    " engine"="pg
    " #"plan_file"="sqitch.plan
    " #"top_dir"=".
    " #"deploy_dir"="deploy
    " #"revert_dir"="revert
    " #"verify_dir"="verify
    " #"extension"="sql
    #"[core""pg"]
    " #"client"="/usr/local/pgsql/bin/psql
    " #"username"="
    " #"password"="
    " #"db_name"="
    " #"host"="
    " #"port"="
    " #"sqitch_schema"="sqitch
    --engine pg
    37
    Wednesday, May 22, 13

    View full-size slide

  149. What’s the Plan Man?
    >
    38
    Wednesday, May 22, 13

    View full-size slide

  150. What’s the Plan Man?
    ""emacs"sqitch.plan
    >
    >
    38
    Wednesday, May 22, 13

    View full-size slide

  151. sqitch.plan
    sqitch.plan
    %syntax*version=1.0.0*b1
    %project=flipr
    %uri=file://../flipr*remote
    39
    Wednesday, May 22, 13

    View full-size slide

  152. sqitch.plan
    sqitch.plan
    %syntax*version=1.0.0*b1
    %project=flipr
    %uri=file://../flipr*remote
    Identified
    39
    Wednesday, May 22, 13

    View full-size slide

  153. Make It So
    >
    40
    Wednesday, May 22, 13

    View full-size slide

  154. Make It So
    >
    ""git"add".
    >"git"commit"*m"'Initialize"Sqitch"configuration.'
    [master"297c07d]"Initialize"Sqitch"configuration.
    "2"files"changed,"20"insertions(+)
    "create"mode"100644"sqitch.conf
    "create"mode"100644"sqitch.plan
    >
    40
    Wednesday, May 22, 13

    View full-size slide

  155. Make It So
    >
    ""git"add".
    >"git"commit"*m"'Initialize"Sqitch"configuration.'
    [master"297c07d]"Initialize"Sqitch"configuration.
    "2"files"changed,"20"insertions(+)
    "create"mode"100644"sqitch.conf
    "create"mode"100644"sqitch.plan
    >
    40
    Wednesday, May 22, 13

    View full-size slide

  156. Make It So
    >
    ""git"add".
    >"git"commit"*m"'Initialize"Sqitch"configuration.'
    [master"297c07d]"Initialize"Sqitch"configuration.
    "2"files"changed,"20"insertions(+)
    "create"mode"100644"sqitch.conf
    "create"mode"100644"sqitch.plan
    >
    40
    Wednesday, May 22, 13

    View full-size slide

  157. Make It So
    >
    ""git"add".
    >"git"commit"*m"'Initialize"Sqitch"configuration.'
    [master"297c07d]"Initialize"Sqitch"configuration.
    "2"files"changed,"20"insertions(+)
    "create"mode"100644"sqitch.conf
    "create"mode"100644"sqitch.plan
    >
    ""git"push
    Counting"objects:"5,"done.
    Delta"compression"using"up"to"4"threads.
    Compressing"objects:"100%"(4/4),"done.
    Writing"objects:"100%"(4/4),"564"bytes,"done.
    Total"4"(delta"0),"reused"0"(delta"0)
    To"../flipr*remote
    """7d28be0..297c07d""master"*>"master
    >
    40
    Wednesday, May 22, 13

    View full-size slide

  158. Make It So
    >
    ""git"add".
    >"git"commit"*m"'Initialize"Sqitch"configuration.'
    [master"297c07d]"Initialize"Sqitch"configuration.
    "2"files"changed,"20"insertions(+)
    "create"mode"100644"sqitch.conf
    "create"mode"100644"sqitch.plan
    >
    ""git"push
    Counting"objects:"5,"done.
    Delta"compression"using"up"to"4"threads.
    Compressing"objects:"100%"(4/4),"done.
    Writing"objects:"100%"(4/4),"564"bytes,"done.
    Total"4"(delta"0),"reused"0"(delta"0)
    To"../flipr*remote
    """7d28be0..297c07d""master"*>"master
    >
    40
    Wednesday, May 22, 13

    View full-size slide

  159. Make It So
    >
    ""git"add".
    >"git"commit"*m"'Initialize"Sqitch"configuration.'
    [master"297c07d]"Initialize"Sqitch"configuration.
    "2"files"changed,"20"insertions(+)
    "create"mode"100644"sqitch.conf
    "create"mode"100644"sqitch.plan
    >
    ""git"push
    Counting"objects:"5,"done.
    Delta"compression"using"up"to"4"threads.
    Compressing"objects:"100%"(4/4),"done.
    Writing"objects:"100%"(4/4),"564"bytes,"done.
    Total"4"(delta"0),"reused"0"(delta"0)
    To"../flipr*remote
    """7d28be0..297c07d""master"*>"master
    >
    40
    Wednesday, May 22, 13

    View full-size slide

  160. Where’ve We Been?
    >
    41
    Wednesday, May 22, 13

    View full-size slide

  161. ""git"log
    commit"a4f765f88c1875ffe9427e73f72a6b66ce363ed4
    Author:"David"E."Wheeler"
    Date:"""Tue"May"21"15:00:33"2013"*0400
    """"Initialize"Sqitch"configuration.
    commit"2c4b51017929634a30f4a74e20268c05effdfcb3
    Author:"David"E."Wheeler"
    Date:"""Tue"May"21"14:59:16"2013"*0400
    """"Initialize"repo,"add"README.
    >
    Where’ve We Been?
    >
    41
    Wednesday, May 22, 13

    View full-size slide

  162. ""git"log
    commit"a4f765f88c1875ffe9427e73f72a6b66ce363ed4
    Author:"David"E."Wheeler"
    Date:"""Tue"May"21"15:00:33"2013"*0400
    """"Initialize"Sqitch"configuration.
    commit"2c4b51017929634a30f4a74e20268c05effdfcb3
    Author:"David"E."Wheeler"
    Date:"""Tue"May"21"14:59:16"2013"*0400
    """"Initialize"repo,"add"README.
    >
    Where’ve We Been?
    >
    41
    Wednesday, May 22, 13

    View full-size slide

  163. ""git"log
    commit"a4f765f88c1875ffe9427e73f72a6b66ce363ed4
    Author:"David"E."Wheeler"
    Date:"""Tue"May"21"15:00:33"2013"*0400
    """"Initialize"Sqitch"configuration.
    commit"2c4b51017929634a30f4a74e20268c05effdfcb3
    Author:"David"E."Wheeler"
    Date:"""Tue"May"21"14:59:16"2013"*0400
    """"Initialize"repo,"add"README.
    >
    Where’ve We Been?
    >
    41
    Wednesday, May 22, 13

    View full-size slide

  164. First Change
    >
    42
    Wednesday, May 22, 13

    View full-size slide

  165. First Change
    >"sqitch"add"appschema"*n"'Adds"flipr"app"schema.'
    Created"deploy/appschema.sql
    Created"revert/appschema.sql
    Created"verify/appschema.sql
    Added""appschema""to"sqitch.plan
    >
    >
    42
    Wednesday, May 22, 13

    View full-size slide

  166. First Change
    >"sqitch"add"appschema"*n"'Adds"flipr"app"schema.'
    Created"deploy/appschema.sql
    Created"revert/appschema.sql
    Created"verify/appschema.sql
    Added""appschema""to"sqitch.plan
    >
    >
    42
    Wednesday, May 22, 13

    View full-size slide

  167. First Change
    >"sqitch"add"appschema"*n"'Adds"flipr"app"schema.'
    Created"deploy/appschema.sql
    Created"revert/appschema.sql
    Created"verify/appschema.sql
    Added""appschema""to"sqitch.plan
    >
    >
    42
    Wednesday, May 22, 13

    View full-size slide

  168. First Change
    >"sqitch"add"appschema"*n"'Adds"flipr"app"schema.'
    Created"deploy/appschema.sql
    Created"revert/appschema.sql
    Created"verify/appschema.sql
    Added""appschema""to"sqitch.plan
    >
    >
    ""emacs"deploy/appschema.sql
    >
    42
    Wednesday, May 22, 13

    View full-size slide

  169. deploy/appschem
    deploy/appschema.sql
    **"Deploy"appschema
    BEGIN;
    COMMIT;
    **"XXX"Add"DDLs"here.
    43
    Wednesday, May 22, 13

    View full-size slide

  170. deploy/appschem
    deploy/appschema.sql
    **"Deploy"appschema
    BEGIN;
    COMMIT;
    CREATE"SCHEMA"flipr;
    43
    Wednesday, May 22, 13

    View full-size slide

  171. >"sqitch"add"appschema"*n"'Adds"flipr"app"schema.'
    Created"deploy/appschema.sql
    Created"revert/appschema.sql
    Created"verify/appschema.sql
    Added""appschema""to"sqitch.plan
    >
    First Change
    >
    ""emacs"deploy/appschema.sql
    >
    44
    Wednesday, May 22, 13

    View full-size slide

  172. >"sqitch"add"appschema"*n"'Adds"flipr"app"schema.'
    Created"deploy/appschema.sql
    Created"revert/appschema.sql
    Created"verify/appschema.sql
    Added""appschema""to"sqitch.plan
    >
    First Change
    >
    ""emacs"deploy/appschema.sql
    >
    ""emacs"revert/appschema.sql
    >
    44
    Wednesday, May 22, 13

    View full-size slide

  173. revert/appschem
    revert/appschema.sql
    **"Revert"appschema
    BEGIN;
    COMMIT;
    **"XXX"Add"DDLs"here.
    45
    Wednesday, May 22, 13

    View full-size slide

  174. revert/appschem
    revert/appschema.sql
    **"Revert"appschema
    BEGIN;
    COMMIT;
    DROP"SCHEMA"flipr;
    45
    Wednesday, May 22, 13

    View full-size slide

  175. Make it So!
    >
    46
    Wednesday, May 22, 13

    View full-size slide

  176. ""createdb"flipr_test
    >"sqitch"**db*name"flipr_test"deploy
    Adding"metadata"tables"to"flipr_test
    Deploying"to"flipr_test
    ""+"appschema".."ok
    >
    Make it So!
    >
    46
    Wednesday, May 22, 13

    View full-size slide

  177. ""createdb"flipr_test
    >"sqitch"**db*name"flipr_test"deploy
    Adding"metadata"tables"to"flipr_test
    Deploying"to"flipr_test
    ""+"appschema".."ok
    >
    Make it So!
    >
    Might need
    -U postgres
    46
    Wednesday, May 22, 13

    View full-size slide

  178. ""createdb"flipr_test
    >"sqitch"**db*name"flipr_test"deploy
    Adding"metadata"tables"to"flipr_test
    Deploying"to"flipr_test
    ""+"appschema".."ok
    >
    Make it So!
    >
    46
    Wednesday, May 22, 13

    View full-size slide

  179. ""createdb"flipr_test
    >"sqitch"**db*name"flipr_test"deploy
    Adding"metadata"tables"to"flipr_test
    Deploying"to"flipr_test
    ""+"appschema".."ok
    >
    Make it So!
    >
    46
    Wednesday, May 22, 13

    View full-size slide

  180. ""createdb"flipr_test
    >"sqitch"**db*name"flipr_test"deploy
    Adding"metadata"tables"to"flipr_test
    Deploying"to"flipr_test
    ""+"appschema".."ok
    >
    Make it So!
    >
    46
    Wednesday, May 22, 13

    View full-size slide

  181. ""createdb"flipr_test
    >"sqitch"**db*name"flipr_test"deploy
    Adding"metadata"tables"to"flipr_test
    Deploying"to"flipr_test
    ""+"appschema".."ok
    >
    Make it So!
    >
    46
    Wednesday, May 22, 13

    View full-size slide

  182. ""createdb"flipr_test
    >"sqitch"**db*name"flipr_test"deploy
    Adding"metadata"tables"to"flipr_test
    Deploying"to"flipr_test
    ""+"appschema".."ok
    >
    Make it So!
    >
    ""psql"*d"flipr_test"*c"'\dn"flipr'
    List"of"schemas
    "Name""|"Owner"
    *******+*******
    "flipr"|"david
    >
    46
    Wednesday, May 22, 13

    View full-size slide

  183. ""createdb"flipr_test
    >"sqitch"**db*name"flipr_test"deploy
    Adding"metadata"tables"to"flipr_test
    Deploying"to"flipr_test
    ""+"appschema".."ok
    >
    Make it So!
    >
    ""psql"*d"flipr_test"*c"'\dn"flipr'
    List"of"schemas
    "Name""|"Owner"
    *******+*******
    "flipr"|"david
    >
    Trust, but
    verify
    46
    Wednesday, May 22, 13

    View full-size slide

  184. ""createdb"flipr_test
    >"sqitch"**db*name"flipr_test"deploy
    Adding"metadata"tables"to"flipr_test
    Deploying"to"flipr_test
    ""+"appschema".."ok
    >
    Make it So!
    >
    ""psql"*d"flipr_test"*c"'\dn"flipr'
    List"of"schemas
    "Name""|"Owner"
    *******+*******
    "flipr"|"david
    >
    ""emacs"verify/appschema.sql
    46
    Wednesday, May 22, 13

    View full-size slide

  185. **"Verify"appschema
    BEGIN;
    ROLLBACK;
    verify/appschem
    verify/appschema.sql
    **"XXX"Add"verifications"here.
    47
    Wednesday, May 22, 13

    View full-size slide

  186. **"Verify"appschema
    BEGIN;
    ROLLBACK;
    SELECT"pg_catalog.has_schema_privilege('nada',"'usage');
    verify/appschem
    verify/appschema.sql
    47
    Wednesday, May 22, 13

    View full-size slide

  187. **"Verify"appschema
    BEGIN;
    ROLLBACK;
    SELECT"pg_catalog.has_schema_privilege('nada',"'usage');
    verify/appschem
    verify/appschema.sql
    Let’s try
    it, first
    47
    Wednesday, May 22, 13

    View full-size slide

  188. Trust, But Verify
    >
    ""emacs"verify/appschema.sql
    >
    48
    Wednesday, May 22, 13

    View full-size slide

  189. Trust, But Verify
    >
    ""emacs"verify/appschema.sql
    >
    ""sqitch"**db*name"flipr_test"verify
    Verifying"flipr_test
    ""*"appschema".."psql:verify/appschema.sql:5:"
    ERROR:""schema""nada""does"not"exist
    #"Verify"script""verify/appschema.sql""failed.
    not"ok
    Verify"Summary"Report
    *********************
    Changes:"1
    Errors:""1
    Verify"failed
    >
    48
    Wednesday, May 22, 13

    View full-size slide

  190. Trust, But Verify
    >
    ""emacs"verify/appschema.sql
    >
    ""sqitch"**db*name"flipr_test"verify
    Verifying"flipr_test
    ""*"appschema".."psql:verify/appschema.sql:5:"
    ERROR:""schema""nada""does"not"exist
    #"Verify"script""verify/appschema.sql""failed.
    not"ok
    Verify"Summary"Report
    *********************
    Changes:"1
    Errors:""1
    Verify"failed
    >
    48
    Wednesday, May 22, 13

    View full-size slide

  191. Trust, But Verify
    >
    ""emacs"verify/appschema.sql
    >
    ""sqitch"**db*name"flipr_test"verify
    Verifying"flipr_test
    ""*"appschema".."psql:verify/appschema.sql:5:"
    ERROR:""schema""nada""does"not"exist
    #"Verify"script""verify/appschema.sql""failed.
    not"ok
    Verify"Summary"Report
    *********************
    Changes:"1
    Errors:""1
    Verify"failed
    >
    48
    Wednesday, May 22, 13

    View full-size slide

  192. Trust, But Verify
    >
    ""emacs"verify/appschema.sql
    >
    ""sqitch"**db*name"flipr_test"verify
    Verifying"flipr_test
    ""*"appschema".."psql:verify/appschema.sql:5:"
    ERROR:""schema""nada""does"not"exist
    #"Verify"script""verify/appschema.sql""failed.
    not"ok
    Verify"Summary"Report
    *********************
    Changes:"1
    Errors:""1
    Verify"failed
    >
    48
    Wednesday, May 22, 13

    View full-size slide

  193. Trust, But Verify
    >
    49
    Wednesday, May 22, 13

    View full-size slide

  194. ""perl"*i"*pe"'s/nada/flipr/'"verify/appschema.sql
    >"
    Trust, But Verify
    >
    49
    Wednesday, May 22, 13

    View full-size slide

  195. ""sqitch"**db*name"flipr_test"verify
    Verifying"flipr_test
    ""*"appschema".."ok
    Verify"successful
    >
    ""perl"*i"*pe"'s/nada/flipr/'"verify/appschema.sql
    >"
    Trust, But Verify
    >
    Mo betta.
    49
    Wednesday, May 22, 13

    View full-size slide

  196. >
    How’s it Look?
    50
    Wednesday, May 22, 13

    View full-size slide

  197. >
    How’s it Look?
    >"sqitch"*d"flipr_test"status
    #"On"database"flipr_test
    #"Project:""flipr
    #"Change:"""748346dfe73cf2af32a8b7088fd75ad8d7aecda3
    #"Name:"""""appschema
    #"Deployed:"2013*05*21"15:04:08"*0400
    #"By:"""""""David"E."Wheeler"
    #"
    Nothing"to"deploy"(up*to*date)
    >
    50
    Wednesday, May 22, 13

    View full-size slide

  198. >
    How’s it Look?
    >"sqitch"*d"flipr_test"status
    #"On"database"flipr_test
    #"Project:""flipr
    #"Change:"""748346dfe73cf2af32a8b7088fd75ad8d7aecda3
    #"Name:"""""appschema
    #"Deployed:"2013*05*21"15:04:08"*0400
    #"By:"""""""David"E."Wheeler"
    #"
    Nothing"to"deploy"(up*to*date)
    >
    50
    Wednesday, May 22, 13

    View full-size slide

  199. >
    How’s it Look?
    >"sqitch"*d"flipr_test"status
    #"On"database"flipr_test
    #"Project:""flipr
    #"Change:"""748346dfe73cf2af32a8b7088fd75ad8d7aecda3
    #"Name:"""""appschema
    #"Deployed:"2013*05*21"15:04:08"*0400
    #"By:"""""""David"E."Wheeler"
    #"
    Nothing"to"deploy"(up*to*date)
    >
    50
    Wednesday, May 22, 13

    View full-size slide

  200. >
    How’s it Look?
    >"sqitch"*d"flipr_test"status
    #"On"database"flipr_test
    #"Project:""flipr
    #"Change:"""748346dfe73cf2af32a8b7088fd75ad8d7aecda3
    #"Name:"""""appschema
    #"Deployed:"2013*05*21"15:04:08"*0400
    #"By:"""""""David"E."Wheeler"
    #"
    Nothing"to"deploy"(up*to*date)
    >
    50
    Wednesday, May 22, 13

    View full-size slide

  201. >
    How’s it Look?
    >"sqitch"*d"flipr_test"status
    #"On"database"flipr_test
    #"Project:""flipr
    #"Change:"""748346dfe73cf2af32a8b7088fd75ad8d7aecda3
    #"Name:"""""appschema
    #"Deployed:"2013*05*21"15:04:08"*0400
    #"By:"""""""David"E."Wheeler"
    #"
    Nothing"to"deploy"(up*to*date)
    >
    50
    Wednesday, May 22, 13

    View full-size slide

  202. >
    How’s it Look?
    >"sqitch"*d"flipr_test"status
    #"On"database"flipr_test
    #"Project:""flipr
    #"Change:"""748346dfe73cf2af32a8b7088fd75ad8d7aecda3
    #"Name:"""""appschema
    #"Deployed:"2013*05*21"15:04:08"*0400
    #"By:"""""""David"E."Wheeler"
    #"
    Nothing"to"deploy"(up*to*date)
    >
    50
    Wednesday, May 22, 13

    View full-size slide

  203. Go Back
    >
    51
    Wednesday, May 22, 13

    View full-size slide

  204. >"sqitch"**db*name"flipr_test"revert
    ""Revert"all"changes"from"flipr_test?"[Yes]
    Go Back
    >
    51
    Wednesday, May 22, 13

    View full-size slide

  205. >"sqitch"**db*name"flipr_test"revert
    ""Revert"all"changes"from"flipr_test?"[Yes]
    Go Back
    >
    51
    Wednesday, May 22, 13

    View full-size slide

  206. >"sqitch"**db*name"flipr_test"revert
    ""Revert"all"changes"from"flipr_test?"[Yes]
    Go Back
    >
    ""*"appschema".."ok
    >
    51
    Wednesday, May 22, 13

    View full-size slide

  207. >"sqitch"**db*name"flipr_test"revert
    ""Revert"all"changes"from"flipr_test?"[Yes]
    Go Back
    >
    ""psql"*d"flipr_test"*c"'\dn"flipr'
    """"""""""""List"of"roles
    List"of"schemas
    "Name"|"Owner"
    ******+*******
    >
    ""*"appschema".."ok
    >
    51
    Wednesday, May 22, 13

    View full-size slide

  208. >"sqitch"**db*name"flipr_test"revert
    ""Revert"all"changes"from"flipr_test?"[Yes]
    Go Back
    >
    ""psql"*d"flipr_test"*c"'\dn"flipr'
    """"""""""""List"of"roles
    List"of"schemas
    "Name"|"Owner"
    ******+*******
    >
    ""*"appschema".."ok
    >
    ""sqitch"*d"flipr_test"status
    #"On"database"flipr_test
    No"changes"deployed
    >
    51
    Wednesday, May 22, 13

    View full-size slide

  209. History
    >
    52
    Wednesday, May 22, 13

    View full-size slide

  210. History
    >
    >"sqitch"*d"flipr_test"log
    On"database"flipr_test
    Revert"748346dfe73cf2af32a8b7088fd75ad8d7aecda3
    Name:""""""appschema
    Committer:"David"E."Wheeler"
    Date:""""""2013*05*21"15:07:07"*0400
    """"Adds"flipr"app"schema.
    Deploy"748346dfe73cf2af32a8b7088fd75ad8d7aecda3
    Name:""""""appschema
    Committer:"David"E."Wheeler"
    Date:""""""2013*05*21"15:04:08"*0400
    """"Adds"flipr"app"schema.
    52
    Wednesday, May 22, 13

    View full-size slide

  211. History
    >
    >"sqitch"*d"flipr_test"log
    On"database"flipr_test
    Revert"748346dfe73cf2af32a8b7088fd75ad8d7aecda3
    Name:""""""appschema
    Committer:"David"E."Wheeler"
    Date:""""""2013*05*21"15:07:07"*0400
    """"Adds"flipr"app"schema.
    Deploy"748346dfe73cf2af32a8b7088fd75ad8d7aecda3
    Name:""""""appschema
    Committer:"David"E."Wheeler"
    Date:""""""2013*05*21"15:04:08"*0400
    """"Adds"flipr"app"schema.
    52
    Wednesday, May 22, 13

    View full-size slide

  212. History
    >
    >"sqitch"*d"flipr_test"log
    On"database"flipr_test
    Revert"748346dfe73cf2af32a8b7088fd75ad8d7aecda3
    Name:""""""appschema
    Committer:"David"E."Wheeler"
    Date:""""""2013*05*21"15:07:07"*0400
    """"Adds"flipr"app"schema.
    Deploy"748346dfe73cf2af32a8b7088fd75ad8d7aecda3
    Name:""""""appschema
    Committer:"David"E."Wheeler"
    Date:""""""2013*05*21"15:04:08"*0400
    """"Adds"flipr"app"schema.
    52
    Wednesday, May 22, 13

    View full-size slide

  213. Commit It!
    >
    53
    Wednesday, May 22, 13

    View full-size slide

  214. Commit It!
    >
    ""git"add".
    >"git"commit"*m"'Add"flipr"schema.'
    [master"dc23038]"Add"flipr"schema.
    "4"files"changed,"22"insertions(+)
    "create"mode"100644"deploy/appschema.sql
    "create"mode"100644"revert/appschema.sql
    "create"mode"100644"verify/appschema.sql
    >
    53
    Wednesday, May 22, 13

    View full-size slide

  215. Commit It!
    >
    ""git"add".
    >"git"commit"*m"'Add"flipr"schema.'
    [master"dc23038]"Add"flipr"schema.
    "4"files"changed,"22"insertions(+)
    "create"mode"100644"deploy/appschema.sql
    "create"mode"100644"revert/appschema.sql
    "create"mode"100644"verify/appschema.sql
    >
    53
    Wednesday, May 22, 13

    View full-size slide

  216. Commit It!
    >
    ""git"add".
    >"git"commit"*m"'Add"flipr"schema.'
    [master"dc23038]"Add"flipr"schema.
    "4"files"changed,"22"insertions(+)
    "create"mode"100644"deploy/appschema.sql
    "create"mode"100644"revert/appschema.sql
    "create"mode"100644"verify/appschema.sql
    >
    53
    Wednesday, May 22, 13

    View full-size slide

  217. Commit It!
    >
    ""git"add".
    >"git"commit"*m"'Add"flipr"schema.'
    [master"dc23038]"Add"flipr"schema.
    "4"files"changed,"22"insertions(+)
    "create"mode"100644"deploy/appschema.sql
    "create"mode"100644"revert/appschema.sql
    "create"mode"100644"verify/appschema.sql
    >
    ""git"push
    Counting"objects:"11,"done.
    Delta"compression"using"up"to"4"threads.
    Compressing"objects:"100%"(6/6),"done.
    Writing"objects:"100%"(9/9),"943"bytes,"done.
    Total"9"(delta"0),"reused"0"(delta"0)
    To"../flipr*remote
    """a4f765f..dc23038""master"*>"master
    53
    Wednesday, May 22, 13

    View full-size slide

  218. Commit It!
    >
    ""git"add".
    >"git"commit"*m"'Add"flipr"schema.'
    [master"dc23038]"Add"flipr"schema.
    "4"files"changed,"22"insertions(+)
    "create"mode"100644"deploy/appschema.sql
    "create"mode"100644"revert/appschema.sql
    "create"mode"100644"verify/appschema.sql
    >
    ""git"push
    Counting"objects:"11,"done.
    Delta"compression"using"up"to"4"threads.
    Compressing"objects:"100%"(6/6),"done.
    Writing"objects:"100%"(9/9),"943"bytes,"done.
    Total"9"(delta"0),"reused"0"(delta"0)
    To"../flipr*remote
    """a4f765f..dc23038""master"*>"master
    53
    Wednesday, May 22, 13

    View full-size slide

  219. Redeploy
    >
    54
    Wednesday, May 22, 13

    View full-size slide

  220. >"sqitch"**db*name"flipr_test"deploy"**verify
    Deploying"changes"to"flipr_test
    ""+"appschema".."ok
    >
    Redeploy
    >
    54
    Wednesday, May 22, 13

    View full-size slide

  221. >"sqitch"**db*name"flipr_test"deploy"**verify
    Deploying"changes"to"flipr_test
    ""+"appschema".."ok
    >
    Redeploy
    >
    Integrated!
    54
    Wednesday, May 22, 13

    View full-size slide

  222. >"sqitch"**db*name"flipr_test"deploy"**verify
    Deploying"changes"to"flipr_test
    ""+"appschema".."ok
    >
    Redeploy
    >
    54
    Wednesday, May 22, 13

    View full-size slide

  223. >"sqitch"**db*name"flipr_test"deploy"**verify
    Deploying"changes"to"flipr_test
    ""+"appschema".."ok
    >
    Redeploy
    >
    ""psql"*d"flipr_test"*c"'\dn"flipr'
    List"of"schemas
    "Name""|"Owner"
    *******+*******
    "flipr"|"david
    >
    54
    Wednesday, May 22, 13

    View full-size slide

  224. Status Update
    >
    55
    Wednesday, May 22, 13

    View full-size slide

  225. >"sqitch"*d"flipr_test"status
    #"On"database"flipr_test
    #"Project:""flipr
    #"Change:"""748346dfe73cf2af32a8b7088fd75ad8d7aecda3
    #"Name:"""""appschema
    #"Deployed:"2013*05*21"15:10:02"*0400
    #"By:"""""""David"E."Wheeler"
    #"
    Nothing"to"deploy"(up*to*date)
    >
    Status Update
    >
    55
    Wednesday, May 22, 13

    View full-size slide

  226. Save My Fingers
    >
    56
    Wednesday, May 22, 13

    View full-size slide

  227. Save My Fingers
    >
    ""sqitch"config"core.pg.db_name"flipr_test
    >
    56
    Wednesday, May 22, 13

    View full-size slide

  228. Save My Fingers
    >
    ""sqitch"config"core.pg.db_name"flipr_test
    >
    ""sqitch"status
    #"On"database"flipr_test
    #"Project:""flipr
    #"Change:"""748346dfe73cf2af32a8b7088fd75ad8d7aecda3
    #"Name:"""""appschema
    #"Deployed:"2013*05*21"15:10:02"*0400
    #"By:"""""""David"E."Wheeler"
    #"
    Nothing"to"deploy"(up*to*date)
    >
    No --db-name
    56
    Wednesday, May 22, 13

    View full-size slide

  229. Save My Fingers
    >
    ""sqitch"config"core.pg.db_name"flipr_test
    >
    ""sqitch"status
    #"On"database"flipr_test
    #"Project:""flipr
    #"Change:"""748346dfe73cf2af32a8b7088fd75ad8d7aecda3
    #"Name:"""""appschema
    #"Deployed:"2013*05*21"15:10:02"*0400
    #"By:"""""""David"E."Wheeler"
    #"
    Nothing"to"deploy"(up*to*date)
    >
    No --db-name
    >"sqitch"config"**bool"deploy.verify"true
    >"sqitch"config"**bool"rebase.verify"true
    > Always
    verify.
    56
    Wednesday, May 22, 13

    View full-size slide

  230. Commit Config
    >
    57
    Wednesday, May 22, 13

    View full-size slide

  231. Commit Config
    >
    ""git"add".
    >"git"commit"*m"'Add"db_name"and"verify"to"config.'
    [master"795697f]"Add"db_name"and"verify"to"config.
    "1"file"changed,"6"insertions(+)
    >
    57
    Wednesday, May 22, 13

    View full-size slide

  232. Commit Config
    >
    ""git"add".
    >"git"commit"*m"'Add"db_name"and"verify"to"config.'
    [master"795697f]"Add"db_name"and"verify"to"config.
    "1"file"changed,"6"insertions(+)
    >
    57
    Wednesday, May 22, 13

    View full-size slide

  233. Commit Config
    >
    ""git"push
    Counting"objects:"5,"done.
    Delta"compression"using"up"to"4"threads.
    Compressing"objects:"100%"(3/3),"done.
    Writing"objects:"100%"(3/3),"376"bytes,"done.
    Total"3"(delta"2),"reused"0"(delta"0)
    To"../flipr*remote
    """dc23038..795697f""master"*>"master
    >
    ""git"add".
    >"git"commit"*m"'Add"db_name"and"verify"to"config.'
    [master"795697f]"Add"db_name"and"verify"to"config.
    "1"file"changed,"6"insertions(+)
    >
    57
    Wednesday, May 22, 13

    View full-size slide

  234. Not Migrations?
    antisocial network
    58
    Wednesday, May 22, 13

    View full-size slide

  235. Not Migrations?
    Incomplete mini-language
    antisocial network
    58
    Wednesday, May 22, 13

    View full-size slide

  236. Not Migrations?
    Incomplete mini-language
    No logical replication integration
    antisocial network
    58
    Wednesday, May 22, 13

    View full-size slide

  237. Not Migrations?
    Incomplete mini-language
    No logical replication integration
    Numbered scripts difficult to track
    antisocial network
    58
    Wednesday, May 22, 13

    View full-size slide

  238. Not Migrations?
    Incomplete mini-language
    No logical replication integration
    Numbered scripts difficult to track
    No VCS awareness
    antisocial network
    58
    Wednesday, May 22, 13

    View full-size slide

  239. SQL Migrations?
    Incomplete mini-language
    No logical replication integration
    Numbered scripts hard to track
    No VCS awareness
    antisocial network
    59
    Wednesday, May 22, 13

    View full-size slide

  240. SQL Migrations?
    Incomplete mini-language
    No logical replication integration
    Numbered scripts hard to track
    No VCS awareness
    ———————————————
    antisocial network
    59
    Wednesday, May 22, 13

    View full-size slide

  241. SQL Migrations?
    Incomplete mini-language
    No logical replication integration
    Numbered scripts hard to track
    No VCS awareness
    ———————————————
    ———————————————————
    antisocial network
    59
    Wednesday, May 22, 13

    View full-size slide

  242. SQL Migrations?
    Incomplete mini-language
    No logical replication integration
    Numbered scripts hard to track
    No VCS awareness
    ———————————————
    ———————————————————
    Managing procedures is a PITA
    antisocial network
    59
    Wednesday, May 22, 13

    View full-size slide

  243. Sq—what?
    sql anges
    ch
    antisocial network
    60
    Wednesday, May 22, 13

    View full-size slide

  244. Sq—what?
    sq ch
    antisocial network
    60
    Wednesday, May 22, 13

    View full-size slide

  245. it
    Sq—what?
    sq ch
    antisocial network
    60
    Wednesday, May 22, 13

    View full-size slide

  246. it
    Sq—what?
    sq ch
    There is no “u”
    antisocial network
    60
    Wednesday, May 22, 13

    View full-size slide

  247. Sqitch Philosophy
    antisocial network
    61
    Wednesday, May 22, 13

    View full-size slide

  248. Sqitch Philosophy
    No opinions
    antisocial network
    61
    Wednesday, May 22, 13

    View full-size slide

  249. Sqitch Philosophy
    No opinions
    Native scripting (psql, sqlite3, SQL*Plus)
    antisocial network
    61
    Wednesday, May 22, 13

    View full-size slide

  250. Sqitch Philosophy
    No opinions
    Native scripting (psql, sqlite3, SQL*Plus)
    Cross-project dependency resolution
    antisocial network
    61
    Wednesday, May 22, 13

    View full-size slide

  251. Sqitch Philosophy
    No opinions
    Native scripting (psql, sqlite3, SQL*Plus)
    Cross-project dependency resolution
    Distribution bundling
    antisocial network
    61
    Wednesday, May 22, 13

    View full-size slide

  252. Sqitch Philosophy
    No opinions
    Native scripting (psql, sqlite3, SQL*Plus)
    Cross-project dependency resolution
    Distribution bundling
    Integrated verification testing
    antisocial network
    61
    Wednesday, May 22, 13

    View full-size slide

  253. Sqitch Philosophy
    No opinions
    Native scripting (psql, sqlite3, SQL*Plus)
    Cross-project dependency resolution
    Distribution bundling
    Integrated verification testing
    No numbering
    antisocial network
    61
    Wednesday, May 22, 13

    View full-size slide

  254. Sqitch Philosophy
    No opinions
    Native scripting (psql, sqlite3, SQL*Plus)
    Cross-project dependency resolution
    Distribution bundling
    Integrated verification testing
    No numbering
    Reliable sequential deployment ordering
    antisocial network
    61
    Wednesday, May 22, 13

    View full-size slide

  255. SHAzbat
    antisocial network
    62
    Wednesday, May 22, 13

    View full-size slide

  256. SHAzbat
    SHA1 ID for every object
    antisocial network
    62
    Wednesday, May 22, 13

    View full-size slide

  257. SHAzbat
    SHA1 ID for every object
    Stolen from Git
    antisocial network
    62
    Wednesday, May 22, 13

    View full-size slide

  258. SHAzbat
    SHA1 ID for every object
    Stolen from Git
    change, tag, deploy, revert, verify
    antisocial network
    62
    Wednesday, May 22, 13

    View full-size slide

  259. SHAzbat
    SHA1 ID for every object
    Stolen from Git
    change, tag, deploy, revert, verify
    Hashed change text includes:
    antisocial network
    62
    Wednesday, May 22, 13

    View full-size slide

  260. SHAzbat
    SHA1 ID for every object
    Stolen from Git
    change, tag, deploy, revert, verify
    Hashed change text includes:
    Project
    antisocial network
    62
    Wednesday, May 22, 13

    View full-size slide

  261. SHAzbat
    SHA1 ID for every object
    Stolen from Git
    change, tag, deploy, revert, verify
    Hashed change text includes:
    Project
    Name
    antisocial network
    62
    Wednesday, May 22, 13

    View full-size slide

  262. SHAzbat
    SHA1 ID for every object
    Stolen from Git
    change, tag, deploy, revert, verify
    Hashed change text includes:
    Project
    Name
    Parent ID
    antisocial network
    62
    Wednesday, May 22, 13

    View full-size slide

  263. SHAzbat
    SHA1 ID for every object
    Stolen from Git
    change, tag, deploy, revert, verify
    Hashed change text includes:
    Project
    Name
    Parent ID
    Planner…
    antisocial network
    62
    Wednesday, May 22, 13

    View full-size slide

  264. SHAsome
    >
    63
    Wednesday, May 22, 13

    View full-size slide

  265. SHAsome
    >
    ""sqitch"show"change"@HEAD
    project"pgxn_manager
    uri"https://github.com/pgxn/pgxn*manager.git
    change"distview
    parent"39d7e105cb00e1798cfabb45f4b74cbc14a82513
    planner"David"E."Wheeler"
    date"2013*02*08T23:51:19Z
    requires
    ""+"roles
    ""+"pgxn_core:types
    ""+"distributions
    ""+"extensions
    Adds"the"filters"table.
    >
    63
    Wednesday, May 22, 13

    View full-size slide

  266. SHAsome
    >
    ""sqitch"show"change"@HEAD
    project"pgxn_manager
    uri"https://github.com/pgxn/pgxn*manager.git
    change"distview
    parent"39d7e105cb00e1798cfabb45f4b74cbc14a82513
    planner"David"E."Wheeler"
    date"2013*02*08T23:51:19Z
    requires
    ""+"roles
    ""+"pgxn_core:types
    ""+"distributions
    ""+"extensions
    Adds"the"filters"table.
    >
    Symbolic
    Sqitch tag
    63
    Wednesday, May 22, 13

    View full-size slide

  267. SHAsome
    >
    ""sqitch"show"change"@HEAD
    project"pgxn_manager
    uri"https://github.com/pgxn/pgxn*manager.git
    change"distview
    parent"39d7e105cb00e1798cfabb45f4b74cbc14a82513
    planner"David"E."Wheeler"
    date"2013*02*08T23:51:19Z
    requires
    ""+"roles
    ""+"pgxn_core:types
    ""+"distributions
    ""+"extensions
    Adds"the"filters"table.
    >
    63
    Wednesday, May 22, 13

    View full-size slide

  268. SHAsome
    >
    ""sqitch"show"change"@HEAD
    project"pgxn_manager
    uri"https://github.com/pgxn/pgxn*manager.git
    change"distview
    parent"39d7e105cb00e1798cfabb45f4b74cbc14a82513
    planner"David"E."Wheeler"
    date"2013*02*08T23:51:19Z
    requires
    ""+"roles
    ""+"pgxn_core:types
    ""+"distributions
    ""+"extensions
    Adds"the"filters"table.
    >
    63
    Wednesday, May 22, 13

    View full-size slide

  269. SHAsome
    >
    ""sqitch"show"change"@HEAD
    project"pgxn_manager
    uri"https://github.com/pgxn/pgxn*manager.git
    change"distview
    parent"39d7e105cb00e1798cfabb45f4b74cbc14a82513
    planner"David"E."Wheeler"
    date"2013*02*08T23:51:19Z
    requires
    ""+"roles
    ""+"pgxn_core:types
    ""+"distributions
    ""+"extensions
    Adds"the"filters"table.
    >
    63
    Wednesday, May 22, 13

    View full-size slide

  270. SHAsome
    >
    ""sqitch"show"change"@HEAD
    project"pgxn_manager
    uri"https://github.com/pgxn/pgxn*manager.git
    change"distview
    parent"39d7e105cb00e1798cfabb45f4b74cbc14a82513
    planner"David"E."Wheeler"
    date"2013*02*08T23:51:19Z
    requires
    ""+"roles
    ""+"pgxn_core:types
    ""+"distributions
    ""+"extensions
    Adds"the"filters"table.
    >
    63
    Wednesday, May 22, 13

    View full-size slide

  271. SHAsome
    >
    ""sqitch"show"change"@HEAD
    project"pgxn_manager
    uri"https://github.com/pgxn/pgxn*manager.git
    change"distview
    parent"39d7e105cb00e1798cfabb45f4b74cbc14a82513
    planner"David"E."Wheeler"
    date"2013*02*08T23:51:19Z
    requires
    ""+"roles
    ""+"pgxn_core:types
    ""+"distributions
    ""+"extensions
    Adds"the"filters"table.
    >
    63
    Wednesday, May 22, 13

    View full-size slide

  272. SHAsome
    >
    ""sqitch"show"change"@HEAD
    project"pgxn_manager
    uri"https://github.com/pgxn/pgxn*manager.git
    change"distview
    parent"39d7e105cb00e1798cfabb45f4b74cbc14a82513
    planner"David"E."Wheeler"
    date"2013*02*08T23:51:19Z
    requires
    ""+"roles
    ""+"pgxn_core:types
    ""+"distributions
    ""+"extensions
    Adds"the"filters"table.
    >
    63
    Wednesday, May 22, 13

    View full-size slide

  273. SHAsome
    >
    ""sqitch"show"change"@HEAD
    project"pgxn_manager
    uri"https://github.com/pgxn/pgxn*manager.git
    change"distview
    parent"39d7e105cb00e1798cfabb45f4b74cbc14a82513
    planner"David"E."Wheeler"
    date"2013*02*08T23:51:19Z
    requires
    ""+"roles
    ""+"pgxn_core:types
    ""+"distributions
    ""+"extensions
    Adds"the"filters"table.
    >
    63
    Wednesday, May 22, 13

    View full-size slide

  274. SHAsome
    >
    ""sqitch"show"change"@HEAD
    project"pgxn_manager
    uri"https://github.com/pgxn/pgxn*manager.git
    change"distview
    parent"39d7e105cb00e1798cfabb45f4b74cbc14a82513
    planner"David"E."Wheeler"
    date"2013*02*08T23:51:19Z
    requires
    ""+"roles
    ""+"pgxn_core:types
    ""+"distributions
    ""+"extensions
    Adds"the"filters"table.
    >
    63
    Wednesday, May 22, 13

    View full-size slide

  275. SHAsome
    >
    ""sqitch"show"change"@HEAD
    project"pgxn_manager
    uri"https://github.com/pgxn/pgxn*manager.git
    change"distview
    parent"39d7e105cb00e1798cfabb45f4b74cbc14a82513
    planner"David"E."Wheeler"
    date"2013*02*08T23:51:19Z
    requires
    ""+"roles
    ""+"pgxn_core:types
    ""+"distributions
    ""+"extensions
    Adds"the"filters"table.
    >
    63
    Wednesday, May 22, 13

    View full-size slide

  276. SHApay!
    antisocial network
    64
    Wednesday, May 22, 13

    View full-size slide

  277. SHApay!
    Each change (except first) includes parent
    antisocial network
    64
    Wednesday, May 22, 13

    View full-size slide

  278. SHApay!
    Each change (except first) includes parent
    Can trace from any change to the beginning
    antisocial network
    64
    Wednesday, May 22, 13

    View full-size slide

  279. SHApay!
    Each change (except first) includes parent
    Can trace from any change to the beginning
    Change tampering (corruption) detectable
    antisocial network
    64
    Wednesday, May 22, 13

    View full-size slide

  280. SHApay!
    Each change (except first) includes parent
    Can trace from any change to the beginning
    Change tampering (corruption) detectable
    Because the hash will be wrong
    antisocial network
    64
    Wednesday, May 22, 13

    View full-size slide

  281. SHApay!
    Each change (except first) includes parent
    Can trace from any change to the beginning
    Change tampering (corruption) detectable
    Because the hash will be wrong
    Stole Linus Torvalds’s “greatest invention”
    antisocial network
    64
    Wednesday, May 22, 13

    View full-size slide

  282. Sqitch Features
    antisocial network
    65
    Wednesday, May 22, 13

    View full-size slide

  283. Sqitch Features
    Reduced duplication
    antisocial network
    65
    Wednesday, May 22, 13

    View full-size slide

  284. Sqitch Features
    Reduced duplication
    Built-in configuration
    antisocial network
    65
    Wednesday, May 22, 13

    View full-size slide

  285. Sqitch Features
    Reduced duplication
    Built-in configuration
    Iterative development
    antisocial network
    65
    Wednesday, May 22, 13

    View full-size slide

  286. Sqitch Features
    Reduced duplication
    Built-in configuration
    Iterative development
    Deployment planning
    antisocial network
    65
    Wednesday, May 22, 13

    View full-size slide

  287. Sqitch Features
    Reduced duplication
    Built-in configuration
    Iterative development
    Deployment planning
    Git-style interface
    antisocial network
    65
    Wednesday, May 22, 13

    View full-size slide

  288. Sqitch Features
    Reduced duplication
    Built-in configuration
    Iterative development
    Deployment planning
    Git-style interface
    Deployment tagging
    antisocial network
    65
    Wednesday, May 22, 13

    View full-size slide

  289. antisocial network
    Your Turn
    66
    Wednesday, May 22, 13

    View full-size slide

  290. antisocial network
    Your Turn
    Configure Sqitch
    66
    Wednesday, May 22, 13

    View full-size slide

  291. antisocial network
    Your Turn
    Configure Sqitch
    Initialize project
    66
    Wednesday, May 22, 13

    View full-size slide

  292. antisocial network
    Your Turn
    Configure Sqitch
    Initialize project
    Add appschema change
    66
    Wednesday, May 22, 13

    View full-size slide

  293. antisocial network
    Your Turn
    Configure Sqitch
    Initialize project
    Add appschema change
    Deploy/Revert
    66
    Wednesday, May 22, 13

    View full-size slide

  294. antisocial network
    Your Turn
    Configure Sqitch
    Initialize project
    Add appschema change
    Deploy/Revert
    Commit/push
    66
    Wednesday, May 22, 13

    View full-size slide

  295. antisocial network
    Your Turn
    Configure Sqitch
    Initialize project
    Add appschema change
    Deploy/Revert
    Commit/push
    https:/
    /github.com/
    theory/agile-flipr.git
    66
    Wednesday, May 22, 13

    View full-size slide

  296. Add Tests
    >
    67
    Wednesday, May 22, 13

    View full-size slide

  297. Add Tests
    >
    ""mkdir"test
    >
    67
    Wednesday, May 22, 13

    View full-size slide

  298. Add Tests
    >
    ""mkdir"test
    >
    ""emacs"test/appschema.sql
    >
    67
    Wednesday, May 22, 13

    View full-size slide

  299. test/appschema.
    pgTAP Basics
    68
    Wednesday, May 22, 13

    View full-size slide

  300. test/appschema.
    pgTAP Basics
    SET"client_min_messages"TO"warning;
    CREATE"EXTENSION"IF"NOT"EXISTS"pgtap;
    RESET"client_min_messages;
    BEGIN;
    **"Plan"the"tests.
    SELECT"plan(1);
    SELECT"has_schema('nada');
    **"Clean"up.
    SELECT"finish();
    ROLLBACK;
    68
    Wednesday, May 22, 13

    View full-size slide

  301. test/appschema.
    pgTAP Basics
    SET"client_min_messages"TO"warning;
    CREATE"EXTENSION"IF"NOT"EXISTS"pgtap;
    RESET"client_min_messages;
    BEGIN;
    **"Plan"the"tests.
    SELECT"plan(1);
    SELECT"has_schema('nada');
    **"Clean"up.
    SELECT"finish();
    ROLLBACK;
    68
    Wednesday, May 22, 13

    View full-size slide

  302. test/appschema.
    pgTAP Basics
    SET"client_min_messages"TO"warning;
    CREATE"EXTENSION"IF"NOT"EXISTS"pgtap;
    RESET"client_min_messages;
    BEGIN;
    **"Plan"the"tests.
    SELECT"plan(1);
    SELECT"has_schema('nada');
    **"Clean"up.
    SELECT"finish();
    ROLLBACK;
    68
    Wednesday, May 22, 13

    View full-size slide

  303. test/appschema.
    pgTAP Basics
    SET"client_min_messages"TO"warning;
    CREATE"EXTENSION"IF"NOT"EXISTS"pgtap;
    RESET"client_min_messages;
    BEGIN;
    **"Plan"the"tests.
    SELECT"plan(1);
    SELECT"has_schema('nada');
    **"Clean"up.
    SELECT"finish();
    ROLLBACK;
    68
    Wednesday, May 22, 13

    View full-size slide

  304. test/appschema.
    pgTAP Basics
    SET"client_min_messages"TO"warning;
    CREATE"EXTENSION"IF"NOT"EXISTS"pgtap;
    RESET"client_min_messages;
    BEGIN;
    **"Plan"the"tests.
    SELECT"plan(1);
    SELECT"has_schema('nada');
    **"Clean"up.
    SELECT"finish();
    ROLLBACK;
    68
    Wednesday, May 22, 13

    View full-size slide

  305. test/appschema.
    pgTAP Basics
    SET"client_min_messages"TO"warning;
    CREATE"EXTENSION"IF"NOT"EXISTS"pgtap;
    RESET"client_min_messages;
    BEGIN;
    **"Plan"the"tests.
    SELECT"plan(1);
    SELECT"has_schema('nada');
    **"Clean"up.
    SELECT"finish();
    ROLLBACK;
    68
    Wednesday, May 22, 13

    View full-size slide

  306. test/appschema.
    pgTAP Basics
    SET"client_min_messages"TO"warning;
    CREATE"EXTENSION"IF"NOT"EXISTS"pgtap;
    RESET"client_min_messages;
    BEGIN;
    **"Plan"the"tests.
    SELECT"plan(1);
    SELECT"has_schema('nada');
    **"Clean"up.
    SELECT"finish();
    ROLLBACK;
    68
    Wednesday, May 22, 13

    View full-size slide

  307. >
    Run the Test
    69
    Wednesday, May 22, 13

    View full-size slide

  308. >
    Run the Test
    ""pg_prove"*v"*d"flipr_test"test/appschema.sql
    test/appschema.sql".."
    1..1
    not"ok"1"*"Schema"nada"should"exist
    #"Failed"test"1:""Schema"nada"should"exist"
    #"Looks"like"you"failed"1"test"of"1
    Failed"1/1"subtests"
    Test"Summary"Report
    *******************
    test/appschema.sql"(Wstat:"0"Tests:"1"Failed:"1)
    ""Failed"test:""1
    Files=1,"Tests=1,""0"wallclock"secs
    Result:"FAIL
    69
    Wednesday, May 22, 13

    View full-size slide

  309. >
    Run the Test
    ""pg_prove"*v"*d"flipr_test"test/appschema.sql
    test/appschema.sql".."
    1..1
    not"ok"1"*"Schema"nada"should"exist
    #"Failed"test"1:""Schema"nada"should"exist"
    #"Looks"like"you"failed"1"test"of"1
    Failed"1/1"subtests"
    Test"Summary"Report
    *******************
    test/appschema.sql"(Wstat:"0"Tests:"1"Failed:"1)
    ""Failed"test:""1
    Files=1,"Tests=1,""0"wallclock"secs
    Result:"FAIL
    69
    Wednesday, May 22, 13

    View full-size slide

  310. >
    Run the Test
    ""pg_prove"*v"*d"flipr_test"test/appschema.sql
    test/appschema.sql".."
    1..1
    not"ok"1"*"Schema"nada"should"exist
    #"Failed"test"1:""Schema"nada"should"exist"
    #"Looks"like"you"failed"1"test"of"1
    Failed"1/1"subtests"
    Test"Summary"Report
    *******************
    test/appschema.sql"(Wstat:"0"Tests:"1"Failed:"1)
    ""Failed"test:""1
    Files=1,"Tests=1,""0"wallclock"secs
    Result:"FAIL
    69
    Wednesday, May 22, 13

    View full-size slide

  311. First Pass
    >
    70
    Wednesday, May 22, 13

    View full-size slide

  312. ""perl"*i"*pe"'s/nada/flipr/'"test/appschema.sql
    >
    First Pass
    >
    70
    Wednesday, May 22, 13

    View full-size slide

  313. ""perl"*i"*pe"'s/nada/flipr/'"test/appschema.sql
    >
    First Pass
    >
    >"pg_prove"*v"*d"flipr_test"test/appschema.sql""""
    "
    test/appschema.sql".."
    1..1
    ok"1"*"Schema"flipr"should"exist
    ok
    All"tests"successful.
    Files=1,"Tests=1,""0"wallclock"secs
    Result:"PASS
    70
    Wednesday, May 22, 13

    View full-size slide

  314. ""perl"*i"*pe"'s/nada/flipr/'"test/appschema.sql
    >
    First Pass
    W00t!
    >
    >"pg_prove"*v"*d"flipr_test"test/appschema.sql""""
    "
    test/appschema.sql".."
    1..1
    ok"1"*"Schema"flipr"should"exist
    ok
    All"tests"successful.
    Files=1,"Tests=1,""0"wallclock"secs
    Result:"PASS
    70
    Wednesday, May 22, 13

    View full-size slide

  315. Pass it On
    >
    71
    Wednesday, May 22, 13

    View full-size slide

  316. ""git"add".
    >
    Pass it On
    >
    71
    Wednesday, May 22, 13

    View full-size slide

  317. ""git"add".
    >
    ""git"commit"*m"'Add"appschema"test.'
    [master"e46bdf9]"Add"appschema"test.
    "1"file"changed,"14"insertions(+)
    "create"mode"100644"test/appschema.sql
    >
    Pass it On
    >
    71
    Wednesday, May 22, 13

    View full-size slide

  318. ""git"add".
    >
    ""git"commit"*m"'Add"appschema"test.'
    [master"e46bdf9]"Add"appschema"test.
    "1"file"changed,"14"insertions(+)
    "create"mode"100644"test/appschema.sql
    >
    Pass it On
    >
    ""git"push
    Counting"objects:"5,"done.
    Delta"compression"using"up"to"4"threads.
    Compressing"objects:"100%"(3/3),"done.
    Writing"objects:"100%"(4/4),"487"bytes,"done.
    Total"4"(delta"1),"reused"0"(delta"0)
    To"../flipr*remote
    """795697f..e46bdf9""master"*>"master
    >
    71
    Wednesday, May 22, 13

    View full-size slide

  319. OMG TAP WTF?
    The Test Anything Protocol (TAP) is a
    protocol to allow communication between
    unit tests and a test harness. It allows
    individual tests (TAP producers) to
    communicate test results to the testing
    harness in a language-agnostic way.
    —Wikipedia
    72
    Wednesday, May 22, 13

    View full-size slide

  320. What is TAP?
    antisocial network
    73
    Wednesday, May 22, 13

    View full-size slide

  321. What does that mean in practice?
    What is TAP?
    antisocial network
    73
    Wednesday, May 22, 13

    View full-size slide

  322. What does that mean in practice?
    Test output easy to interpret
    What is TAP?
    antisocial network
    73
    Wednesday, May 22, 13

    View full-size slide

  323. What does that mean in practice?
    Test output easy to interpret
    By humans
    What is TAP?
    antisocial network
    73
    Wednesday, May 22, 13

    View full-size slide

  324. What does that mean in practice?
    Test output easy to interpret
    By humans
    By computers
    What is TAP?
    antisocial network
    73
    Wednesday, May 22, 13

    View full-size slide

  325. What does that mean in practice?
    Test output easy to interpret
    By humans
    By computers
    pg_prove (the harness)
    What is TAP?
    antisocial network
    73
    Wednesday, May 22, 13

    View full-size slide

  326. What does that mean in practice?
    Test output easy to interpret
    By humans
    By computers
    pg_prove (the harness)
    By aliens
    What is TAP?
    antisocial network
    73
    Wednesday, May 22, 13

    View full-size slide

  327. What does that mean in practice?
    Test output easy to interpret
    By humans
    By computers
    pg_prove (the harness)
    By aliens
    By gum!
    What is TAP?
    antisocial network
    73
    Wednesday, May 22, 13

    View full-size slide

  328. What’s the plan, man?
    antisocial network
    74
    Wednesday, May 22, 13

    View full-size slide

  329. What’s the plan, man?
    Includes Test controls:
    antisocial network
    74
    Wednesday, May 22, 13

    View full-size slide

  330. What’s the plan, man?
    Includes Test controls:
    plan() — How many tests?
    antisocial network
    74
    Wednesday, May 22, 13

    View full-size slide

  331. What’s the plan, man?
    Includes Test controls:
    plan() — How many tests?
    no_plan() — Unknown number of tests
    antisocial network
    74
    Wednesday, May 22, 13

    View full-size slide

  332. What’s the plan, man?
    Includes Test controls:
    plan() — How many tests?
    no_plan() — Unknown number of tests
    diag() — Diagnostic output
    antisocial network
    74
    Wednesday, May 22, 13

    View full-size slide

  333. What’s the plan, man?
    Includes Test controls:
    plan() — How many tests?
    no_plan() — Unknown number of tests
    diag() — Diagnostic output
    finish() — Test finished, report!
    antisocial network
    74
    Wednesday, May 22, 13

    View full-size slide

  334. Scalarly
    antisocial network
    75
    Wednesday, May 22, 13

    View full-size slide

  335. Scalarly
    Includes simple scalar test functions:
    antisocial network
    75
    Wednesday, May 22, 13

    View full-size slide

  336. Scalarly
    Includes simple scalar test functions:
    ok() — Boolean
    antisocial network
    75
    Wednesday, May 22, 13

    View full-size slide

  337. Scalarly
    Includes simple scalar test functions:
    ok() — Boolean
    is() — Value comparison
    antisocial network
    75
    Wednesday, May 22, 13

    View full-size slide

  338. Scalarly
    Includes simple scalar test functions:
    ok() — Boolean
    is() — Value comparison
    isnt() — NOT is()
    antisocial network
    75
    Wednesday, May 22, 13

    View full-size slide

  339. Scalarly
    Includes simple scalar test functions:
    ok() — Boolean
    is() — Value comparison
    isnt() — NOT is()
    cmp_ok() — Compare with specific operator
    antisocial network
    75
    Wednesday, May 22, 13

    View full-size slide

  340. Scalarly
    Includes simple scalar test functions:
    ok() — Boolean
    is() — Value comparison
    isnt() — NOT is()
    cmp_ok() — Compare with specific operator
    matches() — Regex comparison
    antisocial network
    75
    Wednesday, May 22, 13

    View full-size slide

  341. Scalarly
    Includes simple scalar test functions:
    ok() — Boolean
    is() — Value comparison
    isnt() — NOT is()
    cmp_ok() — Compare with specific operator
    matches() — Regex comparison
    imatches() — Case-insensitive regex comparison
    antisocial network
    75
    Wednesday, May 22, 13

    View full-size slide

  342. It’s All Relative
    antisocial network
    76
    Wednesday, May 22, 13

    View full-size slide

  343. It’s All Relative
    Includes functions for testing relations:
    antisocial network
    76
    Wednesday, May 22, 13

    View full-size slide

  344. It’s All Relative
    Includes functions for testing relations:
    results_eq() — Ordered results
    antisocial network
    76
    Wednesday, May 22, 13

    View full-size slide

  345. It’s All Relative
    Includes functions for testing relations:
    results_eq() — Ordered results
    set_eq() — Set of values
    antisocial network
    76
    Wednesday, May 22, 13

    View full-size slide

  346. It’s All Relative
    Includes functions for testing relations:
    results_eq() — Ordered results
    set_eq() — Set of values
    bag_eq() — Bag of values
    antisocial network
    76
    Wednesday, May 22, 13

    View full-size slide

  347. It’s All Relative
    Includes functions for testing relations:
    results_eq() — Ordered results
    set_eq() — Set of values
    bag_eq() — Bag of values
    row_eq() — Single row
    antisocial network
    76
    Wednesday, May 22, 13

    View full-size slide

  348. It’s All Relative
    Includes functions for testing relations:
    results_eq() — Ordered results
    set_eq() — Set of values
    bag_eq() — Bag of values
    row_eq() — Single row
    is_empty() — No results
    antisocial network
    76
    Wednesday, May 22, 13

    View full-size slide

  349. I’m Okay, You’re Okay
    antisocial network
    77
    Wednesday, May 22, 13

    View full-size slide

  350. I’m Okay, You’re Okay
    throws_ok() — Throws an exception
    antisocial network
    77
    Wednesday, May 22, 13

    View full-size slide

  351. I’m Okay, You’re Okay
    throws_ok() — Throws an exception
    throws_like() — Exception matches regex
    antisocial network
    77
    Wednesday, May 22, 13

    View full-size slide

  352. I’m Okay, You’re Okay
    throws_ok() — Throws an exception
    throws_like() — Exception matches regex
    skip() — Skip a subset of tests
    antisocial network
    77
    Wednesday, May 22, 13

    View full-size slide

  353. I’m Okay, You’re Okay
    throws_ok() — Throws an exception
    throws_like() — Exception matches regex
    skip() — Skip a subset of tests
    todo() — Expect subset of tests to fail
    antisocial network
    77
    Wednesday, May 22, 13

    View full-size slide

  354. Schematics
    antisocial network
    78
    Wednesday, May 22, 13

    View full-size slide

  355. Schematics
    has_table(), has_view(), has_function(), etc.
    antisocial network
    78
    Wednesday, May 22, 13

    View full-size slide

  356. Schematics
    has_table(), has_view(), has_function(), etc.
    columns_are(), has_pk(), fk_ok(), etc.
    antisocial network
    78
    Wednesday, May 22, 13

    View full-size slide

  357. Schematics
    has_table(), has_view(), has_function(), etc.
    columns_are(), has_pk(), fk_ok(), etc.
    col_type_is(), col_not_null(), col_default_is()
    antisocial network
    78
    Wednesday, May 22, 13

    View full-size slide

  358. Schematics
    has_table(), has_view(), has_function(), etc.
    columns_are(), has_pk(), fk_ok(), etc.
    col_type_is(), col_not_null(), col_default_is()
    So, so much more…
    antisocial network
    78
    Wednesday, May 22, 13

    View full-size slide

  359. Other Features and Topics
    antisocial network
    79
    Wednesday, May 22, 13

    View full-size slide

  360. Other Features and Topics
    xUnit-Style testing
    antisocial network
    79
    Wednesday, May 22, 13

    View full-size slide

  361. Other Features and Topics
    xUnit-Style testing
    Test-Driven development
    antisocial network
    79
    Wednesday, May 22, 13

    View full-size slide

  362. Other Features and Topics
    xUnit-Style testing
    Test-Driven development
    Integration with Perl unit tests
    antisocial network
    79
    Wednesday, May 22, 13

    View full-size slide

  363. Other Features and Topics
    xUnit-Style testing
    Test-Driven development
    Integration with Perl unit tests
    Integration with pg_regress
    antisocial network
    79
    Wednesday, May 22, 13

    View full-size slide

  364. Other Features and Topics
    xUnit-Style testing
    Test-Driven development
    Integration with Perl unit tests
    Integration with pg_regress
    Negative assertions
    antisocial network
    79
    Wednesday, May 22, 13

    View full-size slide

  365. Other Features and Topics
    xUnit-Style testing
    Test-Driven development
    Integration with Perl unit tests
    Integration with pg_regress
    Negative assertions
    Roles and privileges assertions
    antisocial network
    79
    Wednesday, May 22, 13

    View full-size slide

  366. Other Features and Topics
    xUnit-Style testing
    Test-Driven development
    Integration with Perl unit tests
    Integration with pg_regress
    Negative assertions
    Roles and privileges assertions
    http:/
    /pgtap.org/
    antisocial network
    79
    Wednesday, May 22, 13

    View full-size slide

  367. Other Features and Topics
    xUnit-Style testing
    Test-Driven development
    Integration with Perl unit tests
    Integration with pg_regress
    Negative assertions
    Roles and privileges assertions
    http:/
    /pgtap.org/
    http:/
    /pgxn.org/extension/pgtap/
    antisocial network
    79
    Wednesday, May 22, 13

    View full-size slide

  368. Other Features and Topics
    xUnit-Style testing
    Test-Driven development
    Integration with Perl unit tests
    Integration with pg_regress
    Negative assertions
    Roles and privileges assertions
    http:/
    /pgtap.org/
    http:/
    /pgxn.org/extension/pgtap/
    antisocial network
    79
    Wednesday, May 22, 13

    View full-size slide

  369. antisocial network
    Let’s do it!
    80
    Wednesday, May 22, 13

    View full-size slide

  370. antisocial network
    Let’s do it!
    Create appschema test
    80
    Wednesday, May 22, 13

    View full-size slide

  371. antisocial network
    Let’s do it!
    Create appschema test
    Use pgTAP
    80
    Wednesday, May 22, 13

    View full-size slide

  372. antisocial network
    Let’s do it!
    Create appschema test
    Use pgTAP
    Run test with pg_prove
    80
    Wednesday, May 22, 13

    View full-size slide

  373. antisocial network
    Let’s do it!
    Create appschema test
    Use pgTAP
    Run test with pg_prove
    Make it fail
    80
    Wednesday, May 22, 13

    View full-size slide

  374. antisocial network
    Let’s do it!
    Create appschema test
    Use pgTAP
    Run test with pg_prove
    Make it fail
    Make it pass!
    80
    Wednesday, May 22, 13

    View full-size slide

  375. antisocial network
    Let’s do it!
    Create appschema test
    Use pgTAP
    Run test with pg_prove
    Make it fail
    Make it pass!
    Commit/Push
    80
    Wednesday, May 22, 13

    View full-size slide

  376. antisocial network
    Let’s do it!
    Create appschema test
    Use pgTAP
    Run test with pg_prove
    Make it fail
    Make it pass!
    Commit/Push
    https:/
    /github.com/
    theory/agile-flipr.git
    80
    Wednesday, May 22, 13

    View full-size slide

  377. Let’s talk about…
    antisocial network
    antisocial network
    antisocial network
    81
    Wednesday, May 22, 13

    View full-size slide

  378. antisocial network
    antisocial network
    82
    Wednesday, May 22, 13

    View full-size slide

  379. TDD
    antisocial network
    antisocial network
    82
    Wednesday, May 22, 13

    View full-size slide

  380. 83
    Wednesday, May 22, 13

    View full-size slide

  381. “The act of writing a unit test is
    more an act of design than of
    verification. It is also more an
    act of documentation than of
    verification. The act of writing
    a unit test closes a remarkable
    number of feedback loops, the
    least of which is the one per–
    taining to the verification of
    function.”
    —Robert C. Martin
    83
    Wednesday, May 22, 13

    View full-size slide

  382. antisocial network
    84
    Wednesday, May 22, 13

    View full-size slide

  383. TDD is an act
    of design.
    antisocial network
    84
    Wednesday, May 22, 13

    View full-size slide

  384. antisocial network
    85
    Wednesday, May 22, 13

    View full-size slide

  385. TDD is an act of
    documentation.
    antisocial network
    85
    Wednesday, May 22, 13

    View full-size slide

  386. antisocial network
    86
    Wednesday, May 22, 13

    View full-size slide

  387. Okay.
    antisocial network
    86
    Wednesday, May 22, 13

    View full-size slide

  388. antisocial network
    87
    Wednesday, May 22, 13

    View full-size slide

  389. But…
    antisocial network
    87
    Wednesday, May 22, 13

    View full-size slide

  390. Database Design
    antisocial network
    88
    Wednesday, May 22, 13

    View full-size slide

  391. Database Design
    Specify requirements
    antisocial network
    88
    Wednesday, May 22, 13

    View full-size slide

  392. Database Design
    Specify requirements
    Implement schema design
    antisocial network
    88
    Wednesday, May 22, 13

    View full-size slide

  393. Database Design
    Specify requirements
    Implement schema design
    Program applications
    antisocial network
    88
    Wednesday, May 22, 13

    View full-size slide

  394. Database Design
    Specify requirements
    Implement schema design
    Program applications
    QA
    antisocial network
    88
    Wednesday, May 22, 13

    View full-size slide

  395. Database Design
    Specify requirements
    Implement schema design
    Program applications
    QA
    Big jump!
    antisocial network
    88
    Wednesday, May 22, 13

    View full-size slide

  396. Database Design
    Specify requirements
    Implement schema design
    Program applications
    QA
    Pricy to fix
    antisocial network
    88
    Wednesday, May 22, 13

    View full-size slide

  397. Database Design
    Specify requirements
    Implement schema design
    Program applications
    QA
    Never mind bureaucracy of DBA department
    antisocial network
    88
    Wednesday, May 22, 13

    View full-size slide

  398. Why TDDD
    antisocial network
    89
    Wednesday, May 22, 13

    View full-size slide

  399. Why TDDD
    Ensure data quality
    antisocial network
    89
    Wednesday, May 22, 13

    View full-size slide

  400. Why TDDD
    Ensure data quality
    Data is a core asset
    antisocial network
    89
    Wednesday, May 22, 13

    View full-size slide

  401. Why TDDD
    Ensure data quality
    Data is a core asset
    Validate business rules
    antisocial network
    89
    Wednesday, May 22, 13

    View full-size slide

  402. Why TDDD
    Ensure data quality
    Data is a core asset
    Validate business rules
    Stored procedures
    antisocial network
    89
    Wednesday, May 22, 13

    View full-size slide

  403. Why TDDD
    Ensure data quality
    Data is a core asset
    Validate business rules
    Stored procedures
    Triggers
    antisocial network
    89
    Wednesday, May 22, 13

    View full-size slide

  404. Why TDDD
    Ensure data quality
    Data is a core asset
    Validate business rules
    Stored procedures
    Triggers
    Views
    antisocial network
    89
    Wednesday, May 22, 13

    View full-size slide

  405. Why TDDD
    antisocial network
    90
    Wednesday, May 22, 13

    View full-size slide

  406. Why TDDD
    Identify defects early
    antisocial network
    90
    Wednesday, May 22, 13

    View full-size slide

  407. Why TDDD
    Identify defects early
    …and often!
    antisocial network
    90
    Wednesday, May 22, 13

    View full-size slide

  408. Why TDDD
    Identify defects early
    …and often!
    Create design iteratively
    antisocial network
    90
    Wednesday, May 22, 13

    View full-size slide

  409. Why TDDD
    Identify defects early
    …and often!
    Create design iteratively
    Evolutionarily
    antisocial network
    90
    Wednesday, May 22, 13

    View full-size slide

  410. Why TDDD
    Identify defects early
    …and often!
    Create design iteratively
    Evolutionarily
    Validate refactorings
    antisocial network
    90
    Wednesday, May 22, 13

    View full-size slide

  411. Why TDDD
    Identify defects early
    …and often!
    Create design iteratively
    Evolutionarily
    Validate refactorings
    Make sure nothing breaks
    antisocial network
    90
    Wednesday, May 22, 13

    View full-size slide

  412. Three Questions
    for Database Professionals
    from
    Scott Ambler
    antisocial network
    91
    Wednesday, May 22, 13

    View full-size slide

  413. 92
    Wednesday, May 22, 13

    View full-size slide

  414. “If you’re implementing code
    in the DB in the form of
    stored procedures, triggers...
    shouldn’t you test that code
    to the same level that you
    test your app code?”
    92
    Wednesday, May 22, 13

    View full-size slide

  415. 93
    Wednesday, May 22, 13

    View full-size slide

  416. “Think of all the data quality
    problems you’ve run into
    over the years. Wouldn't it
    have been nice if someone
    had originally tested and
    discovered those problems
    before you did?”
    93
    Wednesday, May 22, 13

    View full-size slide

  417. 94
    Wednesday, May 22, 13

    View full-size slide

  418. “Wouldn’t it be nice to have
    a test suite to run so that
    you could determine how
    (and if) the DB actually
    works?”
    94
    Wednesday, May 22, 13

    View full-size slide

  419. antisocial network
    95
    Wednesday, May 22, 13

    View full-size slide

  420. Okay.
    antisocial network
    95
    Wednesday, May 22, 13

    View full-size slide

  421. antisocial network
    96
    Wednesday, May 22, 13

    View full-size slide

  422. How?
    antisocial network
    96
    Wednesday, May 22, 13

    View full-size slide

  423. TDD How
    antisocial network
    97
    Wednesday, May 22, 13

    View full-size slide

  424. TDD How
    Ideally separate from app tests
    antisocial network
    97
    Wednesday, May 22, 13

    View full-size slide

  425. TDD How
    Ideally separate from app tests
    May be many apps
    antisocial network
    97
    Wednesday, May 22, 13

    View full-size slide

  426. TDD How
    Ideally separate from app tests
    May be many apps
    DB should present interface to all
    antisocial network
    97
    Wednesday, May 22, 13

    View full-size slide

  427. TDD How
    Ideally separate from app tests
    May be many apps
    DB should present interface to all
    Apps may use different permissions
    antisocial network
    97
    Wednesday, May 22, 13

    View full-size slide

  428. TDD How
    Ideally separate from app tests
    May be many apps
    DB should present interface to all
    Apps may use different permissions
    Ideally use DB test Framework
    antisocial network
    97
    Wednesday, May 22, 13

    View full-size slide

  429. TDD How
    Ideally separate from app tests
    May be many apps
    DB should present interface to all
    Apps may use different permissions
    Ideally use DB test Framework
    Like…pgTAP!
    antisocial network
    97
    Wednesday, May 22, 13

    View full-size slide

  430. Branching Out
    >
    98
    Wednesday, May 22, 13

    View full-size slide

  431. ""git"checkout"*b"users"master
    Switched"to"a"new"branch"'users'
    >
    Branching Out
    >
    98
    Wednesday, May 22, 13

    View full-size slide

  432. ""git"checkout"*b"users"master
    Switched"to"a"new"branch"'users'
    >
    Branching Out
    > Branched off
    from others
    98
    Wednesday, May 22, 13

    View full-size slide

  433. ""git"checkout"*b"users"master
    Switched"to"a"new"branch"'users'
    >
    Branching Out
    >
    >"emacs"test/users.sql
    >
    98
    Wednesday, May 22, 13

    View full-size slide

  434. test/users.sql
    Table For One
    99
    Wednesday, May 22, 13

    View full-size slide

  435. SET"client_min_messages"TO"warning;
    CREATE"EXTENSION"IF"NOT"EXISTS"pgtap;
    RESET"client_min_messages;
    BEGIN;
    SELECT"no_plan();
    SET"search_path"TO"flipr,public;
    SELECT"has_table("'users'");
    SELECT"finish();
    ROLLBACK;
    test/users.sql
    Table For One
    99
    Wednesday, May 22, 13

    View full-size slide

  436. SET"client_min_messages"TO"warning;
    CREATE"EXTENSION"IF"NOT"EXISTS"pgtap;
    RESET"client_min_messages;
    BEGIN;
    SELECT"no_plan();
    SET"search_path"TO"flipr,public;
    SELECT"has_table("'users'");
    SELECT"finish();
    ROLLBACK;
    test/users.sql
    Table For One
    99
    Wednesday, May 22, 13

    View full-size slide

  437. SET"client_min_messages"TO"warning;
    CREATE"EXTENSION"IF"NOT"EXISTS"pgtap;
    RESET"client_min_messages;
    BEGIN;
    SELECT"no_plan();
    SET"search_path"TO"flipr,public;
    SELECT"has_table("'users'");
    SELECT"finish();
    ROLLBACK;
    test/users.sql
    Table For One
    99
    Wednesday, May 22, 13

    View full-size slide

  438. SET"client_min_messages"TO"warning;
    CREATE"EXTENSION"IF"NOT"EXISTS"pgtap;
    RESET"client_min_messages;
    BEGIN;
    SELECT"no_plan();
    SET"search_path"TO"flipr,public;
    SELECT"has_table("'users'");
    SELECT"finish();
    ROLLBACK;
    test/users.sql
    Table For One
    99
    Wednesday, May 22, 13

    View full-size slide

  439. Run ’Em
    >
    100
    Wednesday, May 22, 13

    View full-size slide

  440. ""pg_prove"*d"flipr_test"*v"test/users.sql
    test/users.sql".."
    not"ok"1"*"Table"users"should"exist
    #"Failed"test"1:""Table"users"should"exist"
    1..1
    #"Looks"like"you"failed"1"test"of"1
    Failed"1/1"subtests"
    Test"Summary"Report
    *******************
    test/users.sql"(Wstat:"0"Tests:"1"Failed:"1)
    ""Failed"test:""1
    Files=1,"Tests=1,""0"wallclock"secs
    Result:"FAIL
    >
    Run ’Em
    >
    100
    Wednesday, May 22, 13

    View full-size slide

  441. ""pg_prove"*d"flipr_test"*v"test/users.sql
    test/users.sql".."
    not"ok"1"*"Table"users"should"exist
    #"Failed"test"1:""Table"users"should"exist"
    1..1
    #"Looks"like"you"failed"1"test"of"1
    Failed"1/1"subtests"
    Test"Summary"Report
    *******************
    test/users.sql"(Wstat:"0"Tests:"1"Failed:"1)
    ""Failed"test:""1
    Files=1,"Tests=1,""0"wallclock"secs
    Result:"FAIL
    >
    Run ’Em
    >
    100
    Wednesday, May 22, 13

    View full-size slide

  442. ""pg_prove"*d"flipr_test"*v"test/users.sql
    test/users.sql".."
    not"ok"1"*"Table"users"should"exist
    #"Failed"test"1:""Table"users"should"exist"
    1..1
    #"Looks"like"you"failed"1"test"of"1
    Failed"1/1"subtests"
    Test"Summary"Report
    *******************
    test/users.sql"(Wstat:"0"Tests:"1"Failed:"1)
    ""Failed"test:""1
    Files=1,"Tests=1,""0"wallclock"secs
    Result:"FAIL
    >
    Run ’Em
    As expected.
    >
    100
    Wednesday, May 22, 13

    View full-size slide

  443. Sqitch Dependencies!
    >
    101
    Wednesday, May 22, 13

    View full-size slide

  444. Sqitch Dependencies!
    >
    >"sqitch"add"users"**requires"appschema"\
    ""*n"'Creates"table"to"track"our"users.'
    Created"deploy/users.sql
    Created"revert/users.sql
    Created"verify/users.sql
    Added""users"[appschema]""to"sqitch.plan
    >
    101
    Wednesday, May 22, 13

    View full-size slide

  445. Sqitch Dependencies!
    >
    >"sqitch"add"users"**requires"appschema"\
    ""*n"'Creates"table"to"track"our"users.'
    Created"deploy/users.sql
    Created"revert/users.sql
    Created"verify/users.sql
    Added""users"[appschema]""to"sqitch.plan
    >
    101
    Wednesday, May 22, 13

    View full-size slide

  446. Sqitch Dependencies!
    >
    >"sqitch"add"users"**requires"appschema"\
    ""*n"'Creates"table"to"track"our"users.'
    Created"deploy/users.sql
    Created"revert/users.sql
    Created"verify/users.sql
    Added""users"[appschema]""to"sqitch.plan
    >
    ""emacs"deploy/users.sql
    >
    101
    Wednesday, May 22, 13

    View full-size slide

  447. deploy/users.sq
    deploy/users.sql
    **"XXX"Add"DDLs"here.
    COMMIT;
    **"Deploy"users
    **"requires:"appschema
    BEGIN;
    102
    Wednesday, May 22, 13

    View full-size slide

  448. deploy/users.sq
    deploy/users.sql
    **"XXX"Add"DDLs"here.
    COMMIT;
    **"Deploy"users
    **"requires:"appschema
    BEGIN;
    102
    Wednesday, May 22, 13

    View full-size slide

  449. SET"client_min_messages"="'warning';
    CREATE"TABLE"flipr.users"(
    """"nickname""TEXT
    );
    deploy/users.sq
    deploy/users.sql
    COMMIT;
    **"Deploy"users
    **"requires:"appschema
    BEGIN;
    102
    Wednesday, May 22, 13

    View full-size slide

  450. SET"client_min_messages"="'warning';
    CREATE"TABLE"flipr.users"(
    """"nickname""TEXT
    );
    deploy/users.sq
    deploy/users.sql
    COMMIT;
    **"Deploy"users
    **"requires:"appschema
    BEGIN;
    Bare
    Minimum
    102
    Wednesday, May 22, 13

    View full-size slide

  451. Verily, Users
    >
    >"sqitch"add"users"**requires"appschema"\
    ""*n"'Creates"table"to"track"our"users.'
    Created"deploy/users.sql
    Created"revert/users.sql
    Created"verify/users.sql
    Added""users"[appschema]""to"sqitch.plan
    >"emacs"deploy/users.sql
    >
    103
    Wednesday, May 22, 13

    View full-size slide

  452. Verily, Users
    >
    >"sqitch"add"users"**requires"appschema"\
    ""*n"'Creates"table"to"track"our"users.'
    Created"deploy/users.sql
    Created"revert/users.sql
    Created"verify/users.sql
    Added""users"[appschema]""to"sqitch.plan
    >"emacs"deploy/users.sql
    >
    ""emacs"verify/users.sql
    103
    Wednesday, May 22, 13

    View full-size slide

  453. **"Verify"users
    BEGIN;
    verify/users.sq
    verify/users.sql
    **"XXX"Add"verifications"here.
    ROLLBACK;
    104
    Wednesday, May 22, 13

    View full-size slide

  454. **"Verify"users
    BEGIN;
    SELECT"nickname
    ""FROM"flipr.users
    "WHERE"FALSE;
    verify/users.sq
    verify/users.sql
    ROLLBACK;
    104
    Wednesday, May 22, 13

    View full-size slide

  455. Unusered
    >
    >"sqitch"add"users"**requires"appschema"\
    ""*n"'Creates"table"to"track"our"users.'
    Created"deploy/users.sql
    Created"revert/users.sql
    Created"verify/users.sql
    Added""users"[appschema]""to"sqitch.plan
    >"emacs"deploy/users.sql
    >"emacs"verify/users.sql
    >
    105
    Wednesday, May 22, 13

    View full-size slide

  456. Unusered
    >
    >"sqitch"add"users"**requires"appschema"\
    ""*n"'Creates"table"to"track"our"users.'
    Created"deploy/users.sql
    Created"revert/users.sql
    Created"verify/users.sql
    Added""users"[appschema]""to"sqitch.plan
    >"emacs"deploy/users.sql
    >"emacs"verify/users.sql
    >
    >"emacs"revert/users.sql
    105
    Wednesday, May 22, 13

    View full-size slide

  457. revert/users.sq
    revert/users.sql
    **"Revert"users
    BEGIN;
    COMMIT;
    **"XXX"Add"DDLs"here.
    106
    Wednesday, May 22, 13

    View full-size slide

  458. revert/users.sq
    revert/users.sql
    **"Revert"users
    BEGIN;
    COMMIT;
    DROP"TABLE"flipr.users;
    106
    Wednesday, May 22, 13

    View full-size slide

  459. Make Users
    >
    107
    Wednesday, May 22, 13

    View full-size slide

  460. ""sqitch"deploy
    Deploying"changes"to"flipr_test
    ""+"users".."ok
    >
    Make Users
    >
    107
    Wednesday, May 22, 13

    View full-size slide

  461. ""sqitch"deploy
    Deploying"changes"to"flipr_test
    ""+"users".."ok
    >
    Make Users
    >
    107
    Wednesday, May 22, 13

    View full-size slide

  462. ""sqitch"deploy
    Deploying"changes"to"flipr_test
    ""+"users".."ok
    >
    Make Users
    >
    ""psql"*d"flipr_test"*c"'\d"flipr.users'
    """""Table""flipr.users"
    ""Column""|"Type"|"Modifiers"
    **********+******+***********
    "nickname"|"text"|"
    >
    107
    Wednesday, May 22, 13

    View full-size slide

  463. ""sqitch"deploy
    Deploying"changes"to"flipr_test
    ""+"users".."ok
    >
    Make Users
    >
    ""psql"*d"flipr_test"*c"'\d"flipr.users'
    """""Table""flipr.users"
    ""Column""|"Type"|"Modifiers"
    **********+******+***********
    "nickname"|"text"|"
    >
    ""sqitch"verify
    Verifying"flipr_test
    ""*"appschema".."ok
    ""*"users"......"ok
    Verify"successful
    >
    107
    Wednesday, May 22, 13

    View full-size slide

  464. Make Users
    >
    108
    Wednesday, May 22, 13

    View full-size slide

  465. ""pg_prove"*d"flipr_test"*v"test/users.sql
    test/users.sql".."
    ok"1"*"Table"users"should"exist
    1..1
    ok
    All"tests"successful.
    Files=1,"Tests=1,""1"wallclock"secs
    Result:"PASS
    >
    Make Users
    >
    108
    Wednesday, May 22, 13

    View full-size slide

  466. ""pg_prove"*d"flipr_test"*v"test/users.sql
    test/users.sql".."
    ok"1"*"Table"users"should"exist
    1..1
    ok
    All"tests"successful.
    Files=1,"Tests=1,""1"wallclock"secs
    Result:"PASS
    >
    Make Users
    >
    108
    Wednesday, May 22, 13

    View full-size slide

  467. ""pg_prove"*d"flipr_test"*v"test/users.sql
    test/users.sql".."
    ok"1"*"Table"users"should"exist
    1..1
    ok
    All"tests"successful.
    Files=1,"Tests=1,""1"wallclock"secs
    Result:"PASS
    >
    Make Users
    >
    Woohoo!
    108
    Wednesday, May 22, 13

    View full-size slide

  468. ""pg_prove"*d"flipr_test"*v"test/users.sql
    test/users.sql".."
    ok"1"*"Table"users"should"exist
    1..1
    ok
    All"tests"successful.
    Files=1,"Tests=1,""1"wallclock"secs
    Result:"PASS
    >
    Make Users
    >
    ""emacs"test/users.sql
    >
    108
    Wednesday, May 22, 13

    View full-size slide

  469. SET"client_min_messages"TO"warning;
    CREATE"EXTENSION"IF"NOT"EXISTS"pgtap;
    RESET"client_min_messages;
    BEGIN;
    SELECT"no_plan();
    SET"search_path"TO"flipr,public;
    SELECT"has_table("'users'");
    test/users.sql
    Columnist
    SELECT"finish();
    ROLLBACK;
    109
    Wednesday, May 22, 13

    View full-size slide

  470. SELECT"has_column("'users',"'nickname'"");
    SELECT"has_column("'users',"'password'"");
    SELECT"has_column("'users',"'timestamp'");
    SET"client_min_messages"TO"warning;
    CREATE"EXTENSION"IF"NOT"EXISTS"pgtap;
    RESET"client_min_messages;
    BEGIN;
    SELECT"no_plan();
    SET"search_path"TO"flipr,public;
    SELECT"has_table("'users'");
    test/users.sql
    Columnist
    SELECT"finish();
    ROLLBACK;
    109
    Wednesday, May 22, 13

    View full-size slide

  471. Dead Again
    >
    110
    Wednesday, May 22, 13

    View full-size slide

  472. ""pg_prove"*d"flipr_test"*v"test/users.sql
    test/users.sql".."
    ok"1"*"Table"users"should"exist
    ok"2"*"Column"users.nickname"should"exist
    not"ok"3"*"Column"users.password"should"exist
    #"Failed"test"3:""Column"users.password"should"exist"
    not"ok"4"*"Column"users."timestamp""should"exist
    #"Failed"test"4:""Column"users."timestamp""should"exist"
    1..4
    #"Looks"like"you"failed"2"tests"of"4
    Failed"2/4"subtests"
    Test"Summary"Report
    *******************
    test/users.sql"(Wstat:"0"Tests:"4"Failed:"2)
    ""Failed"tests:""3*4
    Files=1,"Tests=4,""0"wallclock"secs
    Result:"FAIL
    Dead Again
    >
    110
    Wednesday, May 22, 13

    View full-size slide

  473. ""pg_prove"*d"flipr_test"*v"test/users.sql
    test/users.sql".."
    ok"1"*"Table"users"should"exist
    ok"2"*"Column"users.nickname"should"exist
    not"ok"3"*"Column"users.password"should"exist
    #"Failed"test"3:""Column"users.password"should"exist"
    not"ok"4"*"Column"users."timestamp""should"exist
    #"Failed"test"4:""Column"users."timestamp""should"exist"
    1..4
    #"Looks"like"you"failed"2"tests"of"4
    Failed"2/4"subtests"
    Test"Summary"Report
    *******************
    test/users.sql"(Wstat:"0"Tests:"4"Failed:"2)
    ""Failed"tests:""3*4
    Files=1,"Tests=4,""0"wallclock"secs
    Result:"FAIL
    Dead Again
    >
    Guess we should
    add them.
    110
    Wednesday, May 22, 13

    View full-size slide

  474. MOAR Deploy
    >
    111
    Wednesday, May 22, 13

    View full-size slide

  475. >"emacs"deploy/users.sql
    >
    MOAR Deploy
    >
    111
    Wednesday, May 22, 13

    View full-size slide

  476. **"Deploy"users
    **"requires:"appschema
    BEGIN;
    SET"client_min_messages"="'warning';
    CREATE"TABLE"flipr.users"(
    """"nickname""TEXT
    deploy/users.sq
    deploy/users.sql
    );
    COMMIT;
    112
    Wednesday, May 22, 13

    View full-size slide

  477. **"Deploy"users
    **"requires:"appschema
    BEGIN;
    SET"client_min_messages"="'warning';
    CREATE"TABLE"flipr.users"(
    """"nickname""TEXT
    """""""""""""""""",
    """"password""TEXT,
    """"timestamp"TIMESTAMPTZ
    deploy/users.sq
    deploy/users.sql
    );
    COMMIT;
    112
    Wednesday, May 22, 13

    View full-size slide

  478. >"emacs"deploy/users.sql
    >
    Update Verify
    >
    113
    Wednesday, May 22, 13

    View full-size slide

  479. >"emacs"deploy/users.sql
    >
    Update Verify
    >
    ""emacs"verify/users.sql
    >
    113
    Wednesday, May 22, 13

    View full-size slide

  480. **"Verify"users
    BEGIN;
    SELECT"nickname
    ""FROM"flipr.users
    "WHERE"FALSE;
    COMMIT;
    verify/users.sq
    verify/users.sql
    114
    Wednesday, May 22, 13

    View full-size slide

  481. **"Verify"users
    BEGIN;
    SELECT"nickname
    ""FROM"flipr.users
    "WHERE"FALSE;
    COMMIT;
    ,"password,"timestamp
    verify/users.sq
    verify/users.sql
    114
    Wednesday, May 22, 13

    View full-size slide

  482. Revert Overhead
    >"emacs"deploy/users.sql
    >"emacs"verify/users.sql
    >
    115
    Wednesday, May 22, 13

    View full-size slide

  483. Revert Overhead
    ""sqitch"revert"**to"@HEAD^"*y
    Reverting"changes"to"appschema"from"flipr_test
    ""*"users".."ok
    >
    >"emacs"deploy/users.sql
    >"emacs"verify/users.sql
    >
    115
    Wednesday, May 22, 13

    View full-size slide

  484. Revert Overhead
    ""sqitch"revert"**to"@HEAD^"*y
    Reverting"changes"to"appschema"from"flipr_test
    ""*"users".."ok
    >
    Yes, really.
    >"emacs"deploy/users.sql
    >"emacs"verify/users.sql
    >
    115
    Wednesday, May 22, 13

    View full-size slide

  485. Revert Overhead
    ""sqitch"revert"**to"@HEAD^"*y
    Reverting"changes"to"appschema"from"flipr_test
    ""*"users".."ok
    >
    >"emacs"deploy/users.sql
    >"emacs"verify/users.sql
    >
    115
    Wednesday, May 22, 13

    View full-size slide

  486. Revert Overhead
    ""sqitch"revert"**to"@HEAD^"*y
    Reverting"changes"to"appschema"from"flipr_test
    ""*"users".."ok
    >
    Remove
    >"emacs"deploy/users.sql
    >"emacs"verify/users.sql
    >
    115
    Wednesday, May 22, 13

    View full-size slide

  487. Revert Overhead
    ""sqitch"revert"**to"@HEAD^"*y
    Reverting"changes"to"appschema"from"flipr_test
    ""*"users".."ok
    >
    >"emacs"deploy/users.sql
    >"emacs"verify/users.sql
    >
    What’s
    that?
    115
    Wednesday, May 22, 13

    View full-size slide

  488. Sqitch Tags
    antisocial network
    116
    Wednesday, May 22, 13

    View full-size slide

  489. Sqitch Tags
    Start with @
    antisocial network
    116
    Wednesday, May 22, 13

    View full-size slide

  490. Sqitch Tags
    Start with @
    To distinguish from changes
    antisocial network
    116
    Wednesday, May 22, 13

    View full-size slide

  491. Sqitch Tags
    Start with @
    To distinguish from changes
    Two symbolic tags:
    antisocial network
    116
    Wednesday, May 22, 13

    View full-size slide

  492. Sqitch Tags
    Start with @
    To distinguish from changes
    Two symbolic tags:
    @HEAD Last change
    antisocial network
    116
    Wednesday, May 22, 13

    View full-size slide

  493. Sqitch Tags
    Start with @
    To distinguish from changes
    Two symbolic tags:
    @HEAD Last change
    @ROOT First change
    antisocial network
    116
    Wednesday, May 22, 13

    View full-size slide

  494. Sqitch Tags
    Start with @
    To distinguish from changes
    Two symbolic tags:
    @HEAD Last change
    @ROOT First change
    Two modifiers:
    antisocial network
    116
    Wednesday, May 22, 13

    View full-size slide

  495. Sqitch Tags
    Start with @
    To distinguish from changes
    Two symbolic tags:
    @HEAD Last change
    @ROOT First change
    Two modifiers:
    ^ Previous change
    antisocial network
    116
    Wednesday, May 22, 13

    View full-size slide

  496. Sqitch Tags
    Start with @
    To distinguish from changes
    Two symbolic tags:
    @HEAD Last change
    @ROOT First change
    Two modifiers:
    ^ Previous change
    ~ Following change
    antisocial network
    116
    Wednesday, May 22, 13

    View full-size slide

  497. Specifying Changes
    antisocial network
    117
    Wednesday, May 22, 13

    View full-size slide

  498. Specifying Changes
    users Change named “users”
    antisocial network
    117
    Wednesday, May 22, 13

    View full-size slide

  499. Specifying Changes
    users Change named “users”
    @HEAD^ Second to last change
    antisocial network
    117
    Wednesday, May 22, 13

    View full-size slide

  500. Specifying Changes
    users Change named “users”
    @HEAD^ Second to last change
    users^ Change before users
    antisocial network
    117
    Wednesday, May 22, 13

    View full-size slide

  501. Specifying Changes
    users Change named “users”
    @HEAD^ Second to last change
    users^ Change before users
    @ROOT~ Second change
    antisocial network
    117
    Wednesday, May 22, 13

    View full-size slide

  502. Specifying Changes
    users Change named “users”
    @HEAD^ Second to last change
    users^ Change before users
    @ROOT~ Second change
    appschema~ Change after appschema
    antisocial network
    117
    Wednesday, May 22, 13

    View full-size slide

  503. Specifying Changes
    users Change named “users”
    @HEAD^ Second to last change
    users^ Change before users
    @ROOT~ Second change
    appschema~ Change after appschema
    @HEAD^^ Third to last change
    antisocial network
    117
    Wednesday, May 22, 13

    View full-size slide

  504. Specifying Changes
    users Change named “users”
    @HEAD^ Second to last change
    users^ Change before users
    @ROOT~ Second change
    appschema~ Change after appschema
    @HEAD^^ Third to last change
    users~4 4th change after users
    antisocial network
    117
    Wednesday, May 22, 13

    View full-size slide

  505. >""sqitch"revert"**to"@HEAD^"*y
    Reverting"changes"to"appschema"from"flipr_test
    ""*"users".."ok
    >
    Whither Users
    >
    118
    Wednesday, May 22, 13

    View full-size slide

  506. >""sqitch"revert"**to"@HEAD^"*y
    Reverting"changes"to"appschema"from"flipr_test
    ""*"users".."ok
    >
    Whither Users
    >
    >"psql"*d"flipr_test"*c"'\d"flipr.users'
    Did"not"find"any"relation"named""flipr.users".
    >
    118
    Wednesday, May 22, 13

    View full-size slide

  507. >""sqitch"revert"**to"@HEAD^"*y
    Reverting"changes"to"appschema"from"flipr_test
    ""*"users".."ok
    >
    ""sqitch"status
    #"On"database"flipr_test
    #"Project:""flipr
    #"Change:"""748346dfe73cf2af32a8b7088fd75ad8d7aecda3
    #"Name:"""""appschema
    #"Deployed:"2013*05*21"15:10:02"*0400
    #"By:"""""""David"E."Wheeler"
    #"
    Undeployed"change:
    ""*"users
    Whither Users
    >
    >"psql"*d"flipr_test"*c"'\d"flipr.users'
    Did"not"find"any"relation"named""flipr.users".
    >
    118
    Wednesday, May 22, 13

    View full-size slide

  508. Whither Users
    >
    119
    Wednesday, May 22, 13

    View full-size slide

  509. >"sqitch"verify
    Verifying"flipr_test
    ""*"appschema".."ok
    Undeployed"change:
    ""*"users
    Verify"successful
    >
    Whither Users
    >
    119
    Wednesday, May 22, 13

    View full-size slide

  510. Back At It
    >
    120
    Wednesday, May 22, 13

    View full-size slide

  511. ""sqitch"deploy
    Deploying"changes"to"flipr_test
    ""+"users".."ok
    >
    Back At It
    >
    120
    Wednesday, May 22, 13

    View full-size slide

  512. ""sqitch"deploy
    Deploying"changes"to"flipr_test
    ""+"users".."ok
    >
    Back At It
    >
    Add
    120
    Wednesday, May 22, 13

    View full-size slide

  513. ""sqitch"deploy
    Deploying"changes"to"flipr_test
    ""+"users".."ok
    >
    Back At It
    >
    ""pg_prove"*d"flipr_test"*v"test/users.sql
    test/users.sql".."
    ok"1"*"Table"users"should"exist
    ok"2"*"Column"users.nickname"should"exist
    ok"3"*"Column"users.password"should"exist
    ok"4"*"Column"users."timestamp""should"exist
    1..4
    ok
    All"tests"successful.
    Files=1,"Tests=4,""0"wallclock"secs
    Result:"PASS
    >
    120
    Wednesday, May 22, 13

    View full-size slide

  514. ""sqitch"deploy
    Deploying"changes"to"flipr_test
    ""+"users".."ok
    >
    Back At It
    >
    ""pg_prove"*d"flipr_test"*v"test/users.sql
    test/users.sql".."
    ok"1"*"Table"users"should"exist
    ok"2"*"Column"users.nickname"should"exist
    ok"3"*"Column"users.password"should"exist
    ok"4"*"Column"users."timestamp""should"exist
    1..4
    ok
    All"tests"successful.
    Files=1,"Tests=4,""0"wallclock"secs
    Result:"PASS
    >
    Woot!
    120
    Wednesday, May 22, 13

    View full-size slide

  515. ""sqitch"deploy
    Deploying"changes"to"flipr_test
    ""+"users".."ok
    >
    Back At It
    >
    ""pg_prove"*d"flipr_test"*v"test/users.sql
    test/users.sql".."
    ok"1"*"Table"users"should"exist
    ok"2"*"Column"users.nickname"should"exist
    ok"3"*"Column"users.password"should"exist
    ok"4"*"Column"users."timestamp""should"exist
    1..4
    ok
    All"tests"successful.
    Files=1,"Tests=4,""0"wallclock"secs
    Result:"PASS
    >
    ""emacs"verify/users.sql
    120
    Wednesday, May 22, 13

    View full-size slide

  516. SET"search_path"="public,tap;
    BEGIN;
    SELECT"*"FROM"no_plan();
    SELECT"has_table(""'users'");
    SELECT"has_column(""""""""'users',"'timestamp'");
    test/users.sql
    SELECT"has_column(""""""""'users',"'nickname'");
    SELECT"has_column(""""""""'users',"'password'");
    SELECT"finish();
    ROLLBACK;
    121
    Wednesday, May 22, 13

    View full-size slide

  517. SET"search_path"="public,tap;
    BEGIN;
    SELECT"*"FROM"no_plan();
    SELECT"has_table(""'users'");
    SELECT"has_column(""""""""'users',"'timestamp'");
    test/users.sql
    SELECT"has_column(""""""""'users',"'nickname'");
    SELECT"has_column(""""""""'users',"'password'");
    SELECT"has_pk("""""'users'");
    SELECT"finish();
    ROLLBACK;
    121
    Wednesday, May 22, 13

    View full-size slide

  518. SELECT"col_type_is("""""""'users',"'nickname',"'text'");
    SELECT"col_hasnt_default("'users',"'nickname'");
    SELECT"col_is_pk("""""""""'users',"'nickname'");
    SET"search_path"="public,tap;
    BEGIN;
    SELECT"*"FROM"no_plan();
    SELECT"has_table(""'users'");
    SELECT"has_column(""""""""'users',"'timestamp'");
    test/users.sql
    SELECT"has_column(""""""""'users',"'nickname'");
    SELECT"has_column(""""""""'users',"'password'");
    SELECT"has_pk("""""'users'");
    SELECT"finish();
    ROLLBACK;
    121
    Wednesday, May 22, 13

    View full-size slide

  519. SELECT"col_type_is("""""""'users',"'nickname',"'text'");
    SELECT"col_hasnt_default("'users',"'nickname'");
    SELECT"col_is_pk("""""""""'users',"'nickname'");
    SET"search_path"="public,tap;
    BEGIN;
    SELECT"*"FROM"no_plan();
    SELECT"has_table(""'users'");
    SELECT"has_column(""""""""'users',"'timestamp'");
    test/users.sql
    SELECT"has_column(""""""""'users',"'nickname'");
    SELECT"has_column(""""""""'users',"'password'");
    SELECT"has_pk("""""'users'");
    SELECT"col_type_is("""""""'users',"'password',"'text'");
    SELECT"col_not_null(""""""'users',"'password'");
    SELECT"col_hasnt_default("'users',"'password'");
    SELECT"finish();
    ROLLBACK;
    121
    Wednesday, May 22, 13

    View full-size slide

  520. SELECT"col_type_is("""""""'users',"'nickname',"'text'");
    SELECT"col_hasnt_default("'users',"'nickname'");
    SELECT"col_is_pk("""""""""'users',"'nickname'");
    SET"search_path"="public,tap;
    BEGIN;
    SELECT"*"FROM"no_plan();
    SELECT"has_table(""'users'");
    SELECT"has_column(""""""""'users',"'timestamp'");
    test/users.sql
    SELECT"has_column(""""""""'users',"'nickname'");
    SELECT"has_column(""""""""'users',"'password'");
    SELECT"has_pk("""""'users'");
    SELECT"col_type_is('users',"'timestamp',"'timestamp"with"time"zone');
    SELECT"col_not_null(""""""'users',"'timestamp'");
    SELECT"col_has_default("""'users',"'timestamp'");
    SELECT"col_default_is(""""'users',"'timestamp',"'now()'");
    SELECT"col_type_is("""""""'users',"'password',"'text'");
    SELECT"col_not_null(""""""'users',"'password'");
    SELECT"col_hasnt_default("'users',"'password'");
    SELECT"finish();
    ROLLBACK;
    121
    Wednesday, May 22, 13

    View full-size slide

  521. Columny
    >
    122
    Wednesday, May 22, 13

    View full-size slide

  522. >"pg_prove"*d"flipr_test"test/users.sql
    test/users.sql".."1/?"
    #"Failed"test"2:""Table"users"should"have"a"primary"key"
    #"Failed"test"6:""Column"users(nickname)"should"be"a"primary"key"
    #"""""""""have:"NULL
    #"""""""""want:"{nickname}
    #"Failed"test"9:""Column"users.password"should"be"NOT"NULL"
    #"Failed"test"13:""Column"users."timestamp""should"be"NOT"NULL"
    #"Failed"test"14:""Column"users."timestamp""should"have"a"default"
    #"Failed"test"15:""Column"users."timestamp""should"default"to"'now()'"
    #"""""Column"users."timestamp""has"no"default
    #"Looks"like"you"failed"6"tests"of"15
    test/users.sql".."Failed"6/15"subtests"
    Test"Summary"Report
    *******************
    test/users.sql"(Wstat:"0"Tests:"15"Failed:"6)
    ""Failed"tests:""2,"6,"9,"13*15
    Files=1,"Tests=15,""0"wallclock"secs
    Result:"FAIL
    Columny
    >
    122
    Wednesday, May 22, 13

    View full-size slide

  523. >"pg_prove"*d"flipr_test"test/users.sql
    test/users.sql".."1/?"
    #"Failed"test"2:""Table"users"should"have"a"primary"key"
    #"Failed"test"6:""Column"users(nickname)"should"be"a"primary"key"
    #"""""""""have:"NULL
    #"""""""""want:"{nickname}
    #"Failed"test"9:""Column"users.password"should"be"NOT"NULL"
    #"Failed"test"13:""Column"users."timestamp""should"be"NOT"NULL"
    #"Failed"test"14:""Column"users."timestamp""should"have"a"default"
    #"Failed"test"15:""Column"users."timestamp""should"default"to"'now()'"
    #"""""Column"users."timestamp""has"no"default
    #"Looks"like"you"failed"6"tests"of"15
    test/users.sql".."Failed"6/15"subtests"
    Test"Summary"Report
    *******************
    test/users.sql"(Wstat:"0"Tests:"15"Failed:"6)
    ""Failed"tests:""2,"6,"9,"13*15
    Files=1,"Tests=15,""0"wallclock"secs
    Result:"FAIL
    Columny
    >
    Let’s make
    it so.
    122
    Wednesday, May 22, 13

    View full-size slide

  524. User Typography
    >
    123
    Wednesday, May 22, 13

    View full-size slide

  525. ""emacs"deploy/users.sql
    >
    User Typography
    >
    123
    Wednesday, May 22, 13

    View full-size slide

  526. **"Deploy"users
    **"requires:"appschema
    BEGIN;
    SET"client_min_messages"="'warning';
    CREATE"TABLE"flipr.users"(
    """"nickname""TEXT
    """"password""TEXT
    """"timestamp"TIMESTAMPTZ
    );
    COMMIT;
    deploy/users.sq
    deploy/users.sql
    ,
    ,
    124
    Wednesday, May 22, 13

    View full-size slide

  527. **"Deploy"users
    **"requires:"appschema
    BEGIN;
    SET"client_min_messages"="'warning';
    CREATE"TABLE"flipr.users"(
    """"nickname""TEXT
    """"password""TEXT
    """"timestamp"TIMESTAMPTZ
    );
    COMMIT;
    deploy/users.sq
    deploy/users.sql
    ,
    """"""""PRIMARY"KEY,
    124
    Wednesday, May 22, 13

    View full-size slide

  528. **"Deploy"users
    **"requires:"appschema
    BEGIN;
    SET"client_min_messages"="'warning';
    CREATE"TABLE"flipr.users"(
    """"nickname""TEXT
    """"password""TEXT
    """"timestamp"TIMESTAMPTZ
    );
    COMMIT;
    deploy/users.sq
    deploy/users.sql
    """"""""PRIMARY"KEY,
    """"""""NOT"NULL,
    124
    Wednesday, May 22, 13

    View full-size slide

  529. **"Deploy"users
    **"requires:"appschema
    BEGIN;
    SET"client_min_messages"="'warning';
    CREATE"TABLE"flipr.users"(
    """"nickname""TEXT
    """"password""TEXT
    """"timestamp"TIMESTAMPTZ
    );
    COMMIT;
    deploy/users.sq
    deploy/users.sql
    """"""""PRIMARY"KEY,
    """"""""NOT"NULL,
    "NOT"NULL"DEFAULT"NOW()
    124
    Wednesday, May 22, 13

    View full-size slide

  530. User Typography
    >"emacs"deploy/users.sql
    >
    125
    Wednesday, May 22, 13

    View full-size slide

  531. ""sqitch"rebase"@HEAD^"*y
    Reverting"changes"to"appschema"from"flipr_test
    ""*"users".."ok
    Deploying"changes"to"flipr_test
    ""+"users".."ok
    >
    User Typography
    >"emacs"deploy/users.sql
    >
    125
    Wednesday, May 22, 13

    View full-size slide

  532. ""sqitch"rebase"@HEAD^"*y
    Reverting"changes"to"appschema"from"flipr_test
    ""*"users".."ok
    Deploying"changes"to"flipr_test
    ""+"users".."ok
    >
    User Typography
    >"emacs"deploy/users.sql
    >
    125
    Wednesday, May 22, 13

    View full-size slide

  533. ""sqitch"rebase"@HEAD^"*y
    Reverting"changes"to"appschema"from"flipr_test
    ""*"users".."ok
    Deploying"changes"to"flipr_test
    ""+"users".."ok
    >
    User Typography
    >"emacs"deploy/users.sql
    >
    125
    Wednesday, May 22, 13

    View full-size slide

  534. ""sqitch"rebase"@HEAD^"*y
    Reverting"changes"to"appschema"from"flipr_test
    ""*"users".."ok
    Deploying"changes"to"flipr_test
    ""+"users".."ok
    >
    User Typography
    >"emacs"deploy/users.sql
    >
    ""pg_prove"*d"flipr_test"test/users.sql
    test/users.sql".."ok""""
    All"tests"successful.
    Files=1,"Tests=15,""0"wallclock"secs
    Result:"PASS
    >
    125
    Wednesday, May 22, 13

    View full-size slide

  535. ""sqitch"rebase"@HEAD^"*y
    Reverting"changes"to"appschema"from"flipr_test
    ""*"users".."ok
    Deploying"changes"to"flipr_test
    ""+"users".."ok
    >
    User Typography
    >"emacs"deploy/users.sql
    >
    ""pg_prove"*d"flipr_test"test/users.sql
    test/users.sql".."ok""""
    All"tests"successful.
    Files=1,"Tests=15,""0"wallclock"secs
    Result:"PASS
    > Boom.
    125
    Wednesday, May 22, 13

    View full-size slide

  536. Additives
    >
    126
    Wednesday, May 22, 13

    View full-size slide

  537. Additives
    >
    ""git"add".
    >
    126
    Wednesday, May 22, 13

    View full-size slide

  538. Additives
    >
    ""git"add".
    >
    ""git"commit"*am"'Add"users"table.'
    [users"610b318]"Add"users"table.
    "5"files"changed,"59"insertions(+)
    "create"mode"100644"deploy/users.sql
    "create"mode"100644"revert/users.sql
    "create"mode"100644"test/users.sql
    "create"mode"100644"verify/users.sql
    >
    126
    Wednesday, May 22, 13

    View full-size slide

  539. Pushers
    >
    127
    Wednesday, May 22, 13

    View full-size slide

  540. Pushers
    >
    ""git"push"**set*upstream"origin"users
    Counting"objects:"17,"done.
    Delta"compression"using"up"to"4"threads.
    Compressing"objects:"100%"(11/11),"done.
    Writing"objects:"100%"(11/11),"1.53"KiB,"done.
    Total"11"(delta"1),"reused"0"(delta"0)
    To"../flipr*remote
    "*"[new"branch]""""""users"*>"users
    Branch"users"set"up"to"track"remote"branch"users"
    from"origin.
    >
    127
    Wednesday, May 22, 13

    View full-size slide

  541. Pushers
    >
    ""git"push"**set*upstream"origin"users
    Counting"objects:"17,"done.
    Delta"compression"using"up"to"4"threads.
    Compressing"objects:"100%"(11/11),"done.
    Writing"objects:"100%"(11/11),"1.53"KiB,"done.
    Total"11"(delta"1),"reused"0"(delta"0)
    To"../flipr*remote
    "*"[new"branch]""""""users"*>"users
    Branch"users"set"up"to"track"remote"branch"users"
    from"origin.
    >
    127
    Wednesday, May 22, 13

    View full-size slide

  542. Pushers
    >
    ""git"push"**set*upstream"origin"users
    Counting"objects:"17,"done.
    Delta"compression"using"up"to"4"threads.
    Compressing"objects:"100%"(11/11),"done.
    Writing"objects:"100%"(11/11),"1.53"KiB,"done.
    Total"11"(delta"1),"reused"0"(delta"0)
    To"../flipr*remote
    "*"[new"branch]""""""users"*>"users
    Branch"users"set"up"to"track"remote"branch"users"
    from"origin.
    >
    127
    Wednesday, May 22, 13

    View full-size slide

  543. Wash, Rinse, Repeat
    antisocial network
    128
    Wednesday, May 22, 13

    View full-size slide

  544. Wash, Rinse, Repeat
    Add failing simple test
    antisocial network
    128
    Wednesday, May 22, 13

    View full-size slide

  545. Wash, Rinse, Repeat
    Add failing simple test
    Add and deploy change
    antisocial network
    128
    Wednesday, May 22, 13

    View full-size slide

  546. Wash, Rinse, Repeat
    Add failing simple test
    Add and deploy change
    Revise test
    antisocial network
    128
    Wednesday, May 22, 13

    View full-size slide

  547. Wash, Rinse, Repeat
    Add failing simple test
    Add and deploy change
    Revise test
    Revise and rebase change
    antisocial network
    128
    Wednesday, May 22, 13

    View full-size slide

  548. Wash, Rinse, Repeat
    Add failing simple test
    Add and deploy change
    Revise test
    Revise and rebase change
    Wash, Rinse, Repeat
    antisocial network
    128
    Wednesday, May 22, 13

    View full-size slide

  549. Wash, Rinse, Repeat
    Add failing simple test
    Add and deploy change
    Revise test
    Revise and rebase change
    Wash, Rinse, Repeat
    Commit/Push when done
    antisocial network
    128
    Wednesday, May 22, 13

    View full-size slide

  550. Wash, Rinse, Repeat
    Add failing simple test
    Add and deploy change
    Revise test
    Revise and rebase change
    Wash, Rinse, Repeat
    Commit/Push when done
    Breathe in, breathe out
    antisocial network
    128
    Wednesday, May 22, 13

    View full-size slide

  551. Time to Work!
    antisocial network
    129
    Wednesday, May 22, 13

    View full-size slide

  552. Time to Work!
    Prepare to hack!
    antisocial network
    129
    Wednesday, May 22, 13

    View full-size slide

  553. Time to Work!
    Prepare to hack!
    git checkout master
    antisocial network
    129
    Wednesday, May 22, 13

    View full-size slide

  554. Time to Work!
    Prepare to hack!
    git checkout master
    git branch -D users
    antisocial network
    129
    Wednesday, May 22, 13

    View full-size slide

  555. Time to Work!
    Prepare to hack!
    git checkout master
    git branch -D users
    git checkout -b users
    antisocial network
    129
    Wednesday, May 22, 13

    View full-size slide

  556. Time to Work!
    Prepare to hack!
    git checkout master
    git branch -D users
    git checkout -b users
    git reset --hard upstream/users
    antisocial network
    129
    Wednesday, May 22, 13

    View full-size slide

  557. Time to Work!
    Prepare to hack!
    git checkout master
    git branch -D users
    git checkout -b users
    git reset --hard upstream/users
    git log
    antisocial network
    129
    Wednesday, May 22, 13

    View full-size slide

  558. Time to Work!
    Prepare to hack!
    git checkout master
    git branch -D users
    git checkout -b users
    git reset --hard upstream/users
    git log
    Should be at “Add users table.”
    antisocial network
    129
    Wednesday, May 22, 13

    View full-size slide

  559. Caution: Hard Reset
    Ahead
    130
    Wednesday, May 22, 13

    View full-size slide

  560. Caution: Hard Reset
    Ahead
    A rare destructive Git command
    130
    Wednesday, May 22, 13

    View full-size slide

  561. Caution: Hard Reset
    Ahead
    A rare destructive Git command
    Deletes HEAD snapshot
    130
    Wednesday, May 22, 13

    View full-size slide

  562. Caution: Hard Reset
    Ahead
    A rare destructive Git command
    Deletes HEAD snapshot
    Replaces it with new snapshot
    130
    Wednesday, May 22, 13

    View full-size slide

  563. Caution: Hard Reset
    Ahead
    A rare destructive Git command
    Deletes HEAD snapshot
    Replaces it with new snapshot
    Almost un-reversible
    130
    Wednesday, May 22, 13

    View full-size slide

  564. Caution: Hard Reset
    Ahead
    A rare destructive Git command
    Deletes HEAD snapshot
    Replaces it with new snapshot
    Almost un-reversible
    Useful for starting from known point
    130
    Wednesday, May 22, 13

    View full-size slide

  565. Caution: Hard Reset
    Ahead
    A rare destructive Git command
    Deletes HEAD snapshot
    Replaces it with new snapshot
    Almost un-reversible
    Useful for starting from known point
    USE WITH CAUTION!
    130
    Wednesday, May 22, 13

    View full-size slide

  566. antisocial network
    Flip Out
    131
    Wednesday, May 22, 13

    View full-size slide

  567. antisocial network
    Flip Out
    Create flips branch
    131
    Wednesday, May 22, 13

    View full-size slide

  568. antisocial network
    Flip Out
    Create flips branch
    Create flips table
    131
    Wednesday, May 22, 13

    View full-size slide

  569. antisocial network
    Flip Out
    Create flips branch
    Create flips table
    flip_id SERIAL PK
    131
    Wednesday, May 22, 13

    View full-size slide

  570. antisocial network
    Flip Out
    Create flips branch
    Create flips table
    flip_id SERIAL PK
    nickname FK
    131
    Wednesday, May 22, 13

    View full-size slide

  571. antisocial network
    Flip Out
    Create flips branch
    Create flips table
    flip_id SERIAL PK
    nickname FK
    body TEXT
    131
    Wednesday, May 22, 13

    View full-size slide

  572. antisocial network
    Flip Out
    Create flips branch
    Create flips table
    flip_id SERIAL PK
    nickname FK
    body TEXT
    timestamptz
    131
    Wednesday, May 22, 13

    View full-size slide

  573. antisocial network
    Flip Out
    Create flips branch
    Create flips table
    flip_id SERIAL PK
    nickname FK
    body TEXT
    timestamptz
    Use TDDD
    131
    Wednesday, May 22, 13

    View full-size slide

  574. antisocial network
    Flip Out
    Create flips branch
    Create flips table
    flip_id SERIAL PK
    nickname FK
    body TEXT
    timestamptz
    Use TDDD
    https:/
    /github.com/
    theory/agile-flipr.git
    131
    Wednesday, May 22, 13

    View full-size slide

  575. Functional Testing
    >
    132
    Wednesday, May 22, 13

    View full-size slide

  576. Functional Testing
    ""git"checkout"*b"userfuncs"users
    Switched"to"a"new"branch"'userfuncs'
    >
    >
    Branches
    from users
    132
    Wednesday, May 22, 13

    View full-size slide

  577. Functional Testing
    ""git"checkout"*b"userfuncs"users
    Switched"to"a"new"branch"'userfuncs'
    >
    >
    ""sqitch"add"insert_user"*r"users"*r"appschema"\
    """"""*n"'Creates"a"function"to"insert"a"user.'
    Created"deploy/insert_user.sql
    Created"revert/insert_user.sql
    Created"verify/insert_user.sql
    Added""insert_user"[users"appschema]""to"sqitch.plan
    >
    132
    Wednesday, May 22, 13

    View full-size slide

  578. Functional Testing
    ""git"checkout"*b"userfuncs"users
    Switched"to"a"new"branch"'userfuncs'
    >
    >
    ""sqitch"add"insert_user"*r"users"*r"appschema"\
    """"""*n"'Creates"a"function"to"insert"a"user.'
    Created"deploy/insert_user.sql
    Created"revert/insert_user.sql
    Created"verify/insert_user.sql
    Added""insert_user"[users"appschema]""to"sqitch.plan
    >
    ""emacs"test/insert_user.sql
    >
    132
    Wednesday, May 22, 13

    View full-size slide

  579. test/insert_use
    test/insert_user.sql
    133
    Wednesday, May 22, 13

    View full-size slide

  580. test/insert_use
    test/insert_user.sql
    SET"client_min_messages"TO"warning;
    CREATE"EXTENSION"IF"NOT"EXISTS"pgtap;
    RESET"client_min_messages;
    SET"search_path"TO"flipr,public;
    133
    Wednesday, May 22, 13

    View full-size slide

  581. test/insert_use
    test/insert_user.sql
    SET"client_min_messages"TO"warning;
    CREATE"EXTENSION"IF"NOT"EXISTS"pgtap;
    RESET"client_min_messages;
    SET"search_path"TO"flipr,public;
    133
    Wednesday, May 22, 13

    View full-size slide

  582. test/insert_use
    test/insert_user.sql
    SET"client_min_messages"TO"warning;
    CREATE"EXTENSION"IF"NOT"EXISTS"pgtap;
    RESET"client_min_messages;
    SET"search_path"TO"flipr,public;
    **"Plan"the"tests.
    BEGIN;
    select"plan(11);
    133
    Wednesday, May 22, 13

    View full-size slide

  583. test/insert_use
    test/insert_user.sql
    SET"client_min_messages"TO"warning;
    CREATE"EXTENSION"IF"NOT"EXISTS"pgtap;
    RESET"client_min_messages;
    SET"search_path"TO"flipr,public;
    **"Plan"the"tests.
    BEGIN;
    select"plan(11);
    133
    Wednesday, May 22, 13

    View full-size slide

  584. test/insert_use
    SELECT"plan(11);
    134
    Wednesday, May 22, 13

    View full-size slide

  585. test/insert_use
    SELECT"plan(11);
    SELECT"has_function("'insert_user'");
    SELECT"has_function(
    """"'insert_user',"ARRAY['text',"'text']
    );
    SELECT"function_lang_is(
    """"'insert_user',"ARRAY['text',"'text'],
    """"'sql'
    );
    SELECT"function_returns(
    """"'insert_user',"ARRAY['text',"'text'],
    """"'void'
    );
    SELECT"volatility_is(
    """"'insert_user',"ARRAY['text',"'text'],
    """"'volatile'
    );
    134
    Wednesday, May 22, 13

    View full-size slide

  586. test/insert_use
    SELECT"plan(11);
    SELECT"has_function("'insert_user'");
    SELECT"has_function(
    """"'insert_user',"ARRAY['text',"'text']
    );
    SELECT"function_lang_is(
    """"'insert_user',"ARRAY['text',"'text'],
    """"'sql'
    );
    SELECT"function_returns(
    """"'insert_user',"ARRAY['text',"'text'],
    """"'void'
    );
    SELECT"volatility_is(
    """"'insert_user',"ARRAY['text',"'text'],
    """"'volatile'
    );
    134
    Wednesday, May 22, 13

    View full-size slide

  587. test/insert_use
    SELECT"plan(11);
    SELECT"has_function("'insert_user'");
    SELECT"has_function(
    """"'insert_user',"ARRAY['text',"'text']
    );
    SELECT"function_lang_is(
    """"'insert_user',"ARRAY['text',"'text'],
    """"'sql'
    );
    SELECT"function_returns(
    """"'insert_user',"ARRAY['text',"'text'],
    """"'void'
    );
    SELECT"volatility_is(
    """"'insert_user',"ARRAY['text',"'text'],
    """"'volatile'
    );
    134
    Wednesday, May 22, 13

    View full-size slide

  588. test/insert_use
    SELECT"plan(11);
    SELECT"has_function("'insert_user'");
    SELECT"has_function(
    """"'insert_user',"ARRAY['text',"'text']
    );
    SELECT"function_lang_is(
    """"'insert_user',"ARRAY['text',"'text'],
    """"'sql'
    );
    SELECT"function_returns(
    """"'insert_user',"ARRAY['text',"'text'],
    """"'void'
    );
    SELECT"volatility_is(
    """"'insert_user',"ARRAY['text',"'text'],
    """"'volatile'
    );
    134
    Wednesday, May 22, 13

    View full-size slide

  589. test/insert_use
    SELECT"plan(11);
    SELECT"has_function("'insert_user'");
    SELECT"has_function(
    """"'insert_user',"ARRAY['text',"'text']
    );
    SELECT"function_lang_is(
    """"'insert_user',"ARRAY['text',"'text'],
    """"'sql'
    );
    SELECT"function_returns(
    """"'insert_user',"ARRAY['text',"'text'],
    """"'void'
    );
    SELECT"volatility_is(
    """"'insert_user',"ARRAY['text',"'text'],
    """"'volatile'
    );
    134
    Wednesday, May 22, 13

    View full-size slide

  590. test/insert_use
    test/insert_user.sql
    """"'volatile'
    );
    135
    Wednesday, May 22, 13

    View full-size slide

  591. test/insert_use
    test/insert_user.sql
    """"'volatile'
    );
    SELECT"lives_ok(
    """"$$"SELECT"insert_user('theory',"'foo')"$$,
    """"'Insert"a"user'
    );
    SELECT"row_eq(
    """'SELECT"*"FROM"users',
    """ROW('theory',"md5('foo'),"NOW())::users,
    """'The"user"should"have"been"inserted'
    );
    135
    Wednesday, May 22, 13

    View full-size slide

  592. test/insert_use
    test/insert_user.sql
    """"'volatile'
    );
    SELECT"lives_ok(
    """"$$"SELECT"insert_user('theory',"'foo')"$$,
    """"'Insert"a"user'
    );
    SELECT"row_eq(
    """'SELECT"*"FROM"users',
    """ROW('theory',"md5('foo'),"NOW())::users,
    """'The"user"should"have"been"inserted'
    );
    135
    Wednesday, May 22, 13

    View full-size slide

  593. test/insert_use
    test/insert_user.sql
    """"'volatile'
    );
    SELECT"lives_ok(
    """"$$"SELECT"insert_user('theory',"'foo')"$$,
    """"'Insert"a"user'
    );
    SELECT"row_eq(
    """'SELECT"*"FROM"users',
    """ROW('theory',"md5('foo'),"NOW())::users,
    """'The"user"should"have"been"inserted'
    );
    135
    Wednesday, May 22, 13

    View full-size slide

  594. test/insert_use
    test/insert_user.sql
    """"'volatile'
    );
    SELECT"lives_ok(
    """"$$"SELECT"insert_user('theory',"'foo')"$$,
    """"'Insert"a"user'
    );
    SELECT"row_eq(
    """'SELECT"*"FROM"users',
    """ROW('theory',"md5('foo'),"NOW())::users,
    """'The"user"should"have"been"inserted'
    );
    135
    Wednesday, May 22, 13

    View full-size slide

  595. test/insert_use
    test/insert_user.sql
    """"'volatile'
    );
    SELECT"lives_ok(
    """"$$"SELECT"insert_user('theory',"'foo')"$$,
    """"'Insert"a"user'
    );
    SELECT"row_eq(
    """'SELECT"*"FROM"users',
    """ROW('theory',"md5('foo'),"NOW())::users,
    """'The"user"should"have"been"inserted'
    );
    135
    Wednesday, May 22, 13

    View full-size slide

  596. test/insert_use
    test/insert_user.sql
    """'The"user"should"have"been"inserted'
    );
    136
    Wednesday, May 22, 13

    View full-size slide

  597. test/insert_use
    test/insert_user.sql
    """'The"user"should"have"been"inserted'
    );
    SELECT"lives_ok(
    """"$$"SELECT"insert_user('strongrrl',"'w00t')"$$,
    """"'Insert"another"user'
    );
    SELECT"bag_eq(
    """"'SELECT"*"FROM"users',
    """"$$"VALUES
    """"""""('theory',""""md5('foo'),""NOW()),
    """"""""('strongrrl',"md5('w00t'),"NOW())
    """"$$,
    """"'Both"users"should"be"present'
    );
    136
    Wednesday, May 22, 13

    View full-size slide

  598. test/insert_use
    test/insert_user.sql
    """'The"user"should"have"been"inserted'
    );
    SELECT"lives_ok(
    """"$$"SELECT"insert_user('strongrrl',"'w00t')"$$,
    """"'Insert"another"user'
    );
    SELECT"bag_eq(
    """"'SELECT"*"FROM"users',
    """"$$"VALUES
    """"""""('theory',""""md5('foo'),""NOW()),
    """"""""('strongrrl',"md5('w00t'),"NOW())
    """"$$,
    """"'Both"users"should"be"present'
    );
    136
    Wednesday, May 22, 13

    View full-size slide

  599. test/insert_use
    test/insert_user.sql
    """'The"user"should"have"been"inserted'
    );
    SELECT"lives_ok(
    """"$$"SELECT"insert_user('strongrrl',"'w00t')"$$,
    """"'Insert"another"user'
    );
    SELECT"bag_eq(
    """"'SELECT"*"FROM"users',
    """"$$"VALUES
    """"""""('theory',""""md5('foo'),""NOW()),
    """"""""('strongrrl',"md5('w00t'),"NOW())
    """"$$,
    """"'Both"users"should"be"present'
    );
    Includes
    dupes
    136
    Wednesday, May 22, 13

    View full-size slide

  600. test/insert_use
    test/insert_user.sql
    """'The"user"should"have"been"inserted'
    );
    SELECT"lives_ok(
    """"$$"SELECT"insert_user('strongrrl',"'w00t')"$$,
    """"'Insert"another"user'
    );
    SELECT"bag_eq(
    """"'SELECT"*"FROM"users',
    """"$$"VALUES
    """"""""('theory',""""md5('foo'),""NOW()),
    """"""""('strongrrl',"md5('w00t'),"NOW())
    """"$$,
    """"'Both"users"should"be"present'
    );
    136
    Wednesday, May 22, 13

    View full-size slide

  601. test/insert_use
    test/insert_user.sql
    """'The"user"should"have"been"inserted'
    );
    SELECT"lives_ok(
    """"$$"SELECT"insert_user('strongrrl',"'w00t')"$$,
    """"'Insert"another"user'
    );
    SELECT"bag_eq(
    """"'SELECT"*"FROM"users',
    """"$$"VALUES
    """"""""('theory',""""md5('foo'),""NOW()),
    """"""""('strongrrl',"md5('w00t'),"NOW())
    """"$$,
    """"'Both"users"should"be"present'
    );
    136
    Wednesday, May 22, 13

    View full-size slide

  602. test/insert_use
    """"'Both"users"should"be"present'
    );
    137
    Wednesday, May 22, 13

    View full-size slide

  603. SELECT"throws_ok(
    """"$$"SELECT"insert_user('theory',"'ha*ha')"$$,
    """"23505,"**"duplicate"key"violation
    """"NULL,""**"localized"error"message
    """"'Should"get"an"error"for"duplicate"nickname'
    );
    SELECT"bag_eq(
    """"'SELECT"*"FROM"users',
    """"$$"VALUES
    """"""""('theory',""""md5('foo'),""NOW()),
    """"""""('strongrrl',"md5('w00t'),"NOW())
    """"$$,
    """"'Should"still"have"just"the"two"users'
    );
    test/insert_use
    """"'Both"users"should"be"present'
    );
    137
    Wednesday, May 22, 13

    View full-size slide

  604. SELECT"throws_ok(
    """"$$"SELECT"insert_user('theory',"'ha*ha')"$$,
    """"23505,"**"duplicate"key"violation
    """"NULL,""**"localized"error"message
    """"'Should"get"an"error"for"duplicate"nickname'
    );
    SELECT"bag_eq(
    """"'SELECT"*"FROM"users',
    """"$$"VALUES
    """"""""('theory',""""md5('foo'),""NOW()),
    """"""""('strongrrl',"md5('w00t'),"NOW())
    """"$$,
    """"'Should"still"have"just"the"two"users'
    );
    test/insert_use
    """"'Both"users"should"be"present'
    );
    137
    Wednesday, May 22, 13

    View full-size slide

  605. SELECT"throws_ok(
    """"$$"SELECT"insert_user('theory',"'ha*ha')"$$,
    """"23505,"**"duplicate"key"violation
    """"NULL,""**"localized"error"message
    """"'Should"get"an"error"for"duplicate"nickname'
    );
    SELECT"bag_eq(
    """"'SELECT"*"FROM"users',
    """"$$"VALUES
    """"""""('theory',""""md5('foo'),""NOW()),
    """"""""('strongrrl',"md5('w00t'),"NOW())
    """"$$,
    """"'Should"still"have"just"the"two"users'
    );
    test/insert_use
    """"'Both"users"should"be"present'
    );
    Appendix A
    137
    Wednesday, May 22, 13

    View full-size slide

  606. SELECT"throws_ok(
    """"$$"SELECT"insert_user('theory',"'ha*ha')"$$,
    """"23505,"**"duplicate"key"violation
    """"NULL,""**"localized"error"message
    """"'Should"get"an"error"for"duplicate"nickname'
    );
    SELECT"bag_eq(
    """"'SELECT"*"FROM"users',
    """"$$"VALUES
    """"""""('theory',""""md5('foo'),""NOW()),
    """"""""('strongrrl',"md5('w00t'),"NOW())
    """"$$,
    """"'Should"still"have"just"the"two"users'
    );
    test/insert_use
    """"'Both"users"should"be"present'
    );
    137
    Wednesday, May 22, 13

    View full-size slide

  607. SELECT"throws_ok(
    """"$$"SELECT"insert_user('theory',"'ha*ha')"$$,
    """"23505,"**"duplicate"key"violation
    """"NULL,""**"localized"error"message
    """"'Should"get"an"error"for"duplicate"nickname'
    );
    SELECT"bag_eq(
    """"'SELECT"*"FROM"users',
    """"$$"VALUES
    """"""""('theory',""""md5('foo'),""NOW()),
    """"""""('strongrrl',"md5('w00t'),"NOW())
    """"$$,
    """"'Should"still"have"just"the"two"users'
    );
    test/insert_use
    """"'Both"users"should"be"present'
    );
    137
    Wednesday, May 22, 13

    View full-size slide

  608. SELECT"throws_ok(
    """"$$"SELECT"insert_user('theory',"'ha*ha')"$$,
    """"23505,"**"duplicate"key"violation
    """"NULL,""**"localized"error"message
    """"'Should"get"an"error"for"duplicate"nickname'
    );
    SELECT"bag_eq(
    """"'SELECT"*"FROM"users',
    """"$$"VALUES
    """"""""('theory',""""md5('foo'),""NOW()),
    """"""""('strongrrl',"md5('w00t'),"NOW())
    """"$$,
    """"'Should"still"have"just"the"two"users'
    );
    test/insert_use
    """"'Both"users"should"be"present'
    );
    137
    Wednesday, May 22, 13

    View full-size slide

  609. SELECT"throws_ok(
    """"$$"SELECT"insert_user('theory',"'ha*ha')"$$,
    """"23505,"**"duplicate"key"violation
    """"NULL,""**"localized"error"message
    """"'Should"get"an"error"for"duplicate"nickname'
    );
    SELECT"bag_eq(
    """"'SELECT"*"FROM"users',
    """"$$"VALUES
    """"""""('theory',""""md5('foo'),""NOW()),
    """"""""('strongrrl',"md5('w00t'),"NOW())
    """"$$,
    """"'Should"still"have"just"the"two"users'
    );
    test/insert_use
    """"'Both"users"should"be"present'
    );
    SELECT"finish();
    ROLLBACK;
    137
    Wednesday, May 22, 13

    View full-size slide

  610. Functional Testing
    ""emacs"test/insert_user.sql
    >
    >
    138
    Wednesday, May 22, 13

    View full-size slide

  611. Functional Testing
    ""emacs"test/insert_user.sql
    >
    >
    ""emacs"deploy/insert_user.sql
    >
    138
    Wednesday, May 22, 13

    View full-size slide

  612. deploy/insert_u
    deploy/insert_user.sql
    **"Deploy"insert_user
    **"requires:"users
    **"requires:"appschema
    BEGIN;
    **"XXX"Add"DDLs"here.
    COMMIT;
    139
    Wednesday, May 22, 13

    View full-size slide

  613. deploy/insert_u
    deploy/insert_user.sql
    **"Deploy"insert_user
    **"requires:"users
    **"requires:"appschema
    BEGIN;
    CREATE"OR"REPLACE"FUNCTION"flipr.insert_user(
    """"nickname"TEXT,
    """"password"TEXT
    )"RETURNS"VOID"LANGUAGE"SQL"SECURITY"DEFINER"AS"$$
    """"INSERT"INTO"flipr.users"VALUES($1,"md5($2));
    $$;
    COMMIT;
    139
    Wednesday, May 22, 13

    View full-size slide

  614. Functional Testing
    ""emacs"deploy/insert_user.sql
    >
    >
    140
    Wednesday, May 22, 13

    View full-size slide

  615. Functional Testing
    ""emacs"deploy/insert_user.sql
    >
    >
    ""emacs"revert/insert_user.sql
    >
    140
    Wednesday, May 22, 13

    View full-size slide

  616. revert/insert_u
    revert/insert_user.sql
    **"Revert"insert_user
    BEGIN;
    COMMIT;
    **"XXX"Add"DDLs"here.
    141
    Wednesday, May 22, 13

    View full-size slide

  617. revert/insert_u
    revert/insert_user.sql
    **"Revert"insert_user
    BEGIN;
    COMMIT;
    DROP"FUNCTION"flipr.insert_user(TEXT,"TEXT);
    141
    Wednesday, May 22, 13

    View full-size slide

  618. Functional Testing
    ""emacs"revert/insert_user.sql
    >
    >
    142
    Wednesday, May 22, 13

    View full-size slide

  619. Functional Testing
    ""emacs"revert/insert_user.sql
    >
    >
    ""emacs"verify/insert_user.sql
    >
    142
    Wednesday, May 22, 13

    View full-size slide

  620. verify/insert_u
    verify/insert_user.sql
    **"Verify"insert_user
    BEGIN;
    **"XXX"Add"DDLs"here.
    ROLLBACK;
    143
    Wednesday, May 22, 13

    View full-size slide

  621. verify/insert_u
    verify/insert_user.sql
    **"Verify"insert_user
    BEGIN;
    SELECT"has_function_privilege(
    """"'flipr.insert_user(text,"text)',
    """"'execute'
    );
    ROLLBACK;
    143
    Wednesday, May 22, 13

    View full-size slide

  622. verify/insert_u
    verify/insert_user.sql
    **"Verify"insert_user
    BEGIN;
    SELECT"has_function_privilege(
    """"'flipr.insert_user(text,"text)',
    """"'execute'
    );
    ROLLBACK;
    Convenient!
    143
    Wednesday, May 22, 13

    View full-size slide

  623. We Good?
    ""emacs"verify/insert_user.sql
    >
    >
    144
    Wednesday, May 22, 13

    View full-size slide

  624. We Good?
    ""emacs"verify/insert_user.sql
    >
    >
    ""pg_prove"*d"flipr_test"test/*.sql""""""""""
    test/appschema.sql"...."ok"""
    test/insert_user.sql".."ok"""""
    test/users.sql"........"ok""""
    All"tests"successful.
    Files=3,"Tests=27,""0"wallclock"secs
    Result:"PASS
    >
    144
    Wednesday, May 22, 13

    View full-size slide

  625. Commitment
    >
    145
    Wednesday, May 22, 13