The straw that breaks the camel's back
Very simply put, the extra effort you're talking about is a straw. The camel does not care about dealing with the added straw, so that's all fine. But by reapplying this "another straw makes no difference", eventually the camel's back will break. And it's not the final straw's fault. It's the fault of all of the unnecessary straws you put on.
why not making a special function or method
There's a progression of events here.
- Let's make a special function for a particular case.
- Now we have two functions.
- Wait, we're violating DRY. We have to create abstractions to reuse our logic.
- Now we have three functions, the abstract one and the two concrete variants.
- Should we make more special functions for particular edge cases? Because we did one, but there's three more. Why would this edge case be treated differently? Bob thinks the other edge case is more important, anyway.
- Now we have many functions to cover all permutations of which edge cases apply in which scenario.
- Now we have to scroll through a ton of code to find the bit we're interested in. Sure, the bit itself is readable, but it's lost in a jungle of individually readable bits.
- We've undone the readability we tried to achieve.
You may think the "many edge cases" argument is only applicable to rare circumstances, but you already hit on that point with your taxi driver example. You singled out "if not drunk and has a license" as a precondition. So what about using "if not drunk and having 3+ years of experience" as a precondition? What if "having a license" is a precondition by itself?
You've made it hard for me. Often, I find myself having to explain that keeping code short and clean is something that people often gloss over and don't understand how much it helps them. You've gone the other way, and now I find myself arguing the point in the opposite direction.
To me, it's always been obvious that using a more specific method is the only way to go.
A very big red flag for me is "the only way". There is more than one way to skin a cat. There is merit to your intention but it is not an absolute, nor is it the epitome of perfection.
I appreciate the intention of your approach. You are making an effort with the intention to enhance something. But overzealousness looms, and you need to be aware of both the lower and upper boundaries of reasonability. This latter part is where I think you've gone off the rails a bit.
- Less code to follow through when reading.
Yes, each individual method becomes more readable. However, the gains from simpler methods are counterbalanced by the much larger amount of methods to know about, be able to use, and support in the future. And that balance needs to be struck very, very carefully.
- Using more specific methods gives more insight into assumptions made by the author. Fewer "why" questions and WTF moments.
Similarly to the point above, you are trading away WTF moments for readers, and instead gaining WTF moments for writers who now have to decide between many different options, which entails learning about all of them, their differences, and having to evaluate which variant is best for them in this particular use case.
Enough WTF moments for writers leads to writers making mistakes or simply getting exhausted from the effort, which in turn will negatively impact the quality of their work, which in turn negatively impacts the reader experience.
From my experience as a consultant in many companies that got stuck on a project, the worst codebases that became unmaintainable messes were not due to lack of effort, but rather an overabundance of unchecked misguided effort that overcomplicated a simple thing and lost track of the ultimate goal.
- Methods have less diverse use cases and, therefore, became simpler.
Think of it like being a mechanic. Yes, a mechanic has several tools in their workshop, because having a single tool for everything tends to lead to an inferior tool overall (jack of all trades, master of none). However, that doesn't mean that there isn't an upper limit on tool count. You have to also consider having too many tools is also a problem for the mechanic.
Taken to a silly level, eventually the mechanic will have to have an entire warehouse and staff just to find the right tool among his collection of millions.
I'm of course exaggerating for the sake of example, but this is the same balance you need to strike. Your argument seems very much rooted in the "more is better" ideology, and I don't quite detect a "but too much is bad" modifier in your argumentation.
A better approach
To be clear, subdividing your logic is good (but never forget that, as with all things, there is an upper boundary). The red flag I'm responding to is the arbitrary preconditions. If you were to simply abstract the validation rules, that's perfectly fine, e.g.:
&& HasLicense(car, driver)
&& HasMinimumLicenseExperience(driver, 3);
By abstracting the validation rule into separate methods, you are able to separately define the concrete validation evaluation, while retaining the readability in this orchestrated method.
Note that I used submethods here for abstraction. That's not the only solution. E.g. you could use validation rule objects, and you could even make these accessible via a base type so that you can dynamically compose the specific validation rules to be followed. How complex you make this abstraction depends on how much benefit it gets you from doing so.
I can't give you a concrete right answer, because the right answer depends on context. Depending on factors such as future extensibility, variability of configuration (e.g. different years of experience required for different licences), scope of the application, size of the development team, ... the choices for what level of abstraction is warranted change.
But I would advise against justifying your approach using arbitrary preconditions. For example, I have no idea why "not drunk and having a license" is grouped together as a precondition, especially if you have two license-related validation rules and yet only include one of them as a precondition.
This suggests to me that your approach is arbitrary, and "arbitrary" tends not to align with "conventionally agreed", which is the basis for readability.