Figuring out the proper abstractions is much, much harder than writing the software itself. Software changes requirements... When you do something new and useful for a customer, they will extend use of the software till it breaks again. You will always run into a new bottleneck.