Unfortunately, it's more of an art than a science. The simple rule is: it should be as simple as possible, and no simpler than that. Obviously, everybody has a different idea of where exactly that line is.
Usually though, if you try to make the code clean and as small as possible while still allowing unit tests to be written, you normally hit a decent spot. YAGNI applies very heavily here too and it's often better to make the code less generic and refactor it later when required.
Usually though, if you try to make the code clean and as small as possible while still allowing unit tests to be written, you normally hit a decent spot. YAGNI applies very heavily here too and it's often better to make the code less generic and refactor it later when required.