Now the reason I posted that inlining article from Carmack leans heavily into this line of advice: "if a function is only called from a single place, consider inlining it."
Taking a complicated mess and breaking it into functions that are called from a single place seems better served by organising the code (maybe with duplication if needed) and writing inline comments rather than devoting a call tree to that section of code.
Sometimes. Other times it means you need to jump around in a file (or jump between files, even) to understand what's going on.
Totally agree with this interpretation and why I wrote a recent article about it: https://bower.sh/anti-pattern
The use cases will likely diverge in the future, and if the functions are DRY'd making changes will make introducing bugs from the calling code that you're not working on easy. Eventually the single function will likely have a lot of conditions in it, which is a red flag for this situation.
I've had bad experiences with the single responsibility principle. It sounds kind of right, but in practice "responsibility" is too vague and often surprisingly hard to agree on what is e.g. one responsibility vs. three responsibilities.
By contrast, loose coupling is more objective and can (at least in theory) be measured.
I use what I've come to call the "Rule of Three" here.
The first time, don't even consider any kind of abstraction. Just do the thing.
The second time, don't abstract yet. Repeat yourself, but do it in a way that will scale and make abstraction possible, while mentally noting how you might abstract it.
The third time, abstract it.
Adhering to this, the vast majority of code will never reach that third stage, thus taming the complexity beast.
It's similar to my own rule about when to add a tool. I wait until I'm doing something else and I feel the lack of that specific tool. The first time, it's on my radar. The second time, it's time to get the tool. If you get it on first impulse, you'll drown in tools (and won't give the tools you have a fair shake). If you wait too long, you'll acclimate to not using the proper tool.
I outsource a lot of decisions to that feeling, so I can focus on other things.
It's a matter of time anyway when it's really easy to ask ChatGPT to refactor everything into this perfect code.
yeah, because you probably forgot about at least one of the other repetitions somewhere in the code
as a developer you should know if it makes sense or not
Good code rhymes! It's better to err on the side of less abstraction and create patterns that will become familiar to the next person working with your code.
Reading code is harder than writing it and most of the time coding is about communicating with humans or "ghosts", i.e. people you might never meet, people who worked on the code in a different context or "era".
https://sonnet.io/posts/code-sober-debug-drunk/
https://sonnet.io/posts/emotive-conjugation/#:~:text=Ghost%2...
When I think of the most difficult to understand code I've come across it was probably written by someone who lives and breathes that interpretation of DRY.
But it doesn't end with code comprehension. Extreme abstraction and countless files and components also lead to buggy and difficult to maintain code.
It's easy to lose understanding of branches and business flow when abstraction exists in the extreme.