The Technical Debt Trap - AgileIndy 2013

3,935 views

Published on

A visually updated version of the slide deck.

0 Comments
3 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
3,935
On SlideShare
0
From Embeds
0
Number of Embeds
1,630
Actions
Shares
0
Downloads
41
Comments
0
Likes
3
Embeds 0
No embeds

No notes for slide
  • Design decisions that allow for more rapid delivery / illicit quick feedback / gather data necessary to correct design
  • - Plan to sell several different products. Starting with just one. Don’t extract interface yet.
  • Allows us to establish a simple understanding of a complex problem in common representational terms
  • "We incurred structural debt in order to meet your deadline. We should discuss that debt and set a plan for paying it back later."
  • "We incurred mechanical debt to stay in budget. We should get metrics around that and make sure we pay the debt down in the future."
  • "We incurred health debt during the surgery. You see, it is like we paid for the surgery with a credit card instead of a home equity loan..."
  • First, let’s get rid of the curlies for readability
  • Now, let’s assume we don’t have multiple tables in our DataSets nor do we need to iterate through multiple columns in cases where we’re looking for an AgentID or queueName
  • Now let’s introduce some abstraction. We’ll create some classes that receive DataSets and return collections of objects. We’re not showing these here. Our ORM could do this for us. And let’s not make the same series of calls for queue data for every agent.
  • Now we can see what this does. We get a list of skills for an agent, compare them to a list of skills for a queue and call SetAgentQueueSkill for all matches. Seems to me, this code shouldn’t exist. The AgentQueueSkill table should be a view in the dB.
  • Is this where this evaluation belongs? Probably not. What are we checking for here? Perhaps a comment will help us.
  • Better? Not really. But what if that comment was code?
  • What about this one?
  • Why is this NOT cruft? We clearly have more code.
  • 1. Gartner Report in 2009 :: Includes Hardware 2. CAST Study in 2009 :: No correlation between size and debt; more modular == less debt
  • Ruby - churn => volume of changes git - gitswarm => visual history of changes Java - Cobertura or Sonar
  • Ruby - churn => volume of changes git - gitswarm => visual history of changes Java - Cobertura or Sonar
  • Java - Cobertura, Clover, EMMA .NET - NCover Ruby - rcov / metric_fu
  • Java - Cobertura, Clover, EMMA .NET - NCover Ruby - rcov / metric_fu
  • NDepend JDepend
  • The Technical Debt Trap - AgileIndy 2013

    1. 1. The Technical Debt TrapMichael “Doc” Norton@DocOnDev
    2. 2. Michael “Doc” NortonGrouponDirector ofTechnologydoc@groupon.com@DocOnDevHusband Father
    3. 3. GrandFatherHe’s SOOO Cute!!
    4. 4. What is Technical Debt?
    5. 5. WardCunninghamShipping first time code islike going into debt. OOPSLA ’92
    6. 6. WardCunninghamShipping first time code islike going into debt. A little debt speedsdevelopment so long as it ispaid back promptly with arewrite. OOPSLA ’92
    7. 7. WardCunninghamThe danger occurs whenthe debt is not repaid.Every minute spent on not-quite-right code counts asinterest on that debt. OOPSLA ’92
    8. 8. WardCunninghamThe danger occurs whenthe debt is not repaid.Every minute spent on not-quite-right code counts asinterest on that debt. OOPSLA ’92
    9. 9. Technical Debt is a Good Idea
    10. 10. Technical Debt is a Good IdeaWTF?
    11. 11. Technical Debt is a Good IdeaStrategic Design Decision Allow for Rapid Delivery To Elicit Quick Feedback And Correct Design
    12. 12. Technical Debt is a MetaphorHere Be Danger
    13. 13. Metaphors Rock We Reason By Analogy Can’t keep running at thisBUILDING ON pace A WEAK FOUNDATIONIT’S RAINING MEN (HALLELUJAH) Puts pressure on our design
    14. 14. Metaphorphosis When Metaphors Go Wrong Return on InvestmentSHORT-TERM Fraudulent Credit Card Inadvertent Reckless AUTO LOAN Debt in the Third PYRAMID SCHEMEHIGH INTEREST Intentional Quadrant Long-Term HOME LOAN Loan Shark VOLUNTARY PrudentPragmatic Leverage Student Loan OPERATIONAL
    15. 15. Metaphorphosis When Metaphors Go Wrong “CUT A LOT OF CORNERS” JAMES SHORE HTTP://JAMESSHORE.COM/BLOG/CARDMEETING/VOLUNTARY-TECHNICAL-DEBT.HTML “QUICK AND DIRTY” MARTIN FOWLER “SLOPPY” HTTP://WWW.MARTINFOWLER.COM/BLIKI/TECHNICALDEBT.HTML DAVID LARIBEE “JUST HACK IT IN”HTTP://MSDN.MICROSOFT.COM/EN-US/MAGAZINE/EE819135.ASPX STEVE MCCONNELL HTTP://BLOGS.CONSTRUX.COM/BLOGS/STEVEMCC/ARCHIVE/2007/11/01/TECHNICAL-DEBT-2.A
    16. 16. The Technical Debt Quadrant http://martinfowler.com/bliki/TechnicalDebtQuadrant.html
    17. 17. Ummm... no.[Many] have explained thedebt metaphor andconfused it with the ideathat you could write codepoorly with the intention ofdoing a good job later. http://www.youtube.com/watch?v=pqeJFYwnkjE&feature=player_embedded YouTube.com ’09
    18. 18. Ummm... no.[Many] have explained thedebt metaphor andconfused it with the ideathat you could write codepoorly with the intention ofdoing a good job later. http://www.youtube.com/watch?v=pqeJFYwnkjE&feature=player_embedded YouTube.com ’09
    19. 19. [Many] have explained thedebt metaphor andconfused it with the ideathat you could write codepoorly with the intention ofdoing a good job later.
    20. 20. ... confused itwith the idea thatyou could writecode poorly ....
    21. 21. Clean Codeis RequiredThe ability to pay back debt[...] depends upon youwriting code that is cleanenough to be able torefactor as you come tounderstand your problem. http://www.youtube.com/watch?v=pqeJFYwnkjE&feature=player_embedded YouTube.com ’09
    22. 22. Clean Codeis RequiredThe ability to pay back debt[...] depends upon youwriting code that is cleanenough to be able torefactor as you come tounderstand your problem. http://www.youtube.com/watch?v=pqeJFYwnkjE&feature=player_embedded YouTube.com ’09
    23. 23. Dirty Code isa BAD IdeaDirty code is to technicaldebt as the pawn broker isto financial debt.Don’t think you are evergoing to get your code back. http://twitter.com/WardCunningham/status/3742903303 twitter ’09
    24. 24. Is It Technical Debt?Ask yourself ... Is the code clean? Is the code tested? Is there a learning objective? Is there a plan for payback? Is the business truly informed?
    25. 25. Is It Technical Debt?If you say no to even one... Is the code clean? Is the code tested? Is there a learning objective? Is there a plan for payback? Is the business truly informed? ... then you don’t have Technical Debt
    26. 26. You have a messMess (noun)Disorderly accumulation, heap, or jumbleA state of embarrassing confusionAn unpleasant or difficult situation
    27. 27. You have cruftCruft (noun)An unpleasant substanceThe result of shoddy constructionRedundant, old or improperly written code
    28. 28. Chill. It’s just semantics,man.Just Semantics?Technical Debt is Good
    29. 29. Chill. It’s just semantics,man.Just Semantics?Quick and Dirty is Technical Debtis Good Technical Debt is Good
    30. 30. Chill. It’s just semantics,man.Just Semantics?Quick and Dirtyis Good Dirty is Good
    31. 31. The Technical Debt Quadrant http://martinfowler.com/bliki/TechnicalDebtQuadrant.html
    32. 32. The Technical Debt Quadrant “Let’s deploy and gather more information.” http://martinfowler.com/bliki/TechnicalDebtQuadrant.html
    33. 33. Technical Debt in otherfieldsConstruction TE R A IB E E L D D AN S S LE CK RE
    34. 34. Technical Debt in otherfieldsAutomotive TE R A IB E E L D D AN S S LE CK RE
    35. 35. Technical Debt in otherfieldsMedical NT TE E R D V IN A N D S A ES K L EC R
    36. 36. The Technical Debt Quadrant LE CA L SI B N I “Let’s deploy and N H T E C EB PO gather more ES T information.” IRR D N T PE TE M INCO http://martinfowler.com/bliki/TechnicalDebtQuadrant.html
    37. 37. DataSet aDs, qDs;aDs = _dbConnector.UpdateAgentList();qDs = _dbConnector.GetQueueList();foreach (DataTable aTable in aDs.Tables) { Cruft or Debt? 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; } } } } } } } } } } } } } }} http://thedailywtf.com/Series/2010/3/CodeSOD.aspx
    38. 38. 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; } }} } } } } } } } } } } } } http://thedailywtf.com/Series/2010/3/CodeSOD.aspx
    39. 39. 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; } } } } } }} http://thedailywtf.com/Series/2010/3/CodeSOD.aspx
    40. 40. 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; } } } }} http://thedailywtf.com/Series/2010/3/CodeSOD.aspx
    41. 41. 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)) { ... }
    42. 42. Cruft or Debt?// If customer is Federally Regulatedif ((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)) { ... }
    43. 43. Cruft or Debt?if (customer.isFederallyRegulated()) { ... }
    44. 44. 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 newRuntimeException ("Should be unreachable");} http://www.refactoring.com/catalog/replaceConditionalWithPolymorphism.html
    45. 45. Cruft or Debt?class Swallow ... double getSpeed() { return getBaseSpeed(); }endclassclass EuropeanSwallow ...end classclass AfricanSwallow ... doublegetSpeed() { return super.getSpeed - coconutLoad(); } doublecoconutLoad() { return getLoadFactor() * _numberOfCoconuts; }endclassclass NorwegianSwallow ... double getSpeed() { return(_isNailed) ? 0 : getBaseSpeed(_voltage); }end class
    46. 46. Cruft is a bad decisionEvery TimeYOU ARE A PROFESSIONAL DEVELOPER (Professionals behave ethically)YOU ARE GOING TO CREATE UNINTENTIONAL CRUFT (You can’t help it. It’s unintentional.)YOU HAVE TO CLEAN UP THE EXISTING CRUFT (Intent doesn’t alter Responsibility)
    47. 47. The Cost of “Technical Debt”We did thisWORLD-WIDE TECHNICAL DEBT (INCLUDES HARDWARE) $1 Trillion (by 2015)PER BUSINESS APPLICATION $1 Million
    48. 48. GrammyDoesn’tLoveMe!Me!Me!It’s all about expectations
    49. 49. The TrapCruft begets cruftPrecedent for speed over qualityExpectation of increased velocityCruft slows you downMust write more cruft to keep up Ask Permission to do your job correctly
    50. 50. Avoid The TrapThresholds set falseexpectations http://blog.castsoftware.com/wp-content/uploads/2011/04/Technical-Debt-Software-Quality1.jpg
    51. 51. Avoid The TrapIncremental fixes fail http://www.jacoozi.com/blog/wp-content/uploads/2007/01/refactoring_coc_big.jpg
    52. 52. Avoid The TrapIncremental fixes fail http://www.jacoozi.com/blog/wp-content/uploads/2007/01/refactoring_coc_big.jpg
    53. 53. Avoid The TrapClean ConstantlyNever make an intentional messMonitor your “Technical Debt”Follow the Boy Scout RuleRemember quality is your responsibility NEVER Ask Permission to do your job correctly
    54. 54. Monitoring Cruft/DebtA few key metricsCode CoverageCyclomatic ComplexityCouplingMaintainability
    55. 55. Monitoring Cruft/DebtCode CoverageCode exercised by automated testsMonitor test types separatelyDon’t set a coverage target100% coverage is a smellMonitor trends, not points
    56. 56. Monitoring Cruft/DebtCyclomatic ComplexityNumber of logical branches in codeDirect relationship with bugsTypically high in concentrated areasReduce conditionalsMonitor trends, not points
    57. 57. Monitoring Cruft/DebtCouplingInterconnectedness of systemsHighly coupled is difficult to changeAfferent (Toward) and Efferent (Away)Dependency InjectionMonitor trends, not points
    58. 58. Cruft/Debt CardsTie it to Business ValueVelocityScalabilityAvailabilityMaintainability
    59. 59. ReviewTechnical DebtIs a strategic design decisionRequires the business to be informedIncludes a pay-back planCruftHappensNeeds to be monitored andcleanedIs NOT Technical Debt
    60. 60. ReviewTechnical DebtIs a strategic design decisionRequires the business to be informedIncludes a pay-back planCruftHappensNeeds to be monitored andcleanedIs (probably) NOT Technical Debt
    61. 61. Thank You! The Technical Debt Trap Michael “Doc” Norton @DocOnDev

    ×