I think I'm capable of writing better code, but just so deep in the habit of "hacking" that I can't be bothered to. What are some ways to force myself to write good code from the beginning?
Depending on your language of choice, if it has tools to initialize and manage packages/system/whatever-deliverables, make use of that to ensure good practice within the context of that language.
Build a CI/CD pipeline, even if it's just local. Most of my code never really leaves my machine (even though I use git for everything I write). I usually have a monitor running (either built into the language tooling like with `dotnet watch test` or just homegrown with `fswatch`) that executes tests/builds with every file save.
Pairing the previous two paragraphs, to avoid rewriting those scripts/tools every time I've forced myself into a habit of using good, standard organization for projects. By incorporating more testing (whether full-on TDD or not), I have to maintain the code quality a bit more.
Also, I've always known that the more time spent with pen and paper before starting to type, the less time it will take and the better organized and thought-out the program will be.
To be specific about the refactoring/clean code, it's basic stuff like: Use spacing to make maths clearer (e.g. write 2x4 + 7, not 2x4+7). Functions that do one thing, with names that perfectly describe what they do. Variables perfectly named, used for one thing, grouped appropriately in data structures. Move comments into code. (Not "//this bit computes the size" but put it in a function called ComputeSize.) No magic numbers. Basic stuff that somehow I'd never focused on before...
I'm not into "forcing myself" to do anything, that doesn't sound like a good way of thinking about it! But maybe taking one of your messy programs and editing/refining it until it can't be improved further will inspire you like it did me.
You could comment the hacked parts of the code. This will allow others or yourself to understand the novel implementation at a later time. I have encountered those sorts of comments before and they are helpful to understand the code and add a new trick to my knowledge.