Expandable software naturally lends itself to modification of its scope.
When approaching a project, I believe that after the information-gathering phase is over and project requirements are well-established, it's important to take a step back and analyze, in a more general sense, what this software is doing.
I then split the project into individual components of functionality which have a highly-defined set of inputs producing a well-known set of outputs. I identify the transports by which this information will pass, in which way these transports can scale and fail, and build a reusable public interface which responds to and on those transports.
Developing in this way allows me to limit the impact of inevitable changes in scope or functionality to a small amount of code:
I don't assume a project can be fully defined before I begin building it, preferring instead to build easily modifiable software that can be iterated upon.