100% coverage may not necessarily be possible, but you should be able to get close in most cases. I usually set the bar at 95%, because sometimes you just can't get all the way.
Typically, when I start from scratch, I'll usually write a test. If I'm at 100%, I'm done. Otherwise, I'll refactor branches to minimize uncovered areas. If I'm at 100% after an initial refactor, I'm done. Finally, I write unit tests to trip as many exception paths as I can. Thanks to @TestSetup, it's now a lot easier to reach 100%, but there are something things you simply can not cover.
They're impossible, because the language has no way to test them. For example, anything to do with row lock handling is impossible to reliably test, because you can't simulate a row being locked as a separate transaction.
What you must cover are all the primary paths through your code. Validate that they work correctly. The primary paths should be at least 75% of your code, because that will allow you to satisfy both the 75% minimum requirement, as well as the philosophy that you should verify the primary paths work correctly.
Using the initial unit test to gauge refactoring lets me not worry about unoptimized code ahead of time. The unit test will tell me what I did wrong. You shouldn't usually spend more time writing your tests than you did writing the initial code (e.g. 50% of your development time should be developing/fixing bugs).
Any more than that, and you'll start to get testing fatigue, as I like to call it. Each additional test beyond the first is going to yield smaller and smaller returns, to the point where you'll be writing 25 lines of code just to cover another 1 line of code, for example. If you're at 75%, you've covered all your branches, you've refactored, and you've reached a point where 1% takes more effort than the first 75%, that's usually the time to give up, or at least do it in phases.