Killing complexity with TDD
Sometimes it’s the small things…
In short
I’m working on an API that can take one of its arguments in a couple of different forms. TDD helped me quickly see where I was going to end up with some combinatorial complexity, so I quickly changed my design to avoid the complexity.
My Rails controller
It’s a common thing: add a user to a group. The old UI allows adding 1 user at a time. The new UI allows adding any number of users at a time. I’m planning to support both.
Here’s what the original controller, supporting the old UI, might look like:
I got the new UI working, and needed to update the controller. Being that it was so simple of a controller, I had previously skipped unit tests for it. So, I spun up a new unit test suite with tests to catch any regressions…
Now to add the multi-user test. (In this case, the UI is building a single input with a ‘,’-delimited list of user ids.) Here’s the first test:
In the back of my mind, I start thinking about other tests I’ll need to write. One set of tests that comes to mind is the case where a :user_id and :user_ids are both passed in.
Hm. How can I avoid writing all these combination-testing tests? Well, seeing as 2! is bigger than 1!, if I can figure out how to drop the number of handled parameters from 2 to 1, I’ll avoid the extra complexity.
In this case, :user_id => '1'
and :user_ids => '1'
should behave the same,
so there’s really no need for two different parameters. A quick UI & test change …
… and I’ve crossed off several tests in my mental checklist.
The API isn’t necessarily ideal (I’m mixing up singular and plural concepts). But, I get a simpler API that still supports the old UI.
Wrap up
In this case, the added complexity was relatively small (only two choices), so the savings in test effort was minimal. Also, I don’t know that I would have actually tested the extra cases, because they’re not that likely or interesting.
However, I think the principal applies: TDD helps you spot complexity as it’s forming, and trim it back to a reasonable level.
Also, I just didn’t have to think any extra “what-ifs”.