I've been a Ruby developer for about five years now. I'm not quite a black belt, but I'm confident with my moves and sometimes, even graceful. Recently, whilst attempting a code kata with a friend, I found myself rejecting my experience in favour of the more intuitive style to me.
It surprised me how quickly I "dropped down" into a procedural technique. Like choosing weapons in some arcade game I selected excitedly- taking the bokken instead of the object-orientated nunchaku. I had side glanced the functional sai, but my unfamiliarity with these seemed too risky against this combatant.
Code katas are not just raw bouts. Matches can be won with brute force; though this is often time consuming and energy draining. The kata is often carefully crafted to force the competitors to not only think about how to win, but also require they "feel around", that they are experimental, and push themselves out of their comfort zone.
Players may find they have an algorithmic advantage with a particular kata, and so the challenge is made more difficult for them by the addition of other constraints or in attempting it with an unfamiliar fighting style or language. A code kata can challenge you at whatever level you consider yourself to be capable of.
Being challenged by the Anagrams code kata, I pulled on my first strategy, one of brute force and almost ignorance. I struck out, bashing, chopping, slicing and mashing. It blocked, retreated, out manoeuvred and countered me. I picked myself up, dusted myself down. I conferred with another competitor.
"You're sloppy. Think about what you are doing; extend your arms, breathe, let your movements flow. Be more graceful. You are on the right lines, you're just not fully applying yourself."
My enthusiastic but frenzied attacks weren't working. Each round, I entered, slowly constructing a sequence to win, but each time, reaching the same dead end- the defence was always there, blocking me, preventing my success. I walked, head-lowered, back to my seat, metaphorically. I felt beaten by this one.
"I give up. I'm thinking I'm winning, I'm making contact, then with a single blow from my opponent I'm on my arse again!"
My team mate stood confidently. "Let me show you some of my technique.", he smiled. "You were on the right track with the hash-table. This is a solid stance. But look, once there, you are restricting yourself by not embracing the full-power of sort. See," he continued, "once you have that in place, then the opponent can then be broken.".
He was right. Now I felt like I could take it on again. Firstly, just to win outright, but also to beat it in a new way. Faster. More elegantly. With more constraints (like an arm behind my back).
"Now, go and try the Trigrams kata.".
Do your upfront planning and sweat the details. There's a time an place for the level of detail you discuss; at your team meetings should be high level, and with your pair should be really low level. Defer as much as you can. If you are unsure of any aspect, ask about it. Be a nag. Be thorough. Be the craftswoman or man.
Can you explain in a sentence what you are trying to achieve? How about to someone you perceive to be smarter than you? What will they think to your solution?
Be prepared to throw it away. Then throw away the next load of stuff you create too. Use version control.
Test 'first' to help you design the system. Test 'during' to help you refine it. Test at the end as a sanity check. Get someone else to test it too. Accept their feedback gracefully.
You want to be the best, right? That takes training, practice, dedication and the help of others. Rely on them and give it all back in return. You are not brilliant yet, but you will be.
Keep each block of work small, and complete that block. If it feels tricky, it is- break it down! Finish it. Tick it off. Recreate blocks that you are not satisfied with later.
Make it red. Make it green.
If you don't understand any aspect of what you are creating, stop and do your research. Don't be afraid of reading. Be even less afraid of asking. Stupid is sitting for 3 days trying to solve the 5 minute problem.
Don't bolt tests on as an after thought because that is all they will be- an after thought. You'll not remember the decisions you made and why you made them.
Recently, I have been thinking about my approach to unit testing with Ruby, in particular relating to private methods within a class. It is generally accepted that you should only test the public interface to a class, with these tests in-turn exercising the private methods therein.
With any class of reasonable complexity, i.e. a typical Rails model complete with obligatory business logic, I am finding that during the course of development, the refactoring process pushes the grunt-work down into the class' private abyss.
This makes me feel uncomfortable as I think that with multiple moving parts, (i.e. changes to many methods; i.e. to the API and its supports) the chances of edge cases and unimagined side effects creeping into the system increase- inconspicuously, and lie dormant, waiting to embarrass me.
How do I tackle this? Test driven development can and should help here, but in legacy systems, I wonder if we sometimes find ourselves paying the technical debt left over by others. This isn't an attractive prospect when required to provide minor fixes or tweaks. It is here that we could expend inordinate energy either capturing the current behaviour (providing "coverage") or changing the existing suite just to get it into the right state for testing. In a class with many private methods, each one controlling an aspect of the class' behaviour, this becomes a nightmare of different contexts, a sea of stubs, and generally what could be considered the recipe for missed subtleties with a side portion of uncertainty.
So what else can I do? Tempting as it might seem to start unit testing the private methods themselves, this indirect implementation testing makes the test suite brittle and prevents us from safely refactoring later. We cannot easily determine how the methods work together this way. I think the other problem here is that I fear my view is obscured; that the class itself is maybe badly designed- the specs lulling me into a false sense of 'correctness' when infact the class is simply doing too much. Perhaps after painstakingly testing a class, I would have developed an emotional attachment to the code, a sort-of "Stockholm Syndrome", just because I inappropriately provided coverage for it.
Can I delete some code? The alarm bells are ringing, and my colleagues and I would be rightly be nervous. Often, we cannot be certain of the impact of this action- after all we are potentially deleting knowledge and information stored in our code; the product of our collective toil.
So where does this leave me? I think the only safe approach left would be to extract collections of methods grouped by their utility to separate classes. The collaborators can be mocked where necessary to simplify testing and we've reduced the size of the original class- also providing ourselves with less cases to consider in our new class.
In conclusion though, I am still left wondering what the consequences of these actions are. By creating new classes have I increased cognitive load for my colleagues as they attempt to assimilate these new parts of the system during each iteration of development? Do we now need to understand a whole collection of classes every time we want to change something? Can I conveniently assume the collaborating classes to be "just doing it right" just because they were named well? Extracting to classes seems like a good idea, but when will it bite us in the behind?