SLIDE 1 The Technical Debt
TRAP
@DocOnDev
SLIDE 2 @DocOnDev
I’m Doc Norton.
CEO at CTO2 @DocOnDev
SLIDE 3 @DocOnDev
I’m Doc Norton.
CEO at CTO2 @DocOnDev
http://leanpub.com/escapevelocity
SLIDE 4 The Technical Debt
TRAP
@DocOnDev
SLIDE 5 @DocOnDev
What is Technical Debt?
SLIDE 6 @DocOnDev
“
Shipping first time code is like going into debt.
SLIDE 7 @DocOnDev
“
Shipping first time code is like going into debt. A little debt speeds development so long as it is paid back with a rewrite.
SLIDE 8 @DocOnDev
“
Shipping first time code is like going into debt. A little debt speeds development so long as it is paid back with a rewrite. The danger
not repaid. Every minute spent on not-quite-right code counts as interest on that debt.
SLIDE 9 “
@DocOnDev
The danger occurs when the debt is not repaid. Every minute spent on not-quite-right code counts as interest on that debt.
SLIDE 10 “
@DocOnDev
The danger occurs when the debt is not repaid. Every minute spent on not-quite-right code counts as interest on that debt.
SLIDE 11 “
@DocOnDev
The danger occurs when the debt is not repaid. Every minute spent on not-quite-right code counts as interest on that debt.
SLIDE 12 “
@DocOnDev
The danger occurs when the debt is not repaid. Every minute spent on not-quite-right code counts as interest on that debt.
SLIDE 13 @DocOnDev
Technical Debt Is
SLIDE 14 @DocOnDev
Technical Debt is
GOOD
SLIDE 15 @DocOnDev
Use captivating images Technical Debt is
GOOD
@DocOnDev
SLIDE 16 @DocOnDev
Technical Debt is
GOOD
SLIDE 17 @DocOnDev
Technical Debt is
A Strategic Design Decision
1
@DocOnDev
SLIDE 18 @DocOnDev
- Allow for Rapid Delivery
- To Elicit Quick Feedback
- And To Correct Design
Technical Debt is a Strategic Design Decision
@DocOnDev
SLIDE 19 @DocOnDev
Technical Debt is
A Strategic Design Decision
1 2
An Indication of Learning
@DocOnDev
SLIDE 20 @DocOnDev
- Now Know What You Need
- And Implementation Doesn’t
Match
Technical Debt is an Indication of Learning
@DocOnDev
SLIDE 21 @DocOnDev
Technical Debt is
A Strategic Design Decision
1 2
An Indication of Learning
3
A Metaphor
@DocOnDev
SLIDE 22 @DocOnDev
Technical Debt is a Metaphor
@DocOnDev
SLIDE 23 @DocOnDev
We Reason By Analogy
Metaphors Rock!
Building on a weak foundation
Puts pressure on our design
Can’t keep running at this pace
It’s raining men
(hallelujah)
SLIDE 24 @DocOnDev
When Metaphors Go Wrong!
Metaphorphosis
Technical Debt
SLIDE 25 @DocOnDev
When Metaphors Go Wrong!
Metaphorphosis
Credit Card
Short-Term
Long-Term
Return on Investment
Operational
Home Loan
Loan Shark
Pragmatic Leverage
Intentional
Auto Loan
Fraudulent
Voluntary
Pyramid Scheme
Prudent
Student Loan
High Interest
Inadvertent Reckless Debt in the Third Quadrant
Technical Debt
SLIDE 26 @DocOnDev
When
Metaphors Go Wrong
@DocOnDev
SLIDE 27 @DocOnDev
“quick and dirty”
“sloppy”
“just hack it in”
“cut a lot of corners”
When
Metaphors Go Wrong
SLIDE 28 @DocOnDev
Reckless Inadvertent Prudent Deliberate
“We don’t have time for design” “We must ship now and deal with consequences” “Now we know how we should have done it” “What’s Layering?”
Technical Debt Quadrant
@DocOnDev
SLIDE 29 @DocOnDev
“
[Many] have explained the debt metaphor and confused it with the idea that you could write code poorly with the intention of doing a good job later.
SLIDE 30 “
@DocOnDev
…confused the debt metaphor with the idea that you could write code poorly…
SLIDE 31 @DocOnDev
“
The ability to pay back debt [...] depends upon you writing code that is clean enough to be able to refactor as you come to understand your problem.
SLIDE 32 “
@DocOnDev
The ability to pay back debt [...] depends upon you writing code that is clean enough to be able to refactor as you come to understand your problem.
SLIDE 33 @DocOnDev
“
Dirty code is to technical debt as the pawn broker is to financial debt. Don’t think you are ever going to get your code back.
SLIDE 34 @DocOnDev
Do I Have
Technical
Debt? Is the code clean? 4 5 Is the code tested? 1 Is there a learning opportunity? 2 Is there a plan for payback? 3 Is the business truly informed?
@DocOnDev
SLIDE 35 @DocOnDev
Do I Have
Technical
Debt? Is the code clean? 4 5 Is the code tested? 1 Is there a learning opportunity? 2 Is there a plan for payback? 3 Is the business truly informed?
@DocOnDev
SLIDE 36 Mess (Noun)
- Disorderly accumulation, heap,
- r jumble
- A state of embarrassing
confusion
- An unpleasant or difficult
situation
@DocOnDev
SLIDE 37 Cruft (Noun)
- An unpleasant substance
- The result of shoddy
construction
- Redundant, old or improperly
written code
@DocOnDev
SLIDE 38 Cruft (Noun)
- An unpleasant substance
- The result of shoddy
construction
- Redundant, old or improperly
written code
@DocOnDev
SLIDE 39 @DocOnDev
But, It’s Just Semantics, Doc.
SLIDE 40 @DocOnDev
It’s Not Just Semantics.
SLIDE 41 @DocOnDev
It’s Not Just Semantics.
Technical Debt is
SLIDE 42 @DocOnDev
It’s Not Just Semantics.
Quick and Dirty is Technical Technical Debt is Good
SLIDE 43 @DocOnDev
It’s Not Just Semantics.
Quick and Dirty is
SLIDE 44 @DocOnDev
Reckless Inadvertent Prudent Deliberate
“We don’t have time for design” “We must ship now and deal with consequences” “We must ship now and deal with consequences” “Now we know how we should have done it” “What’s Layering?”
Technical Debt Quadrant
SLIDE 45 @DocOnDev
Reckless Inadvertent Prudent Deliberate
“We don’t have time for design” “Let’s deploy and gather more information.” “Now we know how we should have done it” “What’s Layering?”
Technical Debt Quadrant
SLIDE 46 @DocOnDev
“Technical Debt”
In Other Fields
SLIDE 47 @DocOnDev
“Technical Debt”
in other fields Construction
@DocOnDev
SLIDE 48 @DocOnDev
Use captivating images “Technical Debt”
in other fields Automotive
@DocOnDev
SLIDE 49 @DocOnDev
“Technical Debt”
in other fields Medical
@DocOnDev
SLIDE 50 @DocOnDev
R e c k l e s s a n d D e l i b e r a t e R e c k l e s s a n d I n a d v e r t e n t “Technical Debt”
in other fields
SLIDE 51 @DocOnDev
Reckless Inadvertent Prudent Deliberate
“We don’t have time for design” “Let’s deploy and gather more information.” “Now we know how we should have done it” “What’s Layering?”
Technical Debt Quadrant
Reckless and Deliberate Reckless and Inadvertent
SLIDE 52 @DocOnDev
Reckless Inadvertent Prudent Deliberate
“We don’t have time for design” “Let’s deploy and gather more information.” “Now we know how we should have done it” “What’s Layering?”
Technical Debt Quadrant
Irresponsible Incompetent
T e c h n i c a l
D e b t
SLIDE 53 @DocOnDev
Cruft or Debt?
SLIDE 54 @DocOnDev
1
Cruft or Debt?
@DocOnDev
SLIDE 55 @DocOnDev
Cruft or Debt?
DataSet aDs, qDs; aDs = _dbConnector.UpdateAgentList(); qDs = _dbConnector.GetQueueList(); foreach (DataTable aTable in aDs.Tables) { foreach (DataRow aRow in aTable.Rows) { foreach (DataColumn aColumn in aTable.Columns) { DataSet asDs = _dbConnector.GetAgentSkills(aRow[aColumn].ToString());//AgentId foreach (DataTable asTable in asDs.Tables) { foreach (DataRow asRow in asTable.Rows) { foreach (DataColumn asColumn in asTable.Columns) { foreach (DataTable qTable in qDs.Tables) { foreach (DataRow qRow in qTable.Rows) { foreach (DataColumn qColumn in qTable.Columns) { DataSet sqDs = _dbConnector.GetSkillsForQueue(qRow[qColumn].ToString()); foreach (DataTable sqTable in sqDs.Tables) { foreach (DataRow sqRow in sqTable.Rows) { foreach (DataColumn sqColumn in sqTable.Columns) { foreach (string skill in sqRow[sqColumn].ToString().Split(paramDelimStr)) { if (skill == asRow[asColumn].ToString()) { try { _dbConnector.SetAgentQueueSkill(aRow[aColumn].ToString(), qRow[qColumn].ToString(), skill); } catch { continue; } } } } } } } } } } } } } } }
SLIDE 56 @DocOnDev
Cruft or Debt?
DataSet aDs, qDs; aDs = _dbConnector.UpdateAgentList(); qDs = _dbConnector.GetQueueList(); foreach (DataTable aTable in aDs.Tables) { foreach (DataRow aRow in aTable.Rows) { foreach (DataColumn aColumn in aTable.Columns) { DataSet asDs = _dbConnector.GetAgentSkills(aRow[aColumn].ToString());//AgentId foreach (DataTable asTable in asDs.Tables) { foreach (DataRow asRow in asTable.Rows) { foreach (DataColumn asColumn in asTable.Columns) { foreach (DataTable qTable in qDs.Tables) { foreach (DataRow qRow in qTable.Rows) { foreach (DataColumn qColumn in qTable.Columns) { DataSet sqDs = _dbConnector.GetSkillsForQueue(qRow[qColumn].ToString()); foreach (DataTable sqTable in sqDs.Tables) { foreach (DataRow sqRow in sqTable.Rows) { foreach (DataColumn sqColumn in sqTable.Columns) { foreach (string skill in sqRow[sqColumn].ToString().Split(paramDelimStr)) { if (skill == asRow[asColumn].ToString()) { try { _dbConnector.SetAgentQueueSkill(aRow[aColumn].ToString(), qRow[qColumn].ToString(), skill); } catch { continue; } } } } } } } } } } } } } } }
SLIDE 57 @DocOnDev
Cruft or Debt?
DataSet aDs, qDs; aDs = _dbConnector.UpdateAgentList(); qDs = _dbConnector.GetQueueList(); foreach (DataTable aTable in aDs.Tables) { foreach (DataRow aRow in aTable.Rows) { foreach (DataColumn aColumn in aTable.Columns) { DataSet asDs = _dbConnector.GetAgentSkills(aRow[aColumn].ToString());//AgentId foreach (DataTable asTable in asDs.Tables) { foreach (DataRow asRow in asTable.Rows) { foreach (DataColumn asColumn in asTable.Columns) { foreach (DataTable qTable in qDs.Tables) { foreach (DataRow qRow in qTable.Rows) { foreach (DataColumn qColumn in qTable.Columns) { DataSet sqDs = _dbConnector.GetSkillsForQueue(qRow[qColumn].ToString()); foreach (DataTable sqTable in sqDs.Tables) { foreach (DataRow sqRow in sqTable.Rows) { foreach (DataColumn sqColumn in sqTable.Columns) { foreach (string skill in sqRow[sqColumn].ToString().Split(paramDelimStr)) { if (skill == asRow[asColumn].ToString()) { try { _dbConnector.SetAgentQueueSkill(aRow[aColumn].ToString(), qRow[qColumn].ToString(), skill); } catch { continue; } } } } } } } } } } } } } } }
SLIDE 58 @DocOnDev
Cruft or Debt?
DataSet aDs, qDs, asDs, sqDs; aDs = _dbConnector.UpdateAgentList(); qDs = _dbConnector.GetQueueList(); foreach (DataRow aRow in aDS.Tables[0].Rows) { String agentID = aRow[“AgentId”].ToString(); asDs = _dbConnector.GetAgentSkills(agentID); foreach (DataRow asRow in asDs.Tables[0].Rows) { String agentSkill = asRow[“Skill”].ToString(); foreach (DataRow qRow in qDs.Tables[0].Rows) { queueName = qRow[“QueueName”].ToString(); sqDs = _dbConnector.GetSkillsForQueue(queueName); foreach (DataRow sqRow in sqDs.Tables[0].Rows) { foreach (string skill in sqRow[“Skills”].ToString().Split(paramDelimStr)) { if (skill == agentSkill) { try { _dbConnector.SetAgentQueueSkill(agentID, queueName, skill); } catch { continue; } } } } } } }
SLIDE 59 @DocOnDev
Cruft or Debt?
AgentList agents = new AgentList(_dbConnector.UpdateAgentList()); QueueList queues = new QueueList(_dbConnector.GetQueueList()); foreach (Agent agent in agents) { foreach (Skill agentSkill in agent.skills) { foreach (Queue queue in queues) { foreach (Skill queueSkill in queue.skills.Where(x => x == agentSkill)) { try {_dbConnector.SetAgentQueueSkill(agent.agentID, queue.name, agentSkill); } catch { continue; } } } } }
SLIDE 60 @DocOnDev
2
Cruft or Debt?
@DocOnDev
SLIDE 61 @DocOnDev
Cruft or Debt?
if ((customer.state == “AL” && customer.type == CustomerType.GENERAL_AGENT && customer.revenue > 100000) || (customer.type == CustomerType.RETRO_AGENT && (customer.state == “WI” || customer.state == “IL”)) || (customer.type == CustomerType.FED_MANAGEMENT && customer.revenue > 150000)) { ... }
SLIDE 62 @DocOnDev
Cruft or Debt?
if ((customer.state == “AL” && customer.type == CustomerType.GENERAL_AGENT && customer.revenue > 100000) || (customer.type == CustomerType.RETRO_AGENT && (customer.state == “WI” || customer.state == “IL”)) || (customer.type == CustomerType.FED_MANAGEMENT && customer.revenue > 150000)) { ... }
SLIDE 63 @DocOnDev
Cruft or Debt?
// If customer is Federally Regulated if ((customer.state == “AL” && customer.type == CustomerType.GENERAL_AGENT && customer.revenue > 100000) || (customer.type == CustomerType.RETRO_AGENT && (customer.state == “WI” || customer.state == “IL”)) || (customer.type == CustomerType.FED_MANAGEMENT && customer.revenue > 150000)) { ... }
SLIDE 64 @DocOnDev
Cruft or Debt?
if (customer.isFederallyRegulated()) { ... }
SLIDE 65 @DocOnDev
3
Cruft or Debt?
Bonus Points - Name The Movie
@DocOnDev
SLIDE 66 @DocOnDev
Cruft or Debt?
double getSpeed() { switch (_type) { case EUROPEAN: return getBaseSpeed(); case AFRICAN: return getBaseSpeed() - getLoadFactor() * _numberOfCoconuts; case NORWEGIAN_BLUE: return (_isNailed) ? 0 : getBaseSpeed(_voltage); } throw new RuntimeException ("Should be unreachable"); }
SLIDE 67 @DocOnDev
Cruft or Debt?
double getSpeed() { switch (_type) { case EUROPEAN: return getBaseSpeed(); case AFRICAN: return getBaseSpeed() - getLoadFactor() * _numberOfCoconuts; case NORWEGIAN_BLUE: return (_isNailed) ? 0 : getBaseSpeed(_voltage); } throw new RuntimeException ("Should be unreachable"); }
SLIDE 68 @DocOnDev
Cruft or Debt?
class Swallow ... double getSpeed() { return getBaseSpeed(); } end class class EuropeanSwallow ... end class class AfricanSwallow ... double getSpeed() { return super.getSpeed - coconutLoad(); } double coconutLoad() { return getLoadFactor() * _numberOfCoconuts; } end class class NorwegianSwallow ... double getSpeed() { return (_isNailed) ? 0 : getBaseSpeed(_voltage); } end class
SLIDE 69 @DocOnDev
Don’t Do Cruft! 1 You’re a Professional Developer 2 You’re Going to Create Unintentional Cruft 3 You Have to Clean Up Existing Cruft
CRUFT!
@DocOnDev
SLIDE 70 @DocOnDev
The Trap! 1 Precedent for Speed over Quality 2 Expectation of Increased Velocity 3 Cruft Slows You Down 4 Must Write More Cruft to Keep Up 5 Ask Permission to do You Job Correctly
SLIDE 71 @DocOnDev
Managing Cruft
SLIDE 72 @DocOnDev
1
Failing Strategy
SLIDE 73 @DocOnDev
Cleaning Iteration
- Schedule Iterations for cleaning code
- Defer quality to cleaning sprint
- Focus on speed/velocity at all other times
SLIDE 74 @DocOnDev
Use captivating images Cleaning Iteration
@DocOnDev
SLIDE 75 @DocOnDev
1
Winning Strategy
SLIDE 76 @DocOnDev
Clean Constantly
- Never make an intentional mess
- Monitor your “Technical Debt”
- Follow the Boy Scout Rule
- Remember quality is your responsibility
- NEVER ask permission to do your job correctly
SLIDE 77 @DocOnDev
Monitor Your Cruft
- Code Coverage
- Code Complexity
- Churn
- Maintainability
- Monitor Trends, Not Points
SLIDE 78 Title Text
HERE Review
Technical Debt
- A strategic design decision
- Requires business to be informed
- Includes a pay-back plan
Cruft
- Happens
- Needs to be monitored and cleaned
- Is NOT Technical Debt
@DocOnDev
SLIDE 79 Title Text
HERE Review
Technical Debt
- A strategic design decision
- Requires business to be informed
- Includes a pay-back plan
Cruft
- Happens
- Needs to be monitored and cleaned
- Is NOT Technical Debt
NEVER ASK PERMISSION TO DO YOUR JOB CORRECTLY
@DocOnDev
SLIDE 80 @DocOnDev
THANK YOU!
SLIDE 81 @DocOnDev
For slides and research:
Send a blank email to cto2@SendYourSlides.com with the subject line: TechnicalDebt
THANK YOU!