Therefore, one of the very first choices to make in designing a software is which abstractions to use, and how to provide footholds for the abstractions in the framework so that they maintain separation of concerns. There are many long standing obstacles to meeting requirements above. There has always been a trade-off between modularity and performance. Modularity is necessary for maintainable and reusable code, but it compromises performance. Some of the conflicting requirements require hard-nosed trade-offs in design. For example, most composable codes run relatively slowly because they sacrifice performance for multiphysics and composability. Sometimes this means using suboptimal options for individual components. The framework should be able to facilitate such unorthodox approaches and therefore should provide hooks for being able to make these choices.
Another important consideration in scientific software design is its dynamic nature. Codes designed for one problem are routinely modified to use other similar problems, or need modification because growing understanding places more demands on the current model. Therefore, extensibility in also a very important aspect of scientific software design.