How to Bring Down Giants Planning and Executing Epics, the Agile - - PowerPoint PPT Presentation

how to bring down giants
SMART_READER_LITE
LIVE PREVIEW

How to Bring Down Giants Planning and Executing Epics, the Agile - - PowerPoint PPT Presentation

How to Bring Down Giants Planning and Executing Epics, the Agile Way HELLO! Stjepan Rajko stjepanr@axosoft.com @dancinghacker Refactoring Database SettingsUI Transactions NodeGit GitKraken Features Memory Submodules Management Right


slide-1
SLIDE 1

How to Bring Down Giants

Planning and Executing Epics, the Agile Way

slide-2
SLIDE 2

HELLO!

Stjepan Rajko

stjepanr@axosoft.com @dancinghacker

slide-3
SLIDE 3

Database Transactions NodeGit Memory Management SettingsUI GitKraken Submodules Refactoring Features Right Wrong

slide-4
SLIDE 4

Database Transactions

Refactoring gone right DB Transactions 101: Unit of work, composed of multiple operations

Deduct money from source account Add money to target account

slide-5
SLIDE 5

Problem #1

DeductMoney(int accountId, int amount, TransactionHelper transaction=null) { // if we forget to pass transaction, GetAccount may block var account = GetAccount(accountId, transaction); account.Deduct(amount); // if we forget to pass transaction, Save may block account.Save(transaction); }

slide-6
SLIDE 6

Problem #2

TransferMoney(..., TransactionHelper transaction=null) { var makeNewTransaction = transaction == null; if (makeNewTransaction ) { SqlHelper.BeginTransaction(connectionString, out transaction); } try { ... } catch { SqlHelper.RollbackTransaction(transaction); throw; } if (makeNewTransaction && SqlHelper.CommitTransaction(transaction)) { throw new SqlTransactionException(); } }

slide-7
SLIDE 7

Problem #3

Save(TransactionHelper transaction == null) { if(transaction != null) { SQLHelper.Execute(transaction, “UPDATE ACCOUNTS... “); } else { SQLHelper.Execute(connectionString, “UPDATE ACCOUNTS... “); } }

slide-8
SLIDE 8

Step #1:

Simplify common usage

Execute(Transaction transaction, string sql) { if(transaction != null) { SQLHelper.Execute(transaction, sql); } else { SQLHelper.Execute(connectionString, sql); } }

slide-9
SLIDE 9

Step #1:

68 changed files with 569 additions and 1,037 deletions.

Save(TransactionHelper transaction=null) { Execute(transaction, “UPDATE ACCOUNTS... “); }

slide-10
SLIDE 10

Step #2:

Simplify more common usage

TransferMoney(..., TransactionHelper transaction=null) { var makeNewTransaction = transaction == null; if (makeNewTransaction ) { SqlHelper.BeginTransaction(connectionString, out transaction); } try { ... } catch { SqlHelper.RollbackTransaction(transaction); throw; } if (makeNewTransaction && SqlHelper.CommitTransaction(transaction)) { throw new SqlTransactionException(); } }

slide-11
SLIDE 11

Step #2:

35 changed files with 1,015 additions and 1,916 deletions.

void TransferMoney(int accountId, int amount, TransactionHelper transaction=null) { using (Transaction(transaction)) { ... } }

slide-12
SLIDE 12

Step #3:

void TransferMoney(int accountId, int amount, TransactionHelper transaction=null) { // now stores active transaction per thread+connectionString: using (Transaction(transaction)) { ... } } Save(TransactionHelper transaction == null) { // now checks whether transaction matches the stored active transaction Execute(transaction, “UPDATE ACCOUNTS... “); }

slide-13
SLIDE 13

Step #4:

70 changed files with 523 additions and 528 deletions

TransferMoney(int accountId, int amount) { using (Transaction()) { ... } } Save() { Execute(“UPDATE ACCOUNTS... “); }

slide-14
SLIDE 14

The timeline

START

Mon Mon

FINISH

slide-15
SLIDE 15

Lessons learned:

Find a step in the right direction that stands on its own START REPEAT At any point, you can STOP if needed Also: LISTEN TO YOUR USERS

slide-16
SLIDE 16

SettingsUI

Refactoring gone wrong

slide-17
SLIDE 17

Good people, good intentions...

Bad idea #1

1. Implement Customer Portal Settings from scratch 2. Implement System Settings - copy/pasted from System Settings 3. Roll our own component to support the two similar settings pages

slide-18
SLIDE 18

The SettingsUI component

  • Gets data from the server

{ types: { defects: { enabled: true, ... }, ... }, ... }

  • Binds html to data

<span data-display-requires="types.defects"> <input type="checkbox" data-id="types.defects.enabled"/> <label>Defect Backlog</label> </span>

  • Sends updated data to the server
slide-19
SLIDE 19

Problem

As SettingsUI expands to support more and more pages, it becomes an unmaintainable mess

slide-20
SLIDE 20

Good people, good intentions...

Bad idea #2

SettingsUI

  • Gets data from the server
  • Binds html to data
  • Sends updated data to the server

FormHelper

  • Gets data from the server
  • Binds html to data using Knockout.js
  • Sends updated data to the server

Copy / Pasted

slide-21
SLIDE 21

Good people, good intentions...

Bad idea #3 No migration plan

slide-22
SLIDE 22

Problem now

  • One bad component
  • One better component
  • Two components to deal with
slide-23
SLIDE 23

The timeline

Knockout.js released Angular.js released SettingsUI FormHelper

2010 2012 2013 2016

slide-24
SLIDE 24

Lessons learned:

Use external components Have a full migration plan OR: Open-source your component

slide-25
SLIDE 25

GitKraken Submodules

Feature gone wrong

Main Repository Submodule Submodule

slide-26
SLIDE 26

Me

Idiot

Can this be done in two weeks? Sure!

Hamid Shojaee

V.P. Product

slide-27
SLIDE 27

Initial team

NOT 2 WEEKS It took 2+ months

Git Submodules No detailed plan / understanding of requirements Uncertain submodule support in libgit2 / NodeGit Unfamiliarity

slide-28
SLIDE 28

Lessons learned:

When wrong, STOP Rethink / replan Resume Or: DON’T resume THINK before you estimate

slide-29
SLIDE 29

NodeGit Memory Management

Feature going right NodeGit (JavaScript) libgit2 (C)

slide-30
SLIDE 30

The challenge

C vs. JavaScript

git_repository *repository; git_commit *commit; git_signature *signature1, *signature2; git_oid oid; git_oid_fromstr(&oid, “123456789…”); git_repository_open(&repository, “/path/to/repo”); git_commit_lookup(&commit, repository, oid); signature1 = git_commit_author(commit); git_signature_default(&signature2, repository); git_commit_free(commit); git_signature_free(signature2); git_repository_free(repository); repository = NodeGit.Repository.open("/path/to/repo"); commit = repository.lookupCommit("123456789…"); signature1 = commit.author(); signature2 = repository.defaultSignature();

slide-31
SLIDE 31

Problem

  • If you free incorrectly, code starts to crash
slide-32
SLIDE 32

Current Solution

Don’t free!

slide-33
SLIDE 33

Plan of attack

  • One type at a time
  • Add memory management mechanisms as needed

Effort per type

slide-34
SLIDE 34

Lessons learned:

Effort per type is fickle Consistent progress is good

slide-35
SLIDE 35

Main takeaways:

Find a good first step to tackling the giant Develop plan to defeat giant completely Don’t create giant problems When realizing you are facing a giant, STOP

slide-36
SLIDE 36

Stjepan Rajko

stjepanr@axosoft.com www.axosoft.com

THANK YOU!