> How can a team combat this, so that we can simultaneously avoid premature abstraction and broken windows leading to overdue abstraction?
From experience, premature abstractions are rarely premature unless you really try and anticipate years into the future. What happens is that abstractions can go in the wrong direction, and when they're not useful then they're called premature incorrectly.
For all the reasons outlined in the article, you want to anticipate how the software will scale, to allow room for growth. As a simple example, if you need one component, think what are the likelihood of similar components in the future, then parameterize that component so it avoids duplication. There is some risk in doing this if you get it wrong, but that's what skill and experience gives you (if you do lots of software design).
The broader pattern goes like this: spend some extra time building out the software to accomodate potential future growth, and be explicit about the assumptions so you know when the growth will start to strain the system again. At that point anticipate that next abstraction point, and plan accordingly to then abstract everything to the next level, adding even more room for scaling the software (not the users, but the actual code and features). This creates slow periods of development, followed by big spikes of productivity as the abstractions are used, i.e. creating a new feature is as simple as importing and using a few functions or maybe even just configuration.
> How can a team combat this, so that we can simultaneously avoid premature abstraction and broken windows leading to overdue abstraction?
From experience, premature abstractions are rarely premature unless you really try and anticipate years into the future. What happens is that abstractions can go in the wrong direction, and when they're not useful then they're called premature incorrectly.
For all the reasons outlined in the article, you want to anticipate how the software will scale, to allow room for growth. As a simple example, if you need one component, think what are the likelihood of similar components in the future, then parameterize that component so it avoids duplication. There is some risk in doing this if you get it wrong, but that's what skill and experience gives you (if you do lots of software design).
The broader pattern goes like this: spend some extra time building out the software to accomodate potential future growth, and be explicit about the assumptions so you know when the growth will start to strain the system again. At that point anticipate that next abstraction point, and plan accordingly to then abstract everything to the next level, adding even more room for scaling the software (not the users, but the actual code and features). This creates slow periods of development, followed by big spikes of productivity as the abstractions are used, i.e. creating a new feature is as simple as importing and using a few functions or maybe even just configuration.