Death By a Thousand Commits

Death By a Thousand Commits

On the 1st commit, things are getting started. On the 10th commit, the feature is live and users are giving feedback. On the 100th commit, users are delighted to be using the application. But on the 1000th commit, users are unhappy with the responsiveness of the application and the developers are struggling to move at the velocity they once were. Does this sound familiar?

We will go over some of the pieces of technical debt that can accumulate and can cause application performance and development velocity issues, and the strategies Clio uses to keep these kinds of technical debt under control.

06841bbcde7df91dc306d22ea0293bf9?s=128

Kyle d'Oliveira

May 02, 2019
Tweet

Transcript

  1. 3.
  2. 9.

    Technical Debt Quadrants – Martin Fowler Reckless Prudent Deliberate “We

    don’t have time for design” Inadvertent “What’s layering”
  3. 10.

    Technical Debt Quadrants – Martin Fowler Reckless Prudent Deliberate “We

    don’t have time for design” “We must ship now and deal with consequences” Inadvertent “What’s layering”
  4. 11.

    Technical Debt Quadrants – Martin Fowler Reckless Prudent Deliberate “We

    don’t have time for design” “We must ship now and deal with consequences” Inadvertent “What’s layering” “Now we know how we should have done it”
  5. 15.

    Lessons ▪ Lesson 1 – Automate the debt away ▪

    Lesson 2 – Clean up code you don't use ▪ Lesson 3 – Make keeping the lights on easier ▪ Lesson 4 – Keep the bad patterns out
  6. 18.

    SELECT * FROM contacts SELECT * FROM emails WHERE contact_id

    = 1 SELECT * FROM emails WHERE contact_id = 2 SELECT * FROM emails WHERE contact_id = 3 SELECT * FROM emails WHERE contact_id = 4 SELECT * FROM emails WHERE contact_id = 5 SELECT * FROM emails WHERE contact_id = 6
  7. 25.

    class ContactSerializer < ActiveModel::Serializer attribute :id, :name has_one :email has_one

    :phone_number has_one :address has_one :emergency_contact end
  8. 29.

    Limitations ▪ Manual human effort ▪ Only fixes one instance

    at a time ▪ Doesn’t handle associations no longer being needed
  9. 33.
  10. 34.
  11. 39.
  12. 40.
  13. 44.
  14. 51.
  15. 66.

    ActiveSupport::Notifications .subscribe(‘active_record.sql’) do |*args| event = ActiveSupport::Notifications::Event.new(*args) if event.duration >

    THRESHOLD exception = LongQueryException.new(“a clear message”) Bugsnag.notify(exception) end end
  16. 67.
  17. 71.

    begin file = Tempfile.new(“contact.csv”) csv = CSV.new(file) csv << [“id”,

    “name”] Contact.all.each do |contact| csv << [contact.id, contact.name] end ensure file.close file.unlink end
  18. 72.

    begin file = Tempfile.new(“contact.csv”) csv = CSV.new(file) csv << [“id”,

    “name”] Contact.all.each do |contact| csv << [contact.id, contact.name] end ensure file.close file.unlink end
  19. 73.
  20. 74.
  21. 77.
  22. 78.
  23. 80.

    Shitlist = [ClassA, ClassB, ClassC] def push_job_that_does_crazy_things(klass) if Shitlist.include?(klass) #

    existing deprecated behavior else raise Shitlist::Error.new(“a clear message”) end end
  24. 81.

    RedisShitlist = [ Session, FragmentCache, AuthenticationTokens ] test “no new

    redis models introduced” do assert_equal RedisShitList, RedisModel.descendants end
  25. 82.
  26. 84.

    Lessons ▪ Lesson 1 – Automate the debt away –

    jit_preloader ▪ Lesson 2 – Clean up code you don't use – Tombstone – dead_code_detector – coverband ▪ Lesson 3 – Make keeping the lights on easier – marginalia/ActiveSupport::CurrentAttributes – ActiveSupport::Notifications ▪ Lesson 4 – Keep the bad patterns out – rubocop – Shitlist-driven development