Rubber-ducking: On Context
I’m torn between extracting auth credentials in the handler from a Go Context and passing them as arguments to service methods, or just passing the context and having the service methods get it from the Context themselves.
Previously, when the auth credentials just had a user ID, we were doing the former. But we’re now using more information about what the user has access to and if we were to continue doing this, we’ll need to pass more parameters through to the service layer. Not only does this make things a little less neater, it’ll mean the next time we do this, we’ll have to do the whole thing again.
But, it means that the service methods would need to get the user IDs themselves, along with this new stuff. Not that that’s an issue: there will be providers that are also using the context to get this info. So this is a viable option. And yet, I feel uneasy about using the context for this.
🦆: So what are your options?
L: I guess I could replace the use of the user ID with a structure that holds both the user ID and this extra stuff.
🦆: Would that work?
L: I mean, I guess it would? It would make it clearer as to who’s request this. It would also mean that we’re being explicit about what the method needs.
🦆: Do you see any downsides with this approach?
L: The only thing I can see is that it would be inconsistent with other parts of the system that are getting it from the context.
🦆: Your hesitating. You don’t seem sure about this.
L: Well, I just don’t like the fact that we’re passing both the context which holds this auth info and the auth info alongside it. And I know that it’s unclear, and would mean that the tests would need to be changed (I mean they’ll need to be changed anyway if we went with this “principal” approach).
🦆: So what’s really going on? Why are you unsure about this?
L: Well, it’s just showing this in a review and having people say “oh, that’s not the right way to write Go.”
🦆: They say that?
L: We’ll, not exactly. But they do have opinions about how best to do this (like pretty much everyone, I guess).
🦆: Do they have an opinion about this decision?
L: Well, not really. In fact, I think they’re pretty okay with either approach.
🦆: So if they’re okay with either approach, it probably doesn’t matter that much. But if it were me, I’d probably prefer something a little more readable.
L: Well, yeah. But how can I trust you? You’re me.
🦆: Am I? I’m a duck. Are you a duck?
L: No, I’m not. But you’re not a duck either. You don’t exist. You’re just a figment of my imagination.
🦆: Really? Then how are you speaking to me?
L: Because I conjured you up so I can work through this problem I’m having.
🦆: So you’re having a go at me because I don’t exist, yet you still need me because you’re stuck on this decision and you need a resolution.
L: Well, I didn’t say I don’t need you. It’s probably still helpful to me to have this conversation.
🦆: Ok, I think we’ve gone off track a little. What are you going to do about this context decision?
…
🦆: Well?
L: Ok, I’m not certain that implicitly including the user ID will work, as the user ID may be different to what is actually in the context. I also don’t like how it’s implicit in the context, and I think I do prefer something a little more readable. It pains me to think that I’ll be effectively duplicating values that are already available to the method. But we’re doing that anyway with the user ID.
So here’s what I’ll do. I’ll replace it with a dedicated type, retrievable from the context and holding all the information that is needed to authorise the user. I’ll also retroactively make those changes to other areas of the code that are doing it.
🦆: Okay. And what of your peers?
L: If they ask about it, I’ll just tell them that I prefer something a little more explicit. I know it’s a departure from how I did things previously. But the benefits outweigh the costs I think in this case.
🦆: Okay. Sounds like you’ve got a way forward now.
L: Great. This has been helpful. Thanks for that, D.
🦆: No worries.