SlideShare a Scribd company logo
The Technical Debt Trap
Michael “Doc” Norton
@DocOnDev
Michael “Doc” Norton
Groupon
Director of
Technology
doc@groupon.com
@DocOnDev




Husband           Father
GrandFather
He’s SOOO Cute!!
What is Technical Debt?
Ward
Cunningham
Shipping first time code is
like going into debt.




                OOPSLA ’92
Ward
Cunningham
Shipping first time code is
like going into debt.
 A little debt speeds
development so long as it is
paid back promptly with a
rewrite.
                OOPSLA ’92
Ward
Cunningham
The danger occurs when
the debt is not repaid.
Every minute spent on not-
quite-right code counts as
interest on that debt.


               OOPSLA ’92
Ward
Cunningham
The danger occurs when
the debt is not repaid.
Every minute spent on not-
quite-right code counts as
interest on that debt.


               OOPSLA ’92
Technical Debt is a Good Idea
Technical Debt is a Good Idea




WTF?
Technical Debt is a Good Idea
Strategic Design Decision
  Allow for Rapid Delivery
  To Elicit Quick Feedback
  And Correct Design
Technical Debt is a Metaphor
Here Be Danger
Metaphors Rock
 We Reason By Analogy
       Can’t keep
    running at thisBUILDING ON
          pace       A WEAK
                   FOUNDATION
IT’S RAINING MEN
    (HALLELUJAH)
                      Puts pressure
                      on our design
Metaphorphosis
  When Metaphors Go Wrong
      Return on Investment
SHORT-TERM Fraudulent         Credit Card
              Inadvertent Reckless
        AUTO LOAN
                  Debt in the Third       PYRAMID SCHEME


HIGH INTEREST
   Intentional       Quadrant Long-Term
                        HOME LOAN
                     Loan Shark
                                     VOLUNTARY

          Prudent
Pragmatic Leverage       Student Loan   OPERATIONAL
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
The Technical Debt Quadrant




        http://martinfowler.com/bliki/TechnicalDebtQuadrant.html
Ummm... no.
[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.
 http://www.youtube.com/watch?v=pqeJFYwnkjE&feature=player_embedded


                          YouTube.com ’09
Ummm... no.
[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.
 http://www.youtube.com/watch?v=pqeJFYwnkjE&feature=player_embedded


                          YouTube.com ’09
[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.
... confused it
with the idea that
you could write
code poorly ....
Clean Code
is Required
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.
 http://www.youtube.com/watch?v=pqeJFYwnkjE&feature=player_embedded


                          YouTube.com ’09
Clean Code
is Required
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.
 http://www.youtube.com/watch?v=pqeJFYwnkjE&feature=player_embedded


                          YouTube.com ’09
Dirty Code is
a BAD Idea
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.
    http://twitter.com/WardCunningham/status/3742903303


                                       twitter ’09
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?
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
You have a mess
Mess (noun)
Disorderly accumulation, heap, or jumble
A state of embarrassing confusion
An unpleasant or difficult situation
You have cruft
Cruft (noun)
An unpleasant substance
The result of shoddy construction
Redundant, old or improperly written code
Chill. It’s just semantics,
man.
Just Semantics?
Technical Debt is Good
Chill. It’s just semantics,
man.
Just Semantics?
Quick and Dirty is Technical Debtis Good
                   Technical Debt is Good
Chill. It’s just semantics,
man.
Just Semantics?
Quick and Dirtyis Good
          Dirty is Good
The Technical Debt Quadrant




        http://martinfowler.com/bliki/TechnicalDebtQuadrant.html
The Technical Debt Quadrant
                                                “Let’s deploy and
                                                   gather more
                                                  information.”




        http://martinfowler.com/bliki/TechnicalDebtQuadrant.html
Technical Debt in other
fields
Construction
                                      TE
                                   R A
                               IB E
                            E L
                      D D
                 AN
           S   S
         LE
      CK
    RE
Technical Debt in other
fields
Automotive
                                      TE
                                   R A
                               IB E
                            E L
                      D D
                 AN
           S   S
         LE
      CK
    RE
Technical Debt in other
fields
Medical
                                     NT
                                  TE
                               E R
                            D V
                        IN A
                  N D
               S A
          ES
       K L
     EC
    R
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
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
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
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
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
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)) { ... }
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)) { ... }
Cruft or Debt?


if (customer.isFederallyRegulated()) { ... }
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");}




                   http://www.refactoring.com/catalog/replaceConditionalWithPolymorphism.html
Cruft or Debt?

class Swallow ... double getSpeed() { return getBaseSpeed(); }end
classclass EuropeanSwallow ...end classclass AfricanSwallow ... double
getSpeed() { return super.getSpeed - coconutLoad(); } double
coconutLoad() { return getLoadFactor() * _numberOfCoconuts; }end
classclass NorwegianSwallow ... double getSpeed() { return
(_isNailed) ? 0 : getBaseSpeed(_voltage); }end class
Cruft is a bad decision
Every Time
YOU 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)
The Cost of “Technical Debt”
We did this
WORLD-WIDE TECHNICAL DEBT (INCLUDES HARDWARE)
              $1 Trillion (by 2015)
PER BUSINESS APPLICATION
              $1 Million
Grammy
Doesn’t
Love
Me!
Me!
Me!

It’s all about expectations
The Trap
Cruft begets cruft
Precedent for speed over quality
Expectation of increased velocity
Cruft slows you down
Must write more cruft to keep up
 Ask Permission to do your job
 correctly
Avoid The Trap
Thresholds set false
expectations




       http://blog.castsoftware.com/wp-content/uploads/2011/04/Technical-Debt-Software-Quality1.jpg
Avoid The Trap
Incremental fixes fail




        http://www.jacoozi.com/blog/wp-content/uploads/2007/01/refactoring_coc_big.jpg
Avoid The Trap
Incremental fixes fail




        http://www.jacoozi.com/blog/wp-content/uploads/2007/01/refactoring_coc_big.jpg
Avoid The Trap
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
Monitoring Cruft/Debt
A few key metrics
Code Coverage
Cyclomatic Complexity
Coupling
Maintainability
Monitoring Cruft/Debt
Code Coverage
Code exercised by automated tests
Monitor test types separately
Don’t set a coverage target
100% coverage is a smell
Monitor trends, not points
Monitoring Cruft/Debt
Cyclomatic Complexity
Number of logical branches in code
Direct relationship with bugs
Typically high in concentrated areas
Reduce conditionals
Monitor trends, not points
Monitoring Cruft/Debt
Coupling
Interconnectedness of systems
Highly coupled is difficult to change
Afferent (Toward) and Efferent (Away)
Dependency Injection
Monitor trends, not points
Cruft/Debt Cards
Tie it to Business Value
Velocity
Scalability
Availability
Maintainability
Review
Technical Debt
Is a strategic design decision
Requires the business to be informed
Includes a pay-back plan
Cruft
HappensNeeds to be monitored and
cleanedIs NOT Technical Debt
Review
Technical Debt
Is a strategic design decision
Requires the business to be informed
Includes a pay-back plan
Cruft
HappensNeeds to be monitored and
cleanedIs (probably) NOT Technical Debt
Thank You!



 The Technical Debt Trap
 Michael “Doc” Norton
 @DocOnDev

More Related Content

PDF
The Technical Debt Trap - NDC Oslo 2014
PDF
Technical Debt - osbridge
PDF
Agile Metrics: Velocity is NOT the Goal - Agile 2013 version
PDF
Let's Start An Epidemic
PDF
Building a JavaScript Library
PPTX
Beyond technical debt
PDF
Improving Agility (Learning from Maersk Line's Journey) | Özlem Yüce | Agile ...
PPS
The Agile Facilitator
The Technical Debt Trap - NDC Oslo 2014
Technical Debt - osbridge
Agile Metrics: Velocity is NOT the Goal - Agile 2013 version
Let's Start An Epidemic
Building a JavaScript Library
Beyond technical debt
Improving Agility (Learning from Maersk Line's Journey) | Özlem Yüce | Agile ...
The Agile Facilitator

Similar to The Technical Debt Trap - AgileIndy 2013 (20)

KEY
Agile and Beyond :: The Technical Debt Trap
PPTX
Virtual enterprise synthesys
PPTX
Ports and Adapters Architecture
PPTX
Jarrar.lecture notes.aai.2011s.ontology part5_egovernmentcasestudy
PDF
Tackling Tech Debt with Rector
PPTX
Government Interoperability Framework (Palestine Case) v5
PDF
Creating Responsive Experiences
PDF
Ports and Adapters Architecture - solid
KEY
Deploying Next Gen Systems with Zero Downtime
KEY
The Technical Debt Trap
PPT
PHPUG Presentation
PDF
Stories Every Developer Should Know
KEY
The Technical Debt Trap - Michael "Doc" Norton
PDF
PHPUnit Episode iv.iii: Return of the tests
PPTX
HTML5 Introduction
PPTX
A Lay of the Meta-land: A Systematic Approach to Dissect the Metaverse
PDF
KSQL: The Streaming SQL Engine for Apache Kafka
PDF
Architecture in an Agile World
PPTX
From CRUD to messages: a true story
PPT
Databaseconcepts
Agile and Beyond :: The Technical Debt Trap
Virtual enterprise synthesys
Ports and Adapters Architecture
Jarrar.lecture notes.aai.2011s.ontology part5_egovernmentcasestudy
Tackling Tech Debt with Rector
Government Interoperability Framework (Palestine Case) v5
Creating Responsive Experiences
Ports and Adapters Architecture - solid
Deploying Next Gen Systems with Zero Downtime
The Technical Debt Trap
PHPUG Presentation
Stories Every Developer Should Know
The Technical Debt Trap - Michael "Doc" Norton
PHPUnit Episode iv.iii: Return of the tests
HTML5 Introduction
A Lay of the Meta-land: A Systematic Approach to Dissect the Metaverse
KSQL: The Streaming SQL Engine for Apache Kafka
Architecture in an Agile World
From CRUD to messages: a true story
Databaseconcepts
Ad

More from Doc Norton (20)

PDF
Tuckman Was Wrong
PDF
A Practical Guide to Cynefin
PDF
Building Blocks of a Knowledge Work Culture - NDC London 2016
PDF
Codemash pre-compiler - Collaborative Decision Making
PDF
Experimentation Mindset
PDF
The Technical Debt Trap
PDF
Switching horses midstream - From Waterfall to Agile
PDF
Autonomy, Connection, and Excellence; The Building Blocks of a DevOps Culture
PPTX
Creative Collaboration: Tools for Teams
PDF
Experimentation mindset
PDF
Agile Metrics : Velocity is NOT the Goal - NDC Oslo 2014
PDF
Teamwork Ain't Easy - RailsConf 2014
PPT
Creating a Global Engineering Culture - Agile india 2014
PDF
Doc That Conference Keynote
PDF
Agile Metrics; Velocity is NOT the Goal - ScrumGathering 2013
PDF
Velocity is not the goal code palo-usa
PDF
Teamwork Ain't Easy
PDF
Velocity is NOT the Goal - PNSQC
PDF
Growing into Excellence - PNSQC
KEY
Velocity is not the Goal
Tuckman Was Wrong
A Practical Guide to Cynefin
Building Blocks of a Knowledge Work Culture - NDC London 2016
Codemash pre-compiler - Collaborative Decision Making
Experimentation Mindset
The Technical Debt Trap
Switching horses midstream - From Waterfall to Agile
Autonomy, Connection, and Excellence; The Building Blocks of a DevOps Culture
Creative Collaboration: Tools for Teams
Experimentation mindset
Agile Metrics : Velocity is NOT the Goal - NDC Oslo 2014
Teamwork Ain't Easy - RailsConf 2014
Creating a Global Engineering Culture - Agile india 2014
Doc That Conference Keynote
Agile Metrics; Velocity is NOT the Goal - ScrumGathering 2013
Velocity is not the goal code palo-usa
Teamwork Ain't Easy
Velocity is NOT the Goal - PNSQC
Growing into Excellence - PNSQC
Velocity is not the Goal
Ad

The Technical Debt Trap - AgileIndy 2013

  • 1. The Technical Debt Trap Michael “Doc” Norton @DocOnDev
  • 2. Michael “Doc” Norton Groupon Director of Technology doc@groupon.com @DocOnDev Husband Father
  • 5. Ward Cunningham Shipping first time code is like going into debt. OOPSLA ’92
  • 6. Ward Cunningham Shipping first time code is like going into debt. A little debt speeds development so long as it is paid back promptly with a rewrite. OOPSLA ’92
  • 7. Ward Cunningham The danger occurs when the debt is not repaid. Every minute spent on not- quite-right code counts as interest on that debt. OOPSLA ’92
  • 8. Ward Cunningham The danger occurs when the debt is not repaid. Every minute spent on not- quite-right code counts as interest on that debt. OOPSLA ’92
  • 9. Technical Debt is a Good Idea
  • 10. Technical Debt is a Good Idea WTF?
  • 11. Technical Debt is a Good Idea Strategic Design Decision Allow for Rapid Delivery To Elicit Quick Feedback And Correct Design
  • 12. Technical Debt is a Metaphor Here Be Danger
  • 13. Metaphors Rock We Reason By Analogy Can’t keep running at thisBUILDING ON pace A WEAK FOUNDATION IT’S RAINING MEN (HALLELUJAH) Puts pressure on our design
  • 14. Metaphorphosis When Metaphors Go Wrong Return on Investment SHORT-TERM Fraudulent Credit Card Inadvertent Reckless AUTO LOAN Debt in the Third PYRAMID SCHEME HIGH INTEREST Intentional Quadrant Long-Term HOME LOAN Loan Shark VOLUNTARY Prudent Pragmatic Leverage Student Loan OPERATIONAL
  • 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. The Technical Debt Quadrant http://martinfowler.com/bliki/TechnicalDebtQuadrant.html
  • 17. Ummm... no. [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. http://www.youtube.com/watch?v=pqeJFYwnkjE&feature=player_embedded YouTube.com ’09
  • 18. Ummm... no. [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. http://www.youtube.com/watch?v=pqeJFYwnkjE&feature=player_embedded YouTube.com ’09
  • 19. [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.
  • 20. ... confused it with the idea that you could write code poorly ....
  • 21. Clean Code is Required 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. http://www.youtube.com/watch?v=pqeJFYwnkjE&feature=player_embedded YouTube.com ’09
  • 22. Clean Code is Required 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. http://www.youtube.com/watch?v=pqeJFYwnkjE&feature=player_embedded YouTube.com ’09
  • 23. Dirty Code is a BAD Idea 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. http://twitter.com/WardCunningham/status/3742903303 twitter ’09
  • 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. 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. You have a mess Mess (noun) Disorderly accumulation, heap, or jumble A state of embarrassing confusion An unpleasant or difficult situation
  • 27. You have cruft Cruft (noun) An unpleasant substance The result of shoddy construction Redundant, old or improperly written code
  • 28. Chill. It’s just semantics, man. Just Semantics? Technical Debt is Good
  • 29. Chill. It’s just semantics, man. Just Semantics? Quick and Dirty is Technical Debtis Good Technical Debt is Good
  • 30. Chill. It’s just semantics, man. Just Semantics? Quick and Dirtyis Good Dirty is Good
  • 31. The Technical Debt Quadrant http://martinfowler.com/bliki/TechnicalDebtQuadrant.html
  • 32. The Technical Debt Quadrant “Let’s deploy and gather more information.” http://martinfowler.com/bliki/TechnicalDebtQuadrant.html
  • 33. Technical Debt in other fields Construction TE R A IB E E L D D AN S S LE CK RE
  • 34. Technical Debt in other fields Automotive TE R A IB E E L D D AN S S LE CK RE
  • 35. Technical Debt in other fields Medical NT TE E R D V IN A N D S A ES K L EC R
  • 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. 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. 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. 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. 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. 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. 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)) { ... }
  • 43. Cruft or Debt? if (customer.isFederallyRegulated()) { ... }
  • 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 new RuntimeException ("Should be unreachable");} http://www.refactoring.com/catalog/replaceConditionalWithPolymorphism.html
  • 45. Cruft or Debt? class Swallow ... double getSpeed() { return getBaseSpeed(); }end classclass EuropeanSwallow ...end classclass AfricanSwallow ... double getSpeed() { return super.getSpeed - coconutLoad(); } double coconutLoad() { return getLoadFactor() * _numberOfCoconuts; }end classclass NorwegianSwallow ... double getSpeed() { return (_isNailed) ? 0 : getBaseSpeed(_voltage); }end class
  • 46. Cruft is a bad decision Every Time YOU 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. The Cost of “Technical Debt” We did this WORLD-WIDE TECHNICAL DEBT (INCLUDES HARDWARE) $1 Trillion (by 2015) PER BUSINESS APPLICATION $1 Million
  • 49. The Trap Cruft begets cruft Precedent for speed over quality Expectation of increased velocity Cruft slows you down Must write more cruft to keep up Ask Permission to do your job correctly
  • 50. Avoid The Trap Thresholds set false expectations http://blog.castsoftware.com/wp-content/uploads/2011/04/Technical-Debt-Software-Quality1.jpg
  • 51. Avoid The Trap Incremental fixes fail http://www.jacoozi.com/blog/wp-content/uploads/2007/01/refactoring_coc_big.jpg
  • 52. Avoid The Trap Incremental fixes fail http://www.jacoozi.com/blog/wp-content/uploads/2007/01/refactoring_coc_big.jpg
  • 53. Avoid The Trap 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
  • 54. Monitoring Cruft/Debt A few key metrics Code Coverage Cyclomatic Complexity Coupling Maintainability
  • 55. Monitoring Cruft/Debt Code Coverage Code exercised by automated tests Monitor test types separately Don’t set a coverage target 100% coverage is a smell Monitor trends, not points
  • 56. Monitoring Cruft/Debt Cyclomatic Complexity Number of logical branches in code Direct relationship with bugs Typically high in concentrated areas Reduce conditionals Monitor trends, not points
  • 57. Monitoring Cruft/Debt Coupling Interconnectedness of systems Highly coupled is difficult to change Afferent (Toward) and Efferent (Away) Dependency Injection Monitor trends, not points
  • 58. Cruft/Debt Cards Tie it to Business Value Velocity Scalability Availability Maintainability
  • 59. Review Technical Debt Is a strategic design decision Requires the business to be informed Includes a pay-back plan Cruft HappensNeeds to be monitored and cleanedIs NOT Technical Debt
  • 60. Review Technical Debt Is a strategic design decision Requires the business to be informed Includes a pay-back plan Cruft HappensNeeds to be monitored and cleanedIs (probably) NOT Technical Debt
  • 61. Thank You! The Technical Debt Trap Michael “Doc” Norton @DocOnDev

Editor's Notes

  • #11: Design decisions that allow for more rapid delivery / illicit quick feedback / gather data necessary to correct design
  • #12: - Plan to sell several different products. Starting with just one. Don’t extract interface yet.
  • #14: Allows us to establish a simple understanding of a complex problem in common representational terms
  • #34: "We incurred structural debt in order to meet your deadline. We should discuss that debt and set a plan for paying it back later."
  • #35: "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."
  • #36: "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..."
  • #38: First, let’s get rid of the curlies for readability
  • #39: 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
  • #40: 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.
  • #41: 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.
  • #42: Is this where this evaluation belongs? Probably not. What are we checking for here? Perhaps a comment will help us.
  • #43: Better? Not really. But what if that comment was code?
  • #45: What about this one?
  • #46: Why is this NOT cruft? We clearly have more code.
  • #48: 1. Gartner Report in 2009 :: Includes Hardware 2. CAST Study in 2009 :: No correlation between size and debt; more modular == less debt
  • #54: Ruby - churn => volume of changes git - gitswarm => visual history of changes Java - Cobertura or Sonar
  • #55: Ruby - churn => volume of changes git - gitswarm => visual history of changes Java - Cobertura or Sonar
  • #56: Java - Cobertura, Clover, EMMA .NET - NCover Ruby - rcov / metric_fu
  • #57: Java - Cobertura, Clover, EMMA .NET - NCover Ruby - rcov / metric_fu
  • #58: NDepend JDepend