← Extractions

Toolbelt of a Seasoned Bug Hunter — debugging a Rails reload slowdown

Damir Zekić's wroclove.rb 2018 talk recounts an 'Odyssey' of diagnosing a progressively slowing Rails test-reload loop. He walks through reproducing the bug, using git bisect and a custom linear-search script, investigating memory leaks with heap dumps (heapy), rendering flame graphs, digging into Rails' routing code and Ruby's undef_method in C, and ultimately tracing the root cause to the monolith/granite framework mounting controllers at Spring boot time.

Model
claude-opus-4-7
Ingestion
97bc0a8f
Input tokens
100,629
fresh
47,005
cached
44,307
cache write
9,317
Output tokens
8,847
Duration
128.4s
Roundtrips
4
Tool calls
13
Cost
$0.00
Nodes/edges extracted
21 / 38
Read set (nodes/edges)
72 / 2

Nodes (21)

update Damir Zekić person
attrs (empty) → {"role" => "Ruby programmer"}
description Conference speaker. → Conference speaker at wroclove.rb 2018. Remote Ruby programmer who enjoys pair programming with his cat Midori. Worke...
short_description Conference speaker. → Ruby developer and conference speaker; worked on the monolith/granite business-actions framework.
update Toolbelt of a Seasoned Bug Hunter talk
description Talk at wroclove.rb 2018. → Damir Zekić's wroclove.rb 2018 talk subtitled '2016: A Debugging Odyssey in Six Chapters with an Epilogue'. Recounts ...
short_description Talk at wroclove.rb 2018. → Story-driven talk on advanced debugging tools applied to a Rails reload slowdown.
update wroclove.rb 2018 event
No changes recorded.
create Spring tool
kind (empty) → tool
name (empty) → Spring
slug (empty) → spring
attrs (empty) → {"category" => "library"}
description (empty) → Rails preloader gem that loads a Rails application into memory once and forks copies for each test run, console, or c...
short_description (empty) → Rails application preloader that keeps the app in memory between commands.
create Guard tool
kind (empty) → tool
name (empty) → Guard
slug (empty) → guard
attrs (empty) → {"category" => "library"}
description (empty) → Ruby tool that watches files and automatically re-runs tests (or other tasks) when files change. Used together with S...
short_description (empty) → Ruby file-watcher that reruns tests on file change.
create git bisect tool
kind (empty) → tool
name (empty) → git bisect
slug (empty) → git-bisect
attrs (empty) → {"category" => "tool"}
description (empty) → Git subcommand that binary-searches commit history between a known-good and a known-bad commit, running a user script...
short_description (empty) → Git command performing binary search through history to find a bad commit.
create find-slow script tool
kind (empty) → tool
name (empty) → find-slow script
slug (empty) → find-slow-script
attrs (empty) → {"category" => "tool"}
description (empty) → Ad-hoc Ruby script the speaker wrote to extend git bisect when no known-good commit existed: checks out each previous...
short_description (empty) → Custom Ruby script that linearly walks git history to find a non-slow commit.
create heapy tool
kind (empty) → tool
name (empty) → heapy
slug (empty) → heapy
attrs (empty) → {"category" => "library"}
description (empty) → Ruby gem installed as a command-line tool that parses ObjectSpace heap-dump JSON files and summarizes how many object...
short_description (empty) → Ruby gem for analyzing heap dumps produced by ObjectSpace.
create ObjectSpace heap dumps concept
kind (empty) → concept
name (empty) → ObjectSpace heap dumps
slug (empty) → objectspace-heap-dumps
attrs (empty) → {"category" => "practice"}
description (empty) → Ruby's built-in facility (ObjectSpace + allocation tracing) to dump every live object to a file where each line is a ...
short_description (empty) → Built-in Ruby mechanism to snapshot all live objects to a JSON-lines file.
create Flame Graphs concept
kind (empty) → concept
name (empty) → Flame Graphs
slug (empty) → flame-graphs
attrs (empty) → {"category" => "practice"}
description (empty) → Visualization technique where the x-axis represents aggregated stack frames and the y-axis represents call depth, let...
short_description (empty) → Stacked visualization of sampled call-stack frames showing where time is spent.
create monolith framework project
kind (empty) → project
name (empty) → monolith framework
slug (empty) → monolith-framework
attrs (empty) → {"status" => "superseded", "license" => "closed-source"}
description (empty) → Internal 'business-action framework' built on top of Rails that let developers encapsulate commands/operations with v...
short_description (empty) → Arkency's in-house business-actions framework for monolithic Rails apps (predecessor of granite).
create granite project
kind (empty) → project
name (empty) → granite
slug (empty) → granite
attrs (empty) → {"status" => "active", "license" => "open-source", "repository" => "github.com/.../granite"}
description (empty) → Open-source successor to the monolith framework, announced publicly for the first time during this talk. A business-a...
short_description (empty) → Open-source business-actions architecture gem for Rails apps.
create Rails RouteSet clear concept
kind (empty) → concept
name (empty) → Rails RouteSet clear
slug (empty) → rails-routeset-clear
attrs (empty) → {"category" => "architecture"}
description (empty) → ActionDispatch::Routing::RouteSet#clear iterates over the _path_helpers and _url_helpers modules and calls undef_meth...
short_description (empty) → Rails routing internal that undef_methods all URL/path helpers on every reload.
create Ruby undef_method performance concept
kind (empty) → concept
name (empty) → Ruby undef_method performance
slug (empty) → ruby-undef_method-performance
attrs (empty) → {"category" => "architecture"}
description (empty) → Benchmark shown in the talk: defining and undef-ing methods across N modules-included-in-objects is far from linear i...
short_description (empty) → undef_method scales super-linearly and spends half its time clearing the method cache.
create Ruby memory retention behavior concept
kind (empty) → concept
name (empty) → Ruby memory retention behavior
slug (empty) → ruby-memory-retention-behavior
attrs (empty) → {"category" => "architecture"}
description (empty) → Ruby's garbage collector reclaims objects inside the Ruby heap but does not shrink the process by returning memory to...
short_description (empty) → Ruby's GC frees objects but does not return freed heap pages to the OS.
create DescendantsTracker concept
kind (empty) → concept
name (empty) → DescendantsTracker
slug (empty) → descendantstracker
attrs (empty) → {"category" => "architecture"}
description (empty) → ActiveSupport::DescendantsTracker is mixed into every class in a Rails app and stores, in a class-level class-variabl...
short_description (empty) → ActiveSupport module that keeps a hash of every class and its subclasses.
create Know thy app takeaway
kind (empty) → takeaway
name (empty) → Know thy app
slug (empty) → know-thy-app
attrs (empty) → {"type" => "lesson-learned"}
description (empty) → The closing lesson of the talk: debugging subtle, long-running performance bugs requires knowing not just your own co...
short_description (empty) → Know your code, your framework, your libraries, and the language — otherwise bugs like this are inevitable.
create Don't create controllers at Rails boot time takeaway
kind (empty) → takeaway
name (empty) → Don't create controllers at Rails boot time
slug (empty) → don-t-create-controllers-at-rails-boot-time
attrs (empty) → {"type" => "lesson-learned"}
description (empty) → Concrete lesson from the monolith debugging: mounting business-action objects (which generated controllers) onto rout...
short_description (empty) → Creating controllers before Spring forks makes them immortal across reloads.
create Use Ruby to debug Ruby takeaway
kind (empty) → takeaway
name (empty) → Use Ruby to debug Ruby
slug (empty) → use-ruby-to-debug-ruby
attrs (empty) → {"type" => "recommendation"}
description (empty) → Takeaway repeated throughout the talk: the speaker leaned on small Ruby scripts (linear-search across git history, di...
short_description (empty) → When off-the-shelf tools don't fit, writing a small Ruby script is often the fastest diagnostic tool.
create Stable commits enable bisect takeaway
kind (empty) → takeaway
name (empty) → Stable commits enable bisect
slug (empty) → stable-commits-enable-bisect
attrs (empty) → {"type" => "recommendation"}
description (empty) → Practical recommendation: to use git bisect effectively, every commit on the main history must be independently build...
short_description (empty) → Keep every commit green so git bisect (and similar tooling) remains usable.
create Was debugging dev-environment performance worth it vs. ditching Spring/Guard? question
kind (empty) → question
name (empty) → Was debugging dev-environment performance worth it vs. ditching Spring/Guard?
slug (empty) → was-debugging-dev-environment-performance-worth-it-vs-ditching-spring-guard
attrs (empty) → {"answer_summary" => "Yes — with 50–200 developers running tests daily, Spring's speedup over the first few runs was ...
description (empty) → Audience Q&A question asking whether the multi-week debugging effort was worth it versus simply removing Spring or Gu...
short_description (empty) → Q&A: was the long debugging investment justified instead of dropping Spring/Guard?

Edges (38)

update Damir Zekić → authored → Toolbelt of a Seasoned Bug Hunter
context (empty) → Delivered the talk at wroclove.rb 2018.
update Toolbelt of a Seasoned Bug Hunter → presented_at → wroclove.rb 2018
context (empty) → Talk given at wroclove.rb 2018 on 2018-03-16.
create Damir Zekić → attended → wroclove.rb 2018
context (empty) → Spoke at the conference.
relation (empty) → attended
source_node_id (empty) → 93c1df89-64bd-4e49-b555-d6b618dd6f35
target_node_id (empty) → 9243ef2c-21bb-4f23-b450-9ecd87882dfe
create Damir Zekić → works_on → monolith framework
attrs (empty) → {"role" => "contributor"}
context (empty) → Worked on the in-house business-actions framework that was the subject of the debugging story.
relation (empty) → works_on
source_node_id (empty) → 93c1df89-64bd-4e49-b555-d6b618dd6f35
target_node_id (empty) → 427481c5-23b4-45bf-a5d8-ce6dc5917c20
create Damir Zekić → works_on → granite
attrs (empty) → {"role" => "contributor"}
context (empty) → Announces granite at the talk as the open-sourced successor to monolith.
relation (empty) → works_on
source_node_id (empty) → 93c1df89-64bd-4e49-b555-d6b618dd6f35
target_node_id (empty) → 25015725-35e3-4aec-ae7a-cc0639ce7059
create Damir Zekić → uses → Spring
context (empty) → Describes using Spring in the team's development workflow.
relation (empty) → uses
source_node_id (empty) → 93c1df89-64bd-4e49-b555-d6b618dd6f35
target_node_id (empty) → a182d808-d06a-4ac8-b5ca-97e0dacd1d0e
create Damir Zekić → uses → Guard
context (empty) → Describes using Guard to automatically run tests on file changes.
relation (empty) → uses
source_node_id (empty) → 93c1df89-64bd-4e49-b555-d6b618dd6f35
target_node_id (empty) → 120a09f1-2974-46e8-899f-5b2c40554e78
create Toolbelt of a Seasoned Bug Hunter → about → Spring
context (empty) → Spring's preloader/fork model is central to the reload-slowdown bug investigated.
relation (empty) → about
source_node_id (empty) → 24118e0e-f605-4f4c-914a-526607d647c1
target_node_id (empty) → a182d808-d06a-4ac8-b5ca-97e0dacd1d0e
create Toolbelt of a Seasoned Bug Hunter → about → Guard
context (empty) → The bug was observed through Guard's test re-runs after file changes.
relation (empty) → about
source_node_id (empty) → 24118e0e-f605-4f4c-914a-526607d647c1
target_node_id (empty) → 120a09f1-2974-46e8-899f-5b2c40554e78
create Toolbelt of a Seasoned Bug Hunter → about → git bisect
context (empty) → Speaker explains and uses git bisect as a first-line debugging tool.
relation (empty) → about
source_node_id (empty) → 24118e0e-f605-4f4c-914a-526607d647c1
target_node_id (empty) → db652a7a-07e4-44b7-98fe-15ba29f719e0
create Toolbelt of a Seasoned Bug Hunter → about → find-slow script
context (empty) → Speaker wrote a custom Ruby script to linearly walk git history when no good commit was known.
relation (empty) → about
source_node_id (empty) → 24118e0e-f605-4f4c-914a-526607d647c1
target_node_id (empty) → 4bdf8947-a5d3-4555-88a3-82f45387f502
create Toolbelt of a Seasoned Bug Hunter → about → ObjectSpace heap dumps
context (empty) → Uses Ruby's built-in heap-dump facility to investigate a suspected memory leak.
relation (empty) → about
source_node_id (empty) → 24118e0e-f605-4f4c-914a-526607d647c1
target_node_id (empty) → 73c2e3f4-1523-42ba-aa2e-4a22a738a3b1
create Toolbelt of a Seasoned Bug Hunter → about → heapy
context (empty) → Uses the heapy gem to analyze heap dumps across GC generations.
relation (empty) → about
source_node_id (empty) → 24118e0e-f605-4f4c-914a-526607d647c1
target_node_id (empty) → af27119d-8034-40ca-a11d-80747227291d
create Toolbelt of a Seasoned Bug Hunter → about → Ruby memory retention behavior
context (empty) → Explains that Ruby does not return freed memory to the OS, complicating leak diagnosis.
relation (empty) → about
source_node_id (empty) → 24118e0e-f605-4f4c-914a-526607d647c1
target_node_id (empty) → 94622f7e-a1ff-4bb1-b2ad-9b10fcd72274
create Toolbelt of a Seasoned Bug Hunter → about → Flame Graphs
context (empty) → Renders flame graphs of Rails startup to compare successive boots and locate the growing function.
relation (empty) → about
source_node_id (empty) → 24118e0e-f605-4f4c-914a-526607d647c1
target_node_id (empty) → 0e6fd01c-57c8-4805-ae3d-106d9b86a619
create Toolbelt of a Seasoned Bug Hunter → about → Rails RouteSet clear
context (empty) → Traces the slowdown to RouteSet#clear undef_methoding URL/path helpers on every reload.
relation (empty) → about
source_node_id (empty) → 24118e0e-f605-4f4c-914a-526607d647c1
target_node_id (empty) → a3df8cde-7d91-4c7c-8afe-9dd5c264f35e
create Toolbelt of a Seasoned Bug Hunter → about → Ruby undef_method performance
context (empty) → Benchmarks and source-dives undef_method, showing super-linear cost and method-cache clearing overhead.
relation (empty) → about
source_node_id (empty) → 24118e0e-f605-4f4c-914a-526607d647c1
target_node_id (empty) → 06520dd1-5953-40db-b7c7-2be35c9b6bde
create Toolbelt of a Seasoned Bug Hunter → about → monolith framework
context (empty) → Root cause of the bug was monolith's boot-time controller mounting.
relation (empty) → about
source_node_id (empty) → 24118e0e-f605-4f4c-914a-526607d647c1
target_node_id (empty) → 427481c5-23b4-45bf-a5d8-ce6dc5917c20
create Toolbelt of a Seasoned Bug Hunter → about → granite
context (empty) → Publicly announces granite as the open-sourced successor to monolith.
relation (empty) → about
source_node_id (empty) → 24118e0e-f605-4f4c-914a-526607d647c1
target_node_id (empty) → 25015725-35e3-4aec-ae7a-cc0639ce7059
create Toolbelt of a Seasoned Bug Hunter → about → DescendantsTracker
context (empty) → Epilogue highlights ActiveSupport::DescendantsTracker as a framework cause of classes never being GC'd.
relation (empty) → about
source_node_id (empty) → 24118e0e-f605-4f4c-914a-526607d647c1
target_node_id (empty) → 66f8eadd-4b37-421c-899a-c557b58cc253
create Toolbelt of a Seasoned Bug Hunter → about → Ruby on Rails
context (empty) → The app under debugging is a monolithic Rails application.
relation (empty) → about
source_node_id (empty) → 24118e0e-f605-4f4c-914a-526607d647c1
target_node_id (empty) → 7aac705a-0987-49f2-b665-9d4e08a6acee
create Toolbelt of a Seasoned Bug Hunter → about → Ruby
context (empty) → Deep dive into Ruby internals (undef_method, ObjectSpace, memory behavior).
relation (empty) → about
source_node_id (empty) → 24118e0e-f605-4f4c-914a-526607d647c1
target_node_id (empty) → c7f25b33-06c9-460e-aca7-fe993123ebee
create monolith framework → related_to → Ruby on Rails
context (empty) → monolith is a business-action framework built on top of Rails.
relation (empty) → related_to
source_node_id (empty) → 427481c5-23b4-45bf-a5d8-ce6dc5917c20
target_node_id (empty) → 7aac705a-0987-49f2-b665-9d4e08a6acee
create granite → related_to → Ruby on Rails
context (empty) → granite is a business-actions architecture gem for Rails apps.
relation (empty) → related_to
source_node_id (empty) → 25015725-35e3-4aec-ae7a-cc0639ce7059
target_node_id (empty) → 7aac705a-0987-49f2-b665-9d4e08a6acee
create granite → related_to → monolith framework
context (empty) → granite is the open-sourced successor to the internal monolith framework.
relation (empty) → related_to
source_node_id (empty) → 25015725-35e3-4aec-ae7a-cc0639ce7059
target_node_id (empty) → 427481c5-23b4-45bf-a5d8-ce6dc5917c20
create Rails RouteSet clear → related_to → Ruby on Rails
context (empty) → Rails internal method in ActionDispatch::Routing::RouteSet.
relation (empty) → related_to
source_node_id (empty) → a3df8cde-7d91-4c7c-8afe-9dd5c264f35e
target_node_id (empty) → 7aac705a-0987-49f2-b665-9d4e08a6acee
create DescendantsTracker → related_to → Ruby on Rails
context (empty) → ActiveSupport module shipped with Rails.
relation (empty) → related_to
source_node_id (empty) → 66f8eadd-4b37-421c-899a-c557b58cc253
target_node_id (empty) → 7aac705a-0987-49f2-b665-9d4e08a6acee
create heapy → related_to → Ruby
context (empty) → Analyzes Ruby ObjectSpace heap dumps.
relation (empty) → related_to
source_node_id (empty) → af27119d-8034-40ca-a11d-80747227291d
target_node_id (empty) → c7f25b33-06c9-460e-aca7-fe993123ebee
create ObjectSpace heap dumps → related_to → Ruby
context (empty) → Built into Ruby's ObjectSpace module.
relation (empty) → related_to
source_node_id (empty) → 73c2e3f4-1523-42ba-aa2e-4a22a738a3b1
target_node_id (empty) → c7f25b33-06c9-460e-aca7-fe993123ebee
create Ruby memory retention behavior → related_to → Ruby
context (empty) → Behavior specific to MRI Ruby's garbage collector and heap management.
relation (empty) → related_to
source_node_id (empty) → 94622f7e-a1ff-4bb1-b2ad-9b10fcd72274
target_node_id (empty) → c7f25b33-06c9-460e-aca7-fe993123ebee
create Ruby undef_method performance → related_to → Ruby
context (empty) → Implementation detail of MRI Ruby's method table and method cache.
relation (empty) → related_to
source_node_id (empty) → 06520dd1-5953-40db-b7c7-2be35c9b6bde
target_node_id (empty) → c7f25b33-06c9-460e-aca7-fe993123ebee
create Know thy app → from_talk → Toolbelt of a Seasoned Bug Hunter
context (empty) → Closing lesson of the talk.
relation (empty) → from_talk
source_node_id (empty) → 69f782f8-a1da-4649-a423-32926cf02a98
target_node_id (empty) → 24118e0e-f605-4f4c-914a-526607d647c1
create Don't create controllers at Rails boot time → from_talk → Toolbelt of a Seasoned Bug Hunter
context (empty) → Concrete fix identified at the end of the debugging odyssey.
relation (empty) → from_talk
source_node_id (empty) → c47f4e4b-7644-43ef-9d99-efdd514d7fd7
target_node_id (empty) → 24118e0e-f605-4f4c-914a-526607d647c1
create Use Ruby to debug Ruby → from_talk → Toolbelt of a Seasoned Bug Hunter
context (empty) → Repeated pattern throughout the debugging odyssey.
relation (empty) → from_talk
source_node_id (empty) → c1c1e52f-de1d-46dc-99e4-fe8a8935c745
target_node_id (empty) → 24118e0e-f605-4f4c-914a-526607d647c1
create Stable commits enable bisect → from_talk → Toolbelt of a Seasoned Bug Hunter
context (empty) → Caveat highlighted during the git bisect chapter.
relation (empty) → from_talk
source_node_id (empty) → 5777130a-2b88-4c75-bb0f-17e7c829d1cd
target_node_id (empty) → 24118e0e-f605-4f4c-914a-526607d647c1
create Was debugging dev-environment performance worth it vs. ditching Spring/Guard? → asked_at → Toolbelt of a Seasoned Bug Hunter
context (empty) → Q&A question at the end of the talk.
relation (empty) → asked_at
source_node_id (empty) → 30bf6c20-cf2f-42eb-a8ea-f26310ddd711
target_node_id (empty) → 24118e0e-f605-4f4c-914a-526607d647c1
create Was debugging dev-environment performance worth it vs. ditching Spring/Guard? → about → Spring
context (empty) → Asks whether it would have been simpler to drop Spring.
relation (empty) → about
source_node_id (empty) → 30bf6c20-cf2f-42eb-a8ea-f26310ddd711
target_node_id (empty) → a182d808-d06a-4ac8-b5ca-97e0dacd1d0e
create Was debugging dev-environment performance worth it vs. ditching Spring/Guard? → about → Guard
context (empty) → Asks whether it would have been simpler to drop Guard.
relation (empty) → about
source_node_id (empty) → 30bf6c20-cf2f-42eb-a8ea-f26310ddd711
target_node_id (empty) → 120a09f1-2974-46e8-899f-5b2c40554e78

Read set

72 nodes

tool Ruby on Rails search_nodes talk Business logic in Ruby search_nodes takeaway Replay-Based Debugging search_nodes talk Rubyana Gems and the Ractorous Rubetta Stones! search_nodes tool Rails 5.2 search_nodes talk Orchestrating video transcoding in ruby search_nodes talk When REST is Not Enough: Implementing Alternative Protocols in Ruby on Rails search_nodes tool Iodine search_nodes person Akira Matsuda search_nodes talk Introducing Sorbet into your Ruby codebase search_nodes concept Static Page Caching via nginx search_nodes talk Better WebPerformance with Rails search_nodes concept Rails Reload-Safe ES Configuration search_nodes takeaway Serve preheated static pages via nginx search_nodes concept Cache Preheating search_nodes question What is an enterprise Rails application? search_nodes talk No-build Utopia: Modern User Experiences with Rails & Web Standards search_nodes question Is rendering from cached pages with server-side includes possible in Rails? search_nodes concept Preloading and Prefetching search_nodes concept Breadth-First Search for Code Removal search_nodes talk Removing Code with Breadth-First Search search_nodes concept Waterfall Analysis search_nodes tool Babel search_nodes takeaway Watch WebAssembly for real performance wins search_nodes tool Grafana search_nodes tool webpagetest.org search_nodes resource Rocket Real-Time Benchmark search_nodes tool Rust search_nodes takeaway Sign serverlessforruby.org petition search_nodes tool Flow search_nodes tool Ruby Event Store search_nodes tool rails_event_store search_nodes tool Action Cable search_nodes tool JSON API Resources search_nodes talk Working with RailsEventStore in Cashflow Management System search_nodes question Can parts of Eventide, Rails Event Store and Trailblazer be combined in one project? search_nodes concept Active Record search_nodes concept Trailblazer Workflow search_nodes talk Multi-region data governance in Rails application search_nodes event wroclove.rb 2018 search_nodes event wroclove.rb 2019 search_nodes event wroclove.rb 2024 search_nodes event wroclove.rb 2022 search_nodes event wroclove.rb 2023 search_nodes event wroclove.rb 2026 search_nodes event wroclove.rb 2025 search_nodes talk Building LLM powered applications in Ruby search_nodes talk Counterintuitive Rails pt. 2 search_nodes talk Counterintuitive Rails pt. 1 search_nodes talk Debug like a scientist search_nodes talk Fix Production Bugs 20x Faster search_nodes talk Toolbelt of a Seasoned Bug Hunter search_nodes+get_node_edges concept Global Interpreter Lock search_nodes tool Heroku search_nodes tool dry-struct search_nodes tool Ruby search_nodes person Koichi Sasada search_nodes concept Method References in Ruby search_nodes takeaway Upgrade Ruby version for free speed search_nodes talk Methods Gem for Ruby Method References search_nodes talk JRuby: Professional-Grade Ruby search_nodes talk An Introduction to Test Bench search_nodes talk Securing Rails applications search_nodes takeaway Use Refinements for Coordinated Modernization search_nodes talk Nightmare neighbours caveats of Rails based mutlitenancy search_nodes concept Refinements search_nodes person Damir Zekić search_nodes person Adam Okoń search_nodes person Julik Tarkhanov search_nodes person Emiliano Della Casa search_nodes concept Ruby Module Customization Mechanics search_nodes concept ActiveSupport Core Extensions search_nodes

2 edges