This sounds like something ridiculously basic, but have you ever really sat down and thought through what the good reasons are for creating functions, methods, variables, classes, etc? Spending a little time on it will help you clarify when you should and when you shouldn’t create one.
Reasons to create a method:
- To take values, method calls, or series of method calls that are used exactly the same in several places, and put them in One And Only One place, so that any time it is changed, those changes apply anywhere it is used. Also known as Don’t Repeat Yourself.
- To give a name to a particular value, function call, or series of function calls. That name is hopefully more descriptive of what you are doing and why than just those lines by themselves.
- To create a scope which isolates intermediate variables needed for some algorithm, so that
- They cannot be affected by or affect anything else in an unexpected way
- It is clear that those values are not used anywhere else
Number 1 is the clearest and most basic. If you’re doing the same thing in several places, make a function or class or whatever’s appropriate.
Number 2 is tricky in that I’d say that it’s best in small doses. The ultimate goal is to make the code clearer and easier to read. Splitting up methods longer than a screen or so into several sub-methods usually makes it easier to read, but it can go the other way if you keep on splitting and end up with hundreds of functions of 1 or 2 lines. When it’s time to debug something, good luck keeping track of exactly what each one does.
Number 3 seems to be the most controversial one. Say you have some algorithm in the middle of some other function that has gotten big and hairy. You’d like to cut that function down to size and make things clearer, so you break that algorithm out into it’s own function. The trick of this is that, from a perspective of scoping, you’ve both made things better and worse. On the one hand, all of the intermediate variables associated with that algorithm are now hidden away in their own function, where it’s clear that they have no affect on anything outside that algorithm. On the other hand, that algorithm is now available throughout the scope of the containing class, so it isn’t immediately clear if it’s used anywhere other than the original function. This can be mitigated somewhat through judicious use of classes and access modifiers.
It occurred to me that this could use some clarification when trying to debug an issue with Devise authentication in unit tests, where my test session wasn’t authenticating. Specifically, valid_for_params_auth? in authenticatable.rb. Looking at it now, it doesn’t strike me as all that bad, if maybe pushing the idea of Number 2 a bit. When I was trying to debug through it, though, it sure felt like a mess trying to figure out which of the 4 sub-methods, most of which are one line, it calls was returning the wrong result, and why. It doesn’t look like a mess now, but I’d say the feeling was more of a cumulative effect of trying to debug through a huge, complex system that I didn’t understand very well to figure out why something wasn’t working the way I expected.
That might give us a little perspective into how to make our code actually clearer and easier to understand. If you have a chunk of code that grops your foo, does it belong in its own method? It’s always a subjective decision which could change either way over the course of a project. It’s worth considering that, if the method is just one line, maybe GropMyFoo is an impediment to understanding compared to inlining foo.grop(), even if you’re doing it in 2 or 3 places.