The code on the left is more readable. It is easy to follow and see what is happening at each step.
That being said, the code on the right is easier to maintain. If the requirement comes down the pipe that we now need to support a new pizza topping it is easy to jump to the add toppings method using the IDE instead of scanning the entire monolith procedural function. Or if we now need to add support for take-and-bake, we can just modify the small entry method.
This also assumes that we are not needing to reuse any of these methods. If you want to add toppings to a sandwich or a salad, better write another huge method like the one on the left, or add a ton of nested if/else or switch statements. If you use the style on the right you can reuse the add toppings method without worrying about if you should preheat the oven for a salad.
The author chose a very simplistic requirement for an example and it is all well and good until you let it fester in a code base for ten years, with multiple interns maintaining it and end up with a huge spaghetti code monster to deal with.
That being said, the code on the right is easier to maintain
That depends if you guessed how it is going to change correctly. All too often you don’t and some weird requirement comes in that your abstraction does not account for and makes the whole thing really awkward. Which tends to lead to spaghetti abstractions that are just as bad to maintain as spaghetti code.
For the context of the code he has given the code on the left is easier to maintain. At least at the start. Once it becomes more complex and more requirements are added and start to make it less maintainable that is the point abstractions should be added. Linear code is far easier to find and add abstractions to that already highly abstracted code.
The problem most of these examples and counter examples make is only showing simple code and assuming that you always want to apply the patterns of abstracting things or not. In both cases the code becomes a mess over time as too many abstractions are equally as bad as not enough abstractions. And where those lines are depends on the code you have in front of you. There are no good rules out there ATM for all cases. Just some that sometimes work, and others that work in different situations. Better to learn all of them and when they are most useful to apply. And don’t be afraid to rip out some bad abstraction (that may have once made sense).
Personally I would do something in the middle of both those solutions. Why is the oven preheat not a method on the oven object? Makes the overall method simpler and still linear. No need to abstract every part of the function into methods, but some do many good candidates for that.
The code on the left is more readable. It is easy to follow and see what is happening at each step.
That being said, the code on the right is easier to maintain. If the requirement comes down the pipe that we now need to support a new pizza topping it is easy to jump to the add toppings method using the IDE instead of scanning the entire monolith procedural function. Or if we now need to add support for take-and-bake, we can just modify the small entry method.
This also assumes that we are not needing to reuse any of these methods. If you want to add toppings to a sandwich or a salad, better write another huge method like the one on the left, or add a ton of nested if/else or switch statements. If you use the style on the right you can reuse the add toppings method without worrying about if you should preheat the oven for a salad.
The author chose a very simplistic requirement for an example and it is all well and good until you let it fester in a code base for ten years, with multiple interns maintaining it and end up with a huge spaghetti code monster to deal with.
That depends if you guessed how it is going to change correctly. All too often you don’t and some weird requirement comes in that your abstraction does not account for and makes the whole thing really awkward. Which tends to lead to spaghetti abstractions that are just as bad to maintain as spaghetti code.
For the context of the code he has given the code on the left is easier to maintain. At least at the start. Once it becomes more complex and more requirements are added and start to make it less maintainable that is the point abstractions should be added. Linear code is far easier to find and add abstractions to that already highly abstracted code.
The problem most of these examples and counter examples make is only showing simple code and assuming that you always want to apply the patterns of abstracting things or not. In both cases the code becomes a mess over time as too many abstractions are equally as bad as not enough abstractions. And where those lines are depends on the code you have in front of you. There are no good rules out there ATM for all cases. Just some that sometimes work, and others that work in different situations. Better to learn all of them and when they are most useful to apply. And don’t be afraid to rip out some bad abstraction (that may have once made sense).
Personally I would do something in the middle of both those solutions. Why is the oven preheat not a method on the oven object? Makes the overall method simpler and still linear. No need to abstract every part of the function into methods, but some do many good candidates for that.
The right is also easier to write tests for, which is crucially important to me.
Do not solve maintenance problems that you don’t face.
On the other hand, not anticipating and preparing for eventualities can be just as bad.
Nothing quite so permanent as a temporary solution, especially those difficult to maintain.
Preventing is better than suffering through high technical debt after a mistake (but don’t get too paranoid, either)