48

I am in a predicament. I am working in a repository that makes a lot of database changes within its functions.

The function I am dealing with returns responseIds (but from a transformation, not from any database action). However, as a side effect, it adds an object containing those responseIds to the database.

So should I name it:

  • getResponseIds: This highlights the return values. It is a very functional way of thinking, but obviously if I have function postToDB it makes no sense to use getStatusOfPost
  • addResponseIdToDB: This highlights the side effect, although I truly think that many of my functions just operate on the database (and tend not to return anything)
  • getAndAddResponseIdsToDB: Very informative, but very long.

What are the pros and cons on the suggestions above? Or can you make a better suggestion yourself?

blunova
  • 388
  • 1
  • 4
  • 16
tintinthong
  • 577
  • 1
  • 4
  • 7
  • 3
    How about `persistResponseIds`? – Kain0_0 Jan 24 '20 at 04:30
  • 5
    You might be suprised how far the functional crowd has gotten with the equivalent of `getStatusOfPost` :) – Luaan Jan 24 '20 at 12:45
  • getAndSaveResponseIds – gnasher729 Jan 24 '20 at 15:17
  • `responseIds = createResponseIds()` seems clear and concise. You `get()` something that didn't exist before, so `create` is the appropriate verb. The newly created stuff is what you'd expect a `create()`function to return. Or maybe I'm missing something obvious? – Bass Jan 25 '20 at 11:52
  • it's **completely normal** to have "and" in function names. Disturbingly, @tintinhong, you mention it is "getting long". Function names *should be extremely long*, they should be *sentences*, not *words*. In the specific example I'd call it `getResponsesButAlsoAddCacheOfTheResponsesToTheDb`. Never, ever, ever try to have short function names. Code should be, must be, *self-commenting*. Function names are *sentences*, not words. (Need look no further than Apple's codebase for a nice example of such.) – Fattie Jan 25 '20 at 14:23
  • Unfortunately 70% of the discussion on this page has drifted off the actual question, to, software chat about the details of the example that happened to be given. – Fattie Jan 25 '20 at 14:30
  • 5
    @fattie I agree that code should be self-commenting. Comments should not tell you what the code does. Comments should tell you why the code exists. If refactoring the code means refactoring it's comments you have bad comments. I agree that names can be long full sentences. A good name should keep looking inside a function from surprising you. It shouldn't tell you how to implement it. It should tell you what the client is expecting. Do that and you have a good worthwhile abstraction. – candied_orange Jan 25 '20 at 14:46
  • IMO you are totally correct, @candied_orange – Fattie Jan 25 '20 at 18:30

8 Answers8

56

A function name that contains and is at the wrong level of abstraction.

I lean towards addResponseIdToDB() because otherwise the ‘side effect’ is a complete surprise. However:

responseIds = addResponseIdToDB();

doesn’t leave anyone surprised.

The command query responsibility segregation principle argues that this should not be the only way to get the responseId object. There should also be a query that does not change the DB that can get this object.

Contrary to Bertrand Meyer, I don't believe this means the add has to return void. Just means an equivalent pure query should exist and be easy to find so the DB doesn't get needlessly abused by use of state changing queries.

Given that getResponseIds() should exist and not talk to the database, the best name for a method that does both is actually addToDB(getResponseId()). But that's just if you want to get all functional composition about it.

candied_orange
  • 102,279
  • 24
  • 197
  • 315
  • 41
    Adding to what has been said, but adding a word that hasn't been used, I would say that "and" is appropriate when a function does something that is **atomic**. If there is a benefit to be gained from two things happening atomically/together/at once, then being explicit with what those two things are in the function name is good. So I agree with your first statement in general, but disagree without any qualifications. – J. Dimeo Jan 24 '20 at 01:52
  • 2
    @J.Dimeo you’re reminding me of testAndSet() which I’ll argue is still at the wrong level of abstraction. Unfortunately I can’t think of a good single word to use instead. But if it existed I’d wager it would be better. I just don’t like lower level details in the name. – candied_orange Jan 24 '20 at 03:52
  • 11
    "*...doesn’t leave anyone surprised.*". I disagree, I'm left surprised that a boolean variable is called `responseIds` and I then have to do a mental shift to realise that it adds and gets all in one. But I disagree with @tintintong that `getAndAddResponseIdsToDB` is too long; it's around half the length of a great many of my function names. If a function does two things, then say so in its name. – David Arno Jan 24 '20 at 07:44
  • 2
    I'd just call `testAndSet`, `trySet` BTW. – David Arno Jan 24 '20 at 07:45
  • 3
    @DavidArno What about `incrementAndGet` or `getAndIncrement`? Sometimes you just can't come up with a both simple and descriptive name, but it's still a single operation. – Malcolm Jan 24 '20 at 10:06
  • 2
    @Malcolm, `incrementAndGet`? Do you mean `+=`? – David Arno Jan 24 '20 at 11:26
  • 2
    Just like @DavidArno, I'm left surprised by this code. What is the type of `responseIds`? Why is the left part of the assignment plural, indicating some kind of collection, while the right part is singular? From something called `addResponseIdToDB`, I'd except either a boolean telling me if the ID has been added, or some integer telling me which ID has been added. – Eric Duminil Jan 24 '20 at 12:24
  • @DavidArno I mean [this](https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/util/concurrent/atomic/AtomicInteger.html#incrementAndGet()). – Malcolm Jan 24 '20 at 12:47
  • 2
    A decent rephrasing of CQS is "Asking a question shouldn't change the answer". "What is current last ID?" is a question. "Give me a new ID" is not, even though it has a result (after all, every single command _must_ have a result - e.g. "foreign key violation" :)). – Luaan Jan 24 '20 at 12:47
  • @Malcolm It's called just `Increment` in .NET - which nicely fits the same logic of "commands can return values, but queries shouldn't change the state". It's a bit silly that Java uses `incrementAndGet` instead of just `increment`, but still uses `compareAndExchange`, which also returns a value :) – Luaan Jan 24 '20 at 12:49
  • 7
    @Luaan OK, what are you going to rename `getAndIncrement` to then? `otherIncrement`? – Malcolm Jan 24 '20 at 13:12
  • @Luaan I’m not arguing that every commands result must be returned. Results can be sent out other ways as well. I’m arguing that a command that returns a result can still be a command. Trouble is only caused when that command is the only way to get that result returned. CQS demands you provide a equivalent query that doesn’t change state. – candied_orange Jan 24 '20 at 13:41
  • 7
    @Luaan So which value does `Increment` return, the value pre-increment or post-increment? It's not clear which it would be without diving into docs. I think `incrementAndGet` and `getAndIncrement` are *far better* method names, which I believe makes a good case for "AND" in method names, at least in the specific case of methods which have side effects which could impact the return value.. – xtratic Jan 24 '20 at 13:49
  • 1
    @xtratic I didn't realize there's two, the naming makes sense that way. But it's really completely pointless to have two. `Increment` returns the incremented value, but it doesn't really matter (it just follows the common expected use; would you really expect an `Increment` method to do otherwise? Why should `newId` return the _old_ id?) - you can get the pre-increment just by decrementing the value. Returning the pre-increment would make it more consistent with `Exchange` and `CompareExchange`, and pre-increments are certainly more common in code (though IMO for all the wrong reasons). – Luaan Jan 24 '20 at 14:45
  • 2
    @Luaan In update methods, returning the old value is actually quite common and I would expect it. In fact the more common increment operator `x++` returns the pre-increment value. See also `put` methods of maps, which return the previous value for that key. *(I'll admit, though, that I'm coming from a Java frame of mind with this)* – xtratic Jan 24 '20 at 14:54
  • @xtratic You're saying what I already said. You return the thing you can't get otherwise. – Luaan Jan 24 '20 at 14:55
  • 1
    @Luaan I think the distinction is important for async calls. With race conditions, the difference between both will not always be 1. Only when you're actively looking for bugs :) – Eric Duminil Jan 24 '20 at 14:56
  • 6
    @Luaan When you say "Increment returns the incremented value" you mean the pre-increment value? Gotcha, misunderstood you at first.. *which is actually a great example for why `getAndIncrement` and `incrementAndGet` are better names than just `increment`, even when considered individually* – xtratic Jan 24 '20 at 15:03
  • 3
    @xtratic This is why I would think it couldn't hurt to be just a bit more expressive - while post-increment is more common in languages like Java, pre-increment is [often](https://stackoverflow.com/questions/4706199/post-increment-and-pre-increment-within-a-for-loop-produce-same-output#comment44540825_4706225) [used](https://stackoverflow.com/questions/30036749/is-it-still-better-to-prefer-pre-increment-over-post-increment) more frequently in languages like C and C++. (this is basically the point you made in your edited comment [:)](https://xkcd.com/541/)) – Salem Jan 24 '20 at 15:04
  • 1
    @Moira Exactly, and I just realized that my misunderstanding of what Luaan was saying actually ended up supporting my, and your, opinion of the explicit names being better, *and not* just *for async calls @ Eric Duminil* – xtratic Jan 24 '20 at 15:06
  • "Asking a question shouldn't change the answer" sounds totally reasonable. I find it amusing that quantum theory isn't reasonable at all, and [asking a question always changes the answer](https://en.wikipedia.org/wiki/Uncertainty_principle). – Eric Duminil Jan 24 '20 at 15:49
  • @xtratic No, it's the post-increment value that's returned. But I already said that the pre-increment would be more consistent with the other methods. It even obviously makes sense for the others. And due to what's essentially historical accident, `i++` is the more commonly used increment in C-like languages (iterating through arrays using an index variable that you have to manually increment is just ridiculous, and having to use `i++` instead of `++i` is also just from the C-like convention of having the increment happen _before_ the body of the loop, also ridiculous). – Luaan Jan 24 '20 at 15:58
  • @EricDuminil Actually, no. The whole point of atomic increments is that they are exactly the next value after the original value. If you have two hundred atomic increments at the same time, it must be true for each of them that `getAndIncrement() + 1 == incrementAndGet()` (of course if you run this pseudo-code in Java, it would not be true, but I hope you understand what I'm trying to say). The difference is for _non-atomic_ increment, where you'd get completely wrong results for each of the increments :) – Luaan Jan 24 '20 at 16:01
  • 1
    @EricDuminil That's a bit of a (common) misunderstanding of quantum theory (and of course, is true even in classical mechanics, just less obvious). Asking the question doesn't change the answer, in a manner of speaking. It's just that asking a question is _impossible_ - you only have commands :) – Luaan Jan 24 '20 at 16:03
  • 1
    @Luaan I'm not quite sure what you mean regarding pre-/post-increment and loops; as far as I'm concerned the result of the increment should be irrelevant, and is evaluated _after_ each iteration...? (additionally, AFAIK, `i++` is not always more common in certain C-family languages — in C itself, for example.) – Salem Jan 24 '20 at 16:05
  • @Luaan: I'll think about what you wrote about getAndIncrement vs incrementAndGet. I agree with your clarification that asking a question is impossible in quantum theory, and that's also the way I understand the uncertainty principle. – Eric Duminil Jan 24 '20 at 16:12
  • 1
    @Luaan In Java the only thing consistent with other operations is to have both `getAndIncrement` and `incrementAndGet`. Other operations also come in two varieties, and this is especially useful when you make a `getAndUpdate` operation, which takes a function, and thus you can't easily get the previous value because you may not have a reverse operation. There are valid use cases for having both versions, they are being added for a reason. – Malcolm Jan 24 '20 at 16:21
  • @Malcolm I wouldn't argue that only one is needed. Rather I'd argue that `preIncrement()` and `postIncrement()` are slightly better names. I still don't like them though because even with `and` removed they are focused on how the functions work and not on what the client needed them for. Hard to pick better names though without envisioning a made up client. I'd argue that if the client only needed a `next()` the client likely wouldn't care if you implement it with a pre or post increment and would be glad to not know. – candied_orange Jan 24 '20 at 20:58
  • @candied_orange `preUpdate`, `preAccumulate`, `postSet`... I don't know about you, but I wouldn't have understood that `postSet` is actually `setAndGet` for certain without reading the docs. To me it's not better, but in fact much worse. – Malcolm Jan 24 '20 at 23:19
  • 1
    @Malcolm functions names that tell me every step of what the function does make me wonder why I need the function. Give me good abstractions or get out of my way. – candied_orange Jan 24 '20 at 23:41
  • 1
    @Luaan: A good atomic library should have separate functions for `Decrement`, `DecrementAndGet`, and `DecrementAndTestBecameZero`. Although one could use `DecrementAndGet` for all purposes, on some platforms there may be substantial differences in cost among those different operations. Even older versions of the x86 instruction set, for example, one could simply use `lock dec` if all one needed to know about the value is whether it became zero, but trying to atomically perform the decrement and retrieve the value is much harder. – supercat Jan 25 '20 at 08:33
  • Unfortunately I have to say this answer is just horrible. The first sentence is completely wrong (the "level of abstraction" is wholly unrelated to the issue at hand, and the concept is used incorrectly anyway). Unfortunately the rest goes downhill from there (atomic operations are the most common thing in computing; the various points posited here really make little sense either alone or in relation to the question). Unforunately. – Fattie Jan 25 '20 at 14:28
  • @candied_orange What are you going to build abstractions with? There are much higher-level tools in Java, of course, but you need primitives to build them in the first place, and this is one of them. Also sometimes, although rarely, abstractions are not good enough for your use case, and you have to use the primitives yourself. – Malcolm Jan 25 '20 at 18:46
  • 1
    @Malcolm primitives are also abstractions. Even booleans. I build abstractions from abstractions. What I don't do is turn the abstractions name into a todo list. The name needs to have meaning in the context it is used. Not the context the function contains. The only excuse to do less is if you simply can't think of such a name. Be careful when it comes to settling for less because bad names force people to look inside over and over to understand the code making them jump around until they wish you'd just write 100 line functions instead. – candied_orange Jan 25 '20 at 19:02
  • @candied_orange So I take it you agreed with what I said? Because I don't see any contradictions. – Malcolm Jan 25 '20 at 23:44
16

As others have mentioned, using and in a function name, automatically implies that a function is doing at least two things, which usually indicates that it's doing too much or is doing something at the wrong place.

Using the and clause in a function name however, in my experience, can make sense when there are some steps in a certain flow that need to be performed in sequence or when the whole computation becomes meaningful.

In numerical computing this can make a lot of sense:

normalizeAndInvert(Matrix m)

I mean, who knows, right? If you need to compute some... say, lighting trajectories in computer graphics and at a certain stage you need to normalize and invert a certain matrix, and you find yourself constantly writing:

m = normalize(m), followed by invert(m), just as a simple example, introducing the abstraction of normalizing and inverting, can be good from a readability perspective.

Speaking semantically, stuff like divideAndConquer() etc, is probably discouraged to be explicitly written out, but, well, the And there is basically necessary.

Bruno Oliveira
  • 503
  • 3
  • 4
  • Functions regularly do more than one thing, but those should be little things add up to one big thing that's meaningful at a higher level. `normalizeAndInvert` could be `computeLighting` and `divideAndConquer` could be `applyMachiavelli` – Robin Bennett Jan 24 '20 at 14:12
  • Obviously :) Just stating that first creating a function with an "and" in the name might come as a natural abstraction based on a particular context. Refactoring via the "Rename method" should come immediately after ;) – Bruno Oliveira Jan 24 '20 at 15:07
  • @BrunoOliveira Except sometimes the two steps are themselves a component of many things, rename isn't the right approach. And should be rare but never using it means you're repeating yourself. – Loren Pechtel Jan 26 '20 at 19:56
  • @RobinBennett *" `normalizeAndInvert` could be `computeLighting`"* Not if you need to normalize and invert outside the context of computing the lighting. – Mehdi Charife Jul 05 '23 at 14:35
  • @MehdiCharife then you should try to come up with a phrase that describes what you achieve, rather than just describing the contents of the function. – Robin Bennett Jul 05 '23 at 16:34
  • @RobinBennett Isn't `normalizeAndInvert` descriptive of what you want to achieve when you want to normalize and invert? – Mehdi Charife Jul 05 '23 at 17:37
9

I would say it's fine in general, but isn't acceptable in all cases.

For example, one could argue against and in a function name based on the single responsibility principle aka the S in SOLID - because the and could imply multiple responsibilities. If the and really does mean the function is doing two things, then you probably want to think carefully about what's happening.

An example of a library that utilizes and in function names can be found in Java's concurrency and here and is actually a very important part of what's going on, and closely matches what you describe in your post where the state is changed and state is returned. So clearly there are some people (and some use cases) where it's deemed acceptable.

Shaz
  • 2,614
  • 1
  • 12
  • 14
  • "*the `and` could imply multiple responsibilities*". And in this case it does indeed indicate that. The function gets some response IDs from somewhere, writes them to a database *and* returns them. The need for that "and" should indeed at least raise the idea with the OP that two functions are needed: one to get the IDs and one to write them to the database. – David Arno Jan 24 '20 at 08:55
  • 2
    While I agree that the `and` is a code smell, the basic behavior seems legit: It is generally not a bad idea to return some additional value(s) from a method that happen to be necessary (intermediate) results of carrying out its main function. If the main function of this method is to add the response IDs to the database, additionally returning the IDs it has added to the caller is just a neat, effortless usability feature of the method. – cmaster - reinstate monica Jan 24 '20 at 09:32
  • 1
    I don't think this necessarily violates SRP. You will always need functions that do multiple things. The important part is to have these functions call another function for every thing they want to do instead of containing the required code directly. – Tim Pohlmann Jan 24 '20 at 11:53
  • 1
    If the function does two things then the name should reflect it. – gnasher729 Jan 24 '20 at 15:18
4

Indeed, that's a difficult question to answer as numerous comments can attest. People seem to have contradictory opinions and advice on what is a good name.

I'd like to add my 2 cents to this 2-month old thread because I think I can add more colors to what was already suggested.

Naming is a process

All of this reminds me of the excellent 6 steps guide: Naming as a process.

A poor function name is one that's surprising and you realize you can't trust. Good naming is hard to get right at first shot. It becomes easier with experience.

6 iterative steps to build a good name

  1. Replace surprising name with obvious nonsense like appleSauce(). Sounds silly, but it's temporary and it make it obvious that the name can't be trusted.
  2. Get to an honest name, based on what you understand from what the function does. Say you didn't understood yet the insertion in DB part, getResponseIdsAndProbablyUseDb would be an option.
  3. Get to completely honest, so the function name says everything the function does (getAndAddResponseIdsToDB or getResponsesButAlsoAddCacheOfTheResponsesToTheDb from @Fattie are great examples)
  4. Get to "does the right thing" which is basically the part where you split your function along the "AND". So you actually have getResponseIds and addResponseIdsToDb.
  5. Get to an "Intention Revealing" name which solves the point of "I always want to get the response ID I inserted in DB after I do so". Stop thinking about the implementation detail and build an abstraction that uses the 2 smallest functions to build something else. This is the higher-abstraction level mentioned by @candied_orange. For example createResponseIds could do it and will be the composition of getResponseIds and addResponseIdsToDb.
  6. Get to your Domain abstraction. This is hard, it depends on your business. It takes time listening to your business language to get right. Eventually, you may end up with the concept of a Response and Response.createIds() would make sense. Or maybe ResponseId is something valuable and you would have a factory to create many. Insertion in DB would be an implementation detail of a Repository, etc. Yes, the design would be more abstract. It would be richer and let you express more. No-one outside of your team can tell you what the correct Domain abstraction should be in your situation. It depends.

In your case, you've already figured out 1 and 2, so you should probably at least go to 3 (use "AND") or further. But going further is not just a question of naming, you'd actually split the responsibilities.

Thus, the different suggestions here are valid

Essentially:

  • Yes, surprising function names are terrible and no-one wants to deal with that
  • Yes, "AND" is acceptable in a function name because it's better than a misleading name
  • Yes, the level of abstraction is a related concept and it matters

You don't have to go for stage 6 right away, it's fine to stay at stage 2, 3 or 4 until you know better!


I hope that would be helpful in giving a different perspective on what looks like contradictory advise. I do believe that everyone aims for the same goal (non-surprising names) but stop at different stages, based on their own experience.

Happy to answer if you have any questions :)

nicoespeon
  • 554
  • 3
  • 3
2

Your function does two things:

  1. Gets a set of IDs via a transform and returns them to the caller,
  2. Writes that set of IDs to a database.

Remember the single responsibility principle here: you should aim for a function to have one responsibility, not two. You have two responsibilities, so have two functions:

getResponseIds - Gets a set of IDs via a transform and returns them to the caller
addResponseIdToDB - Takes a set of IDs and writes them to a database.

And the whole issue of what to call a single function with multiple responsibilities goes away and the temptation to put and in the function names goes away too.

As an added bonus, because the same function is no longer responsible for two unrelated actions, getResponseIds could be moved out of your repo code (where it doesn't belong as it's not doing any DB-related activity), into a separate business-level class/module etc.

David Arno
  • 38,972
  • 9
  • 88
  • 121
  • 4
    That's surely true, but you find yourself repeating a snippet using these two SRP methods many times, then you probably should write a trivial method doing this so you DRY, shouldn't you? – maaartinus Jan 24 '20 at 10:47
  • 1
    @maaartinus, if you find yourself calling these two methods in many places, then you likely have a problem with a lack of cohesion in your code. Creating a "DRY" function then just masks this lack of cohesion. – David Arno Jan 24 '20 at 11:24
  • 1
    In that situation, you may have just followed the “single responsibility principle” too blindly. And creating and storing the ids _is_ the single responsibility of the function. – gnasher729 Jan 06 '22 at 19:18
1

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>).
Cort Ammon
  • 10,840
  • 3
  • 23
  • 32
  • For people who don't know the maths: Both 1/x and sqrt(x) are rather slow functions. Using some clever maths, we can calculate 1 / sqrt(x) _faster_ than either a division or a square root. Actually so much faster that the fastest way to calculate sqrt(x) is to calculate 1 / sqrt(x) with a clever implementation, and multiplying the result by x. – gnasher729 Apr 14 '20 at 22:43
  • Like in physics, where the elementary units are not meter and second anymore, but second and speed of light, because we can measure the speed of light with more precision than the length of a meter. – gnasher729 Apr 14 '20 at 22:44
0

What is the ultimate intent of this method? Why add these transformed ids to the DB, is it for the purpose of caching? I'll work under that assumption.

It sounds like the intent of the method is really just to get the transformed response ids (either doing the transform or getting them from a cache), and so there's your method name:

getTransformedResponseIds or less verbosely getResponseIds()

A method with this name could cache or maybe not, and that can be documented, but your method name shouldn't tie you to a specific implementation; What if you decide to stop using a DB and instead cache them elsewhere such as just temporarily in memory?

Side effects should be just that, side effects, they should probably be documented but they shouldn't really impact the core intent (or success) or name of the function. If getting the value from the cache fails (or if you configure to skip caching) that shouldn't be a critical issue, the method should just transparently re-calculate, cache (or not), and return the new value.

Sometimes using an "AND" is helpful to convey intent such as with the Java methods getAndIncrement and incrementAndGet so that's certainly not off the table.

xtratic
  • 456
  • 2
  • 11
-2

You say that the function returns something "from a transformation, not from any db action".

This suggests that the function behaves more like a constructor than a getter. However, constructors also shouldn't cause side-effects (thanks for the link, @MechMK1).

Factory methods, on the other hand, already presuppose some level of messiness. For example, one of the motivations for using factory methods is that a factory method:

Allows for more readable code in cases where multiple constructors exist, each for a different reason. - wikipedia

Likewise,

If you need to do some work with side effects, a factory method can be named in such a way that it's more clear what those side effects are. - ruakh

Consider making the function a factory method, using the term "logged" to alert programmers to the database storage:

  • Declaration: createLoggedResponseid(...) {...} // log object to db
  • Call: responseid = createLoggedResponseid(...);
Lawrence
  • 637
  • 3
  • 10
  • [Constructors should not cause side-effects](http://blog.millermedeiros.com/constructors-should-not-cause-side-effects/) – MechMK1 Jan 24 '20 at 14:03
  • @MechMK1 I've edited to remove the *constructor* suggestion, and to provide more support for an appropriately-named factory method. Let me know what you think. – Lawrence Jan 24 '20 at 14:28