Having a composite function name is acceptable, so which naming scheme you use depends on your context. There are purists who will tell you to break it up, but I will argue otherwise.
There are two reasons to try to avoid such things:
- Purity - A function which does two things should be converted to two functions which do one thing each.
- Lexical size - a
bigUglyCompositeFunctionName
gets hard to read.
The purity argument is typically the one that is focused on. As a vague general heuristic, breaking up functions is a good thing. It helps compartmentalize what is going on, making it easier to understand. You are less likely to do "clever" tricks with sharing of variables which lead to coupling between the two behaviors.
So the thing to ask yourself is "should I do it anyways?" I say breaking things up is a vague heuristic, so you really only need a decent argument to go against it.
One major reason is that its beneficial to think of the two operations as one. The end-all-ultimate example of this is found in atomic operations: compare_and_set
. compare_and_set
is a fundamental cornerstone of atomics (which are a way of doing multithreading without locks) which is successful enough that many are willing to think of it as the cornerstone. There's an "and" in that name. There's a very good reason for this. The entire point of this operation is that it does the comparison and the setting as one indivisible operation. If you broke it up into compare()
and set()
, you'd defeat the entire reason the function existed in the first place, because two compares()
could occur back to back before the set()
s.
We also see this in performance. My favorite example is fastInvSqrt. This is a famous algorithm from Quake which calculates 1/sqrt(x). We combine the "inverse" and "square root" operations because doing so gives us a massive performance boost. They do a Newton's approximation which does both operations in a single step (and happens to do it in integer math rather than floating point, which mattered in the era). If you were to do inverse(sqrt(x))
, it would be much slower!
There are some times where the result is more clear. I've written several APIs involving threading where one has to be dreadfully careful about things like deadlocks. I didn't want my user to be aware of the internal implementation details (especially since I might change them), so I wrote the API with a few "and" functions which prevented the user from ever having to hold a lock for more than one function call. This means they never needed to know how I was handling the multithreaded synchronization under the hood. In fact, most of my users weren't even aware they were in a multithreaded environment!
So while the general rule is to break things up, there can always be a reason to couple them together. For example, can your user break your architecture by adding to a database multiple times without the "gets" inbetween? If each of the responses logged in the DB needs to have a unique identifier, you could get in trouble if the user got to do them willy nilly. Likewise, would you ever want to let a user "get" without logging the results in the DB? If the answer is "yes" then break them up so that the user can access the "get" function. However, if the security of your application is broken if the user can "get" without the result being logged in the DB, you really should keep the functions together.
Now as for the lexical issues, your function name should describe what the user needs to know about it. Let's start with the "get" part. Its pretty easy to remove the "get" from a function name, because all of your examples will show an assignment: int id = addResponseIdToDB()
". In all places where the function is used, you will end up documenting the fact that the function returned a value.
Likewise "add" can be optional. We use the term "side effect" as an all encompassing term, but there's different shades of it. If the DB entries are merely a log, there may be no reason to highlight it. We never see functions like playMusicAndSendPersonalInformationToOurCorporateServers
. It's just playMusic
, and if you're lucky, the documentation may mention something about a socket connection over the internet. On the other hand, if users are expected to call this function for the purpose of adding things to the DB, then "add" is essential. Don't take it out.
Now, I've written a lot of reasons to do every possible combination of what you are asking. I've intentionally not answered the question because there's a choice to be made. It's not a rule to follow. You're crafting an API.
That being said, my instinct is that addResponseIdToDB is likely to be the best answer. In most cases the "get" portion is an obvious enough side effect that it doesn't earn the extra noise caused by typing it everywhere. However, there's a few places where I might consider it important:
- If the "get" is expensive -- If you have to do a "get" that involves fetching something over the internet and then adding it to a local cache DB, by all means the "get" is important. It is the thing the user is trying to do.
- If you need to make it clear that the user needs to pay attention to the value. If the user wants to use the variable, they'll realize the API returns it, and they'll use it. However, you want to watch out for the user that doesn't know they need it. For example, if you have to "close" this operation by ID later to free up memory, you may want to draw attention to the fact that you're doing something. In these cases, you might want to look at a verb other than "get." "get" often implies idempotent functions (calling it again doesn't do anything). If it returns resources, other verbs like "create" or "acquire" are nice. This particular failure mechanism is a major issue in C when doing exception handling. C lacks the catch/throw mechanism of C++, so it relies on return codes. Developers are famous for simply failing to check these return codes and getting into really bad situations like buffer overflows because of it.
- Symmetry -- Sometimes you design an API to have a lexical symmetry to it. You set up the words so that they come in pairs, or other patterns, and craft the commands so that it is easy to visually identify whether the pattern has been followed or not. I'd say this is rare, but its not unheard of. There's a reason closing XML tags repeat the tag name (such as <foo> and </foo>).
persistResponseIds
?getStatusOfPost
:)responseIds = createResponseIds()
seems clear and concise. Youget()
something that didn't exist before, socreate
is the appropriate verb. The newly created stuff is what you'd expect acreate()
function to return. Or maybe I'm missing something obvious?