My favourite is when I go back to old code, think "why on earth was that written that way" and then proceed to rewrite it. Then after an half an hour in I realise that my rewrite is worse than the original method I used. At which point I promptly undo my changes then leave a comment for future self explaining why my superficially bad code works well.
Sadly those instances are fewer than those where my old bad code is literally just bad.
I don't mean this as a response to you personally, but instead to everyone who works this way.
We've been in this situation lots of times, where we'll write some code, then finish the solution after a few days or a week, then look at it a month later and say "Wow, this is really not a good way of doing it," only to spend another few days or weeks rewriting it.
The idea of spending 10 minutes drafting a solution, throwing it out, spending another 10 minutes on diagrams and guesses, throwing it out, typically gets met with "Just _code_ damnit," but, diagramming could compress months of rework into minutes.
You're not wrong and my programming style is very much like that albeit I use the code itself to diagram as I find that's easier for my brain to parse than flow charts. But effectively it's the same process of "sketching a design and throwing it out" cycle that you described.
I suspect quite a few developers who "just code damnit" follow this same process too. After all, it's not exactly hard to rewrite code and with tools like Git in place you can easily stash the different implementation methods so you're not having to lose any work during the cycles.
Usually when bad code gets written by experienced developers it's not so much because of a lack of willingness to conceptualise the design but often just because either the deadline is sufficiently tight that you are forced into writing a "quick fix" rather than something robust. Or because the code base is already an mess (due to the evolution of the product and the aforementioned issue of quick fixes) which means the "ideal world" solution is a significantly larger undertaking than it should be and best not undertaken while you have deadlines depending on it. Or sometimes you see "bad" code simply because the aim of the project is a minimum viable product or proof of concept, thus it's more about proving the product works as a concept than the implementation of it. In that scenario it can make a lot of sense to throw quick code at a problem with the understanding that chunks of the application will be revisited when you start to scale the product.
I do sketches both on paper, and in code. I write all my code in many, many drafts, so that by the time it ships each function has been rewritten. I'd guess about 5-10 times on average. Possibly contrary to intuition, I find this enables me to work much, much faster, and have far fewer defects than not doing it.
> My favourite is when I go back to old code, think "why on earth was that written that way" and then proceed to rewrite it. Then after an half an hour in I realise that my rewrite is worse than the original method I used.
...and remember that the "rewrite" is similar to an even older version that you temporarily forgot about.
As I've matured as a developer, I've taken to commenting much more heavily. However, I focus more on capturing why I'm doing something, which sometimes includes why I didn't do it a particular way.
Why vs What in comments is such a hard distinction to make, that I suspect many professional programmers never reach.
In my past few contracts, the devs at each shop fell cleanly into the overcommenting or never comment school of thought. Many old time C coders hate or love comments for exactly the opposite reasons as new grads writing Javascript or Ruby, and when conversing most argued that the code should explain itself. It is very hard to make code explain why because it is doing what it does, and this tiny nuance is hard to grab.
Just associating the code with a bug or feature request often helps the next guy intuit why you did something.
One job we kept reintroducing the same bugs and the customer was furious. I started reading the version histories more closely and figured out two developers were dueling over two separate bugs in the same block of code. Each would reintroduce the other' bug. Since then and due to some other experiences, I spend time looking at how the code arrived to deduce why it was the way it was. I pride myself on a low regression rate and this helps a lot.
Note that if you value quality over quantity, you're self
-selecting for writing less code but in more critical parts of the application, like libraries or cross cutting concerns like security or localization. And you also have to accept that if you insist that everyone on the team coded like you then nothing would ever get done. The last bit is, IME, the hardest part.
Once I had a situation where I was the developer assigned to a particular interface and my code was synonymous with the code.
Unbeknownst to me, a defect was entered and another programmer made a change. It wasn't really a bugfix but more like a change in the desired functionality. His change worked perfectly well but I wasn't made aware that someone else had changed the code. When I was assigned another defect, I used the version of the code that I had from my last checkout to make the change. When I checked in, I overwrote his change.
We quickly discovered this during testing and I reverted my change. I used his last checked in code as the basis for my change and all was right with the world but we had a confused user and two confused programmers for a couple of hours.
I'm surprised your version control solution let this situation arise considering this is exactly the kinds of problem version control aims to resolve. Usually you only hear of these things happening when you have multiple devs FTP / SFTPing files to a shared repository rather than following a managed check out and check in procedure via your preferred VCS frontend.
Sadly those instances are fewer than those where my old bad code is literally just bad.