More seriously, writing (failing) tests before writing code is the essence of TDD and BDD. It means designing the tests and then testable code. The result of writing (failing) tests first is that it actually changes how you write the application code, i.e. to be testable. This results in very different code. Small methods are just one example of the result of this approach. Although TDD implies test first, it often means writing the tests along with the code and going between the two a lot as you discover negative and edge cases to cover. Having two different people (that aren't live pairing) in this will be very slow.
When a different person such as an automation engineer writes tests this is usually going to be in the BDD form of Given, When, Then.
When writing unit tests it is not practical to write the right tests beforehand when you are a different person in a different role because as you write application code you discover approaches and negative/edge test cases and perform refactorings as you code the solution. Separating tests and application code this way will slow the process and lead to lower quality solutions.
Returning to the term 'normal'.
It is probably uncommon but, with the right approach such as high level BDD, it is the right way. If it works for you. However you will need a lot of interaction with the developers along the way. You do not have to implement cucumber btw. You could specify Given, When, Then in tickets for developers to use. I have done this effectively and find that it provides them with the specification they need.
- do we want the automation engineer to specify behavior (declarative) or implementation specifics (imperative).
For example "User completes the add item form" is declarative behavior where as "User clicks on Complete Item button and database is updated and they are taken to the details page" is imperative behavior, i.e. the "what"(imperative) vs the "how"(declarative).