I've always programmed thus:
* Try to assimilate a mental model of the bit I'm working on, and as much of that bit's dependencies as is practical, into my head. This may involve fiddling with the code and seeing how it breaks when I do certain things to it, or hammering at it inside a REPL or similar. The bit that I'm concerned about could be a single method, but is usually larger.
* Try to develop a mental model of the bit as it _should_ be. Do a diff between the 'is' and 'ought', make code changes as necessary, fiddle with the new code to make sure the shape of its function is roughly the 'ought' shape.
* Update the unit tests with specific tests for the 'ought' case, run tests, fix failures.
* Check in or submit code for review.
This is not how software is written now. How software is written now is a hill-climbing algorithm where each cycle is one "red-green-refactor" sequence. Write a tiny failing test which no matter how small or trivial still has a few lines of setup/teardown boilerplate, make the smallest possible code change to make the tiny test pass (change minus sign to plus sign?), run the ENTIRE test suite to make sure that your sign flip didn't break anything anywhere else, if the code needs to be refactored do so and run the entire test suite again, fucking repeat. There are a number of theoretical advantages to this from a management perspective:
* Developers produce working code quickly in the early stages of the lifecycle, whereas with the slow approach you do a lot of sittin' and thinkin' up front without much to show for it -- maybe some design documents or UI mockups, I dunno.
* Development no longer relies on developers' internal mental models of the code. Developers now code against a model which is incarnate in the test suite -- itself a deliverable, documentable artifact.
* Because programmers no longer work from internal mental models but the single-bit state of "do the tests as written pass or fail?", the concept of "code ownership" -- along with related management problems of coders waxing territorial over the pet modules they've spent days or weeks ruminating over in order to understand deeply enough to change -- evaporates.
* Programmers look busy all the time, because they are constantly typing out test code to exercise even the most trivial of changes.
* Thanks to pair programming, you no longer need worry about the "guy in a room" problem. Your dev team is always seen to be interacting with one another.
But I hate it because it is incompatible with my temperament. I like to think about _what_ it is I intend to write, before hammering out test cases to exercise it.