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

David Chelimsky, RubyConf 2010: Maintaining balance while reducing duplication

David Chelimsky, RubyConf 2010: Maintaining balance while reducing duplication

Benjamin Fleischer

November 13, 2010
Tweet

More Decks by Benjamin Fleischer

Other Decks in Programming

Transcript

  1. Maintaining balance while reducing duplication David Chelimsky Saturday, November 13,

    2010
  2. This talk is about duplication About duplication, is this talk

    Saturday, November 13, 2010
  3. Duplication is bad Saturday, November 13, 2010

  4. Code smell: Duplicated Code Saturday, November 13, 2010

  5. “Duplicate code is the root of all evil in software

    design.” Saturday, November 13, 2010
  6. Don’t Repeat Yourself The DRY principle Saturday, November 13, 2010

  7. You keep using that word ... Saturday, November 13, 2010

  8. Every piece of knowledge must have a single, unambiguous, authoritative

    representation within a system Saturday, November 13, 2010
  9. Every piece of knowledge must have a single, unambiguous, authoritative

    representation within a system Saturday, November 13, 2010
  10. Every piece of knowledge must have a single, unambiguous, authoritative

    representation within a system Saturday, November 13, 2010
  11. ALLOCATIONS_ON_DATE = "allocations/{sourceType}/on/{year}/{month}" CREATE_ALLOCATION = "allocations/{sourceType}/{sourceId}/{targetType}/{targetId}/{year}/{month}/{percentage}" DELETE_ALLOCATION = "allocations/{sourceType}/{sourceId}/{targetType}/{targetId}/{year}/{month}" Saturday,

    November 13, 2010
  12. "allocations/{sourceType}/on/{year}/{month}" "allocations/{sourceType}/{sourceId}/{targetType}/{targetId}/{year}/{month}/{percentage}" "allocations/{sourceType}/{sourceId}/{targetType}/{targetId}/{year}/{month}" Saturday, November 13, 2010

  13. ALLOCATIONS = "allocations" ALLOCATIONS_ON_DATE = ALLOCATIONS + "/{sourceType}/on/{year}/{month}" CREATE_ALLOCATION =

    ALLOCATIONS + "/{sourceType}/{sourceId}/{targetType}/{targetId}/{year}/{month}/{percentage}" DELETE_ALLOCATION = ALLOCATIONS + "/{sourceType}/{sourceId}/{targetType}/{targetId}/{year}/{month}" Saturday, November 13, 2010
  14. ALLOCATIONS = "allocations" SOURCE_TYPE = "/{sourceType}" ALLOCATIONS_ON_DATE = ALLOCATIONS +

    SOURCE_TYPE + "/on/{year}/{month}" CREATE_ALLOCATION = ALLOCATIONS + SOURCE_TYPE + "/{sourceId}/{targetType}/{targetId}/{year}/{month}/{percentage}" DELETE_ALLOCATION = ALLOCATIONS + SOURCE_TYPE + "/{sourceId}/{targetType}/{targetId}/{year}/{month}" Saturday, November 13, 2010
  15. ALLOCATIONS = "allocations" SOURCE_TYPE = "/{sourceType}" SOURCE_ID = "/{sourceId}" TARGET_TYPE

    = "/{targetType}" TARGET_ID = "/{targetId}" EFFECTIVE_DATE = "/{year}/{month}" ALLOCATIONS_ON_DATE = ALLOCATIONS + SOURCE_TYPE + "/on" + EFFECTIVE_DATE CREATE_ALLOCATION = ALLOCATIONS + SOURCE_TYPE + SOURCE_ID + TARGET_TYPE + TARGET_ID + EFFECTIVE_DATE + "/{percentage}" DELETE_ALLOCATION = ALLOCATIONS + SOURCE_TYPE + SOURCE_ID + TARGET_TYPE + TARGET_ID + EFFECTIVE_DATE Saturday, November 13, 2010
  16. DELIMITERS = %w[/ { }] ALLOCATIONS = "allocations" EFFECTIVE_DATE =

    DELIMITERS[0..1].join + "year" + DELIMITERS[2] + DELIMITERS[0..1].join + "month" + DELIMITERS[2] SOURCE = "source" TARGET = "target" ID = "Id" TYPE = "Type" SOURCE_TYPE = DELIMITERS[0..1].join + SOURCE + TYPE + DELIMITERS[2] SOURCE_ID = DELIMITERS[0..1].join + SOURCE + ID + DELIMITERS[2] TARGET_TYPE = DELIMITERS[0..1].join + TARGET + TYPE + DELIMITERS[2] TARGET_ID = DELIMITERS[0..1].join + TARGET + ID + DELIMITERS[2] ALLOCATION_SOURCE = SOURCE_TYPE + SOURCE_ID ALLOCATION_TARGET = TARGET_TYPE + TARGET_ID ALLOCATIONS_ON_DATE = ALLOCATIONS + SOURCE_TYPE + DELIMITERS[0] + "on" + EFFECTIVE_DATE CREATE_ALLOCATION = ALLOCATIONS + ALLOCATION_SOURCE + ALLOCATION_TARGET + EFFECTIVE_DATE + DELIMITERS[0..1].join + "percentage" + DELIMITERS[2] DELETE_ALLOCATION = ALLOCATIONS + ALLOCATION_SOURCE + ALLOCATION_TARGET + EFFECTIVE_DATE Saturday, November 13, 2010
  17. def token(string) "/{#{string}}" end def allocation_path(path) "allocations#{path}" end EFFECTIVE_DATE =

    token("year") + token("month") SOURCE = "source" TARGET = "target" ID = "Id" TYPE = "Type" SOURCE_TYPE = token(SOURCE + TYPE) SOURCE_ID = token(SOURCE + ID) TARGET_TYPE = token(TARGET + TYPE) TARGET_ID = token(TARGET + ID) ALLOCATION_SOURCE = SOURCE_TYPE + SOURCE_ID ALLOCATION_TARGET = TARGET_TYPE + TARGET_ID ALLOCATIONS_ON_DATE = allocation_path(SOURCE_TYPE + "/on" + EFFECTIVE_DATE) CREATE_ALLOCATION = allocation_path( ALLOCATION_SOURCE + ALLOCATION_TARGET + EFFECTIVE_DATE + token("percentage") ) DELETE_ALLOCATION = allocation_path( ALLOCATION_SOURCE + ALLOCATION_TARGET + EFFECTIVE_DATE ) Saturday, November 13, 2010
  18. "/allocations/developer/37/project/2010/10/42" def token(string) "/{#{string}}" end def allocation_path(path) "allocations#{path}" end EFFECTIVE_DATE

    = token("year") + token("month") SOURCE = "source" TARGET = "target" ID = "Id" TYPE = "Type" SOURCE_TYPE = token(SOURCE + TYPE) SOURCE_ID = token(SOURCE + ID) TARGET_TYPE = token(TARGET + TYPE) TARGET_ID = token(TARGET + ID) ALLOCATION_SOURCE = SOURCE_TYPE + SOURCE_ID ALLOCATION_TARGET = TARGET_TYPE + TARGET_ID ALLOCATIONS_ON_DATE = allocation_path(SOURCE_TYPE + "/on" + EFFECTIVE_DATE) CREATE_ALLOCATION = allocation_path( ALLOCATION_SOURCE + ALLOCATION_TARGET + EFFECTIVE_DATE + token("percentage") ) DELETE_ALLOCATION = allocation_path( ALLOCATION_SOURCE + ALLOCATION_TARGET + EFFECTIVE_DATE ) Saturday, November 13, 2010
  19. "/allocations/developer/37/project/2010/10/42" ALLOCATIONS_ON_DATE = "allocations/{sourceType}/on/{year}/{month}" CREATE_ALLOCATION = "allocations/{sourceType}/{sourceId}/{targetType}/{targetId}/{year}/{month}/{percentage}" DELETE_ALLOCATION = "allocations/{sourceType}/{sourceId}/{targetType}/{targetId}/{year}/{month}"

    "allocations/{sourceType}/on/{year}/{month}" "allocations/{sourceType}/{sourceId}/{targetType}/{targetId}/{year}/{month}/{percentage}" "allocations/{sourceType}/{sourceId}/{targetType}/{targetId}/{year}/{month}" Saturday, November 13, 2010
  20. def token(string) "/{#{string}}" end def allocation_path(path) "allocations#{path}" end EFFECTIVE_DATE =

    token("year") + token("month") SOURCE = "source" TARGET = "target" ID = "Id" TYPE = "Type" SOURCE_TYPE = token(SOURCE + TYPE) SOURCE_ID = token(SOURCE + ID) TARGET_TYPE = token(TARGET + TYPE) TARGET_ID = token(TARGET + ID) ALLOCATION_SOURCE = SOURCE_TYPE + SOURCE_ID ALLOCATION_TARGET = TARGET_TYPE + TARGET_ID ALLOCATIONS_ON_DATE = allocation_path( SOURCE_TYPE + "/on" + EFFECTIVE_DATE ) CREATE_ALLOCATION = allocation_path( ALLOCATION_SOURCE + ALLOCATION_TARGET + EFFECTIVE_DATE + token("percentage") ) DELETE_ALLOCATION = allocation_path( ALLOCATION_SOURCE + ALLOCATION_TARGET + EFFECTIVE_DATE ) ALLOCATIONS_ON_DATE = "allocations/{sourceType}/on/{year}/{month}" CREATE_ALLOCATION = "allocations/{sourceType}/{sourceId}/{targetType}/{targetId}/{year}/{month}/{percentage}" DELETE_ALLOCATION = "allocations/{sourceType}/{sourceId}/{targetType}/{targetId}/{year}/{month}" Saturday, November 13, 2010
  21. def token(string) "/{#{string}}" end def allocation_path(path) "allocations#{path}" end EFFECTIVE_DATE =

    token("year") + token("month") SOURCE = "source" TARGET = "target" ID = "Id" TYPE = "Type" SOURCE_TYPE = token(SOURCE + TYPE) SOURCE_ID = token(SOURCE + ID) TARGET_TYPE = token(TARGET + TYPE) TARGET_ID = token(TARGET + ID) ALLOCATION_SOURCE = SOURCE_TYPE + SOURCE_ID ALLOCATION_TARGET = TARGET_TYPE + TARGET_ID ALLOCATIONS_ON_DATE = allocation_path( SOURCE_TYPE + "/on" + EFFECTIVE_DATE ) CREATE_ALLOCATION = allocation_path( ALLOCATION_SOURCE + ALLOCATION_TARGET + EFFECTIVE_DATE + token("percentage") ) DELETE_ALLOCATION = allocation_path( ALLOCATION_SOURCE + ALLOCATION_TARGET + EFFECTIVE_DATE ) ALLOCATIONS_ON_DATE = "allocations/{sourceType}/on/{year}/{month}" CREATE_ALLOCATION = "allocations/{sourceType}/{sourceId}/{targetType}/{targetId}/{year}/{month}/{percentage}" DELETE_ALLOCATION = "allocations/{sourceType}/{sourceId}/{targetType}/{targetId}/{year}/{month}" some duplicate letters/words Saturday, November 13, 2010
  22. def token(string) "/{#{string}}" end def allocation_path(path) "allocations#{path}" end EFFECTIVE_DATE =

    token("year") + token("month") SOURCE = "source" TARGET = "target" ID = "Id" TYPE = "Type" SOURCE_TYPE = token(SOURCE + TYPE) SOURCE_ID = token(SOURCE + ID) TARGET_TYPE = token(TARGET + TYPE) TARGET_ID = token(TARGET + ID) ALLOCATION_SOURCE = SOURCE_TYPE + SOURCE_ID ALLOCATION_TARGET = TARGET_TYPE + TARGET_ID ALLOCATIONS_ON_DATE = allocation_path( SOURCE_TYPE + "/on" + EFFECTIVE_DATE ) CREATE_ALLOCATION = allocation_path( ALLOCATION_SOURCE + ALLOCATION_TARGET + EFFECTIVE_DATE + token("percentage") ) DELETE_ALLOCATION = allocation_path( ALLOCATION_SOURCE + ALLOCATION_TARGET + EFFECTIVE_DATE ) ALLOCATIONS_ON_DATE = "allocations/{sourceType}/on/{year}/{month}" CREATE_ALLOCATION = "allocations/{sourceType}/{sourceId}/{targetType}/{targetId}/{year}/{month}/{percentage}" DELETE_ALLOCATION = "allocations/{sourceType}/{sourceId}/{targetType}/{targetId}/{year}/{month}" some duplicate letters/words #!(*&?$@ Saturday, November 13, 2010
  23. DRY does not mean “don’t type the same characters twice”

    Saturday, November 13, 2010
  24. DRY is about duplicated concepts Saturday, November 13, 2010

  25. every time you reduce duplication you increase coupling by introducing

    new dependencies Saturday, November 13, 2010
  26. it is important to depend on the right things Saturday,

    November 13, 2010
  27. DRY is about isolation of change Saturday, November 13, 2010

  28. depend on things which are less likely to change Saturday,

    November 13, 2010
  29. The Dependency Inversion Principle depend on abstractions Saturday, November 13,

    2010
  30. in Ruby, almost everything is an abstraction Saturday, November 13,

    2010
  31. How do I know if this particular duplication is problematic?

    Saturday, November 13, 2010
  32. Purpose/Intention Saturday, November 13, 2010

  33. Risk Saturday, November 13, 2010

  34. Have to change in more than one place? Saturday, November

    13, 2010
  35. Might forget to change in other place! Saturday, November 13,

    2010
  36. Duplication is not always obvious Saturday, November 13, 2010

  37. Same name, different meaning Saturday, November 13, 2010

  38. Less duplication can mean more indirection Saturday, November 13, 2010

  39. this can make it harder to reason about code Saturday,

    November 13, 2010
  40. Metaprogramming is helpful when you can’t know the runtime conditions

    Saturday, November 13, 2010
  41. Metaprogramming can also make it harder to reason about code

    Saturday, November 13, 2010
  42. Many problems in software can be traced back to duplication,

    but ... Saturday, November 13, 2010
  43. ... not all duplication is inherently evil Saturday, November 13,

    2010
  44. Duplication comes in many forms Saturday, November 13, 2010

  45. Duplication appears for several reasons Saturday, November 13, 2010

  46. Every piece of knowledge must have a single, unambiguous, authoritative

    representation within a system Saturday, November 13, 2010
  47. This is not really about duplicated strings of characters Saturday,

    November 13, 2010
  48. DRY is about duplicated concepts Saturday, November 13, 2010

  49. every time you reduce duplication you increase coupling by introducing

    new dependencies Saturday, November 13, 2010
  50. depend on things which are less likely to change Saturday,

    November 13, 2010
  51. learn about code smells Saturday, November 13, 2010

  52. Duplicated Code Feature Envy Inappropriate Intimacy Large Class Long Method

    Saturday, November 13, 2010
  53. learn more principles! Saturday, November 13, 2010

  54. •Single Responsibility •Open/Closed •Liskov Substitution •Interface Segregation •Dependency Inversion SOLID

    Principles Saturday, November 13, 2010
  55. OO in one sentence keep it shy, DRY, and tell

    the other guy! Saturday, November 13, 2010