> If breaking up the code makes it slightly less easy to understand right now, but will make it easier to add some feature later that you know you know you're definitely going to need, then hold off.
This. Your code shouldn't be documenting what it will do in the future: it should be clear what it's doing right now. Otherwise some other engineer maybe from another team will come upon it, misread it, and bugs ensue. Trying to save yourself time in the future ends up wasting time for others, and likely for yourself as well.
This relates to "Rule of 3 Before Abstracting": One concrete instance of a proposed abstraction is YAGNI (adding an abstraction blurs the concrete code and makes it harder to understand), two concrete instances are coincidence (it's maybe a sign that you need an abstraction between the two, but you don't have enough scientific data to consider), at least three concrete instances is (finally) a pattern (abstracting a known pattern is right and good and because it becomes a document of the high level pattern itself and the differences and distinctions between the concrete implementations become more distinct and obvious).
That "Rule of 3" is sometimes a good reminder itself not to build for some possible future of the codebase, but to build for the patterns in your code base as they exist today.
I hadn't heard of it but it's a good rule! There are too many examples of code that was abstracted out to be shared between uses that end up evolving independently, and the abstraction ends up with a bunch of if-statements and confusion.
This. Your code shouldn't be documenting what it will do in the future: it should be clear what it's doing right now. Otherwise some other engineer maybe from another team will come upon it, misread it, and bugs ensue. Trying to save yourself time in the future ends up wasting time for others, and likely for yourself as well.