The biggest problem with building software is that we rarely adhere to ways to ensure that it can change from it was intended to be, into what it needs to be.
I usually make a naive basic distinction of a generic solution and a specialised solution, based on the amount of knowns and unknowns. If you are very familiar with the problem and you know your audience, you can work very fast and create something that is nice, snug and specialised. The less you know, the more generic and adaptable you need to be.
Software engineering is not only about programming, it is about utilising the most confident methods of handling complexity and unknowns. For the most part it is about managing errors. For that I mean, you have reference counting, garbage collectors, exceptions handlers, unit tests, systems tests, profilers, debuggers, static analysis, coverage tests, documentation, bug trackers and other big items that are their to account for your errors, find your errors or tell you where errors might be found, and how to create new errors(or features). And there are certain protocols of working for groups of programmers so that they can work more reliably and produce less errors for the systems in place. Manage memory, manage performance and manage your errors.
Even though all the professional building tools are available for almost free, all their problems will come with it. You need to know more than just how to use them, you need to know what issues they will introduce so you can use them properly.