Dave had an interesting experience where duck typing dug him (and a client) out of a hole. Hed written a large Ruby-based Web application that (among other things) kept a database table full of details of participants in a competition. The system provided a comma-separated value (CSV) download capability, allowing administrators to import this information into their local spreadsheets.Just before competition time, the phone starts ringing. The download, which had been working fine up to this point, was now taking so long that requests were timing out. The pressure was intense, as the administrators had to use this information to buildschedules and send out mailings. A little experimentation showed that the problem was in the routine that took the resultsof the database query and generated the CSV download. The code looked something like def csv_from_row(op, row) res = "" until row.empty? entry = row.shift.to_s if /[,"]/ =~ entry entry = entry.gsub(/"/, '""') res << '"' << entry << '"' else res << entry end res << "," unless row.empty? end op << res << CRLF end result = "" query.each_row {|row| csv_from_row(result, row)} http.write resultWhen this code ran against moderate-size data sets, it performed fine. But at a certain input size, it suddenly slowed right down. The culprit? Garbage collection. The approach was generating thousands of intermediate strings and building one big resultstring, one line at a time. As the big string grew, it needed more space, and garbage collection was invoked, which necessitated scanning and removing all the intermediate strings. The answer was simple and surprisingly effective. Rather than build the result string as it went along, the code was changed to store each CSV row as an element in an array. This meant that the intermediate lines were still referenced and hence were no longer garbage. It also meant that we were no longer building an ever-growing stringthat forced garbage collection. Thanks to duck typing, the change was trivial. def csv_from_row(op, row) # as before end result = [] query.each_row {|row| csv_from_row(result, row)} http.write result.joinAll that changed is that we passed an array into the csv_from_row method. Because it (implicitly) used duck typing, the method itself was not modified: it continued to append the data it generated to its parameter, not caring what type that parameter was.After the method returned its result, we joined all those individual lines into one big string. This one change reduced the time to run from more than 3 minutes to a few seconds.