I frequently redesign/rewrite an implementation a few times before committing it, often changing observable behaviors, all of which will change what the tests need to look like to ensure proper coverage. Some code is intrinsically and unavoidably non-modular. Tests are dependent code that need to be scoped to the implementation details. Unless you are writing simple CRUD apps, the design of the implementation is unlikely to be sufficiently well specified upfront to write tests before the code itself. Writing detailed tests first would be making assumptions that aren't actually true in many cases.
I also write thorough tests for private interfaces, not just public interfaces. This is often the only practical way to get proper test coverage of all observable behaviors, and requires far less test code for equivalent coverage. I don't grok the mindset that only public interfaces should be tested if the objective is code quality.
When practical, I also write fuzzers or exhaustive tests for code components as part of the test writing process. You don't run these all the time since they are very slow but it is useful for qualifying a release.