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?