So a little snippet about why form is important. This system I inherited definitely has a "God object". Like pretty much every other order-processing/e-commerce project, that's "Order". I've whittled it from about 900 LOC to ~600 so far, and every bit is a battle (but one worth doing!). A virtue of having smaller objects that do one (or at least, for today, n-1) things is that it's actually practical to test that one thing.
The "unit" tests on the order object take about 30 seconds to run (it's also about 600 lines) and expensive to maintain. Today I am finally extracting a chunk of logic out of it (since I'm going to be extending functionality of that bit of logic) and I found that of the about 30 LOC I'm extracting, there are, I'd say six different behaviors. Those are:
- Check one of the order's parents to decide if it needs external reporting
- Convert the order to a hash
- Replace one of that hash's attributes with a "processed" version
- Replace another of that hash's attributes with a "processed" version
- Initialize a reporter
- Send a report
Of those, the following behaviors have any testing at all: 1, and 2. The hash attribute manipulation is actually somewhat hairy logic, but I'm sure it's a huge pain in the butt to test while it's buried there in the order object (I'm certainly not going to try).
So now I've extracted the entire external data reporting code to:
def complete_order_delayed ... SubtleDataReporter.new(self) ... end
Which can be tested as such:
should "send itself off to the SubtleDataReporter" do SubtleDataReporter.expects(:new).with(@order) job = @order.complete_order_delayed job.invoke_job end
Simple, complete, and effective. Now I can start building a testing framework around the hairy code in isolation and extend it.
So why is form important? Because once you've sacrificed form, the costs of good discipline (testing, readability, etc) begin to outweigh the benefits.