When I started coding, I believed I could get to the right code abstraction on my first attempt. I just had to focus on the problem and carefully consider all the variables involved. This was true for simple use cases. However, as my career progressed and I faced new challenges, I realized it became increasingly complicated to get to a good abstraction easily.
One day, while discussing software over a beer, a friend introduced me to "the Michelangelo approach" which came from this quote:
"The sculpture is already complete within the marble block, before I start my work. It is already there, I just have to chisel away the superfluous material." ― Michelangelo
This approach is a simplified version of Test Driven Development (TDD), where you follow the sequence "Make it work, make it pretty, make it fast."
Make it work:
When writing code, the first step is constructing the necessary components, such as conditions, if statements, classes, etc. to ensure the feature works properly. It's essential to create a stable interface for the feature, meaning that the API consumers will use should remain consistent. The only changes that will occur in later stages of this process will target the inner components.
Make it pretty:
Once you've got your code working, it's a good idea to add unit tests or other kinds of automated tests to ensure its reliability. Then, you can start refactoring your code. The great thing is that now you've explored all the code paths and know what needs to be done, the abstraction is no longer hidden. You just need to reorganize the code to make it more readable and comprehensible, so that anyone can easily understand it.
Make it fast:
Last but not least, make any performance improvement needed, run explain analyze on the queries you added, etc.
This method applies to any design level, including architectures, modules/namespaces, classes, functions, etc.