adr icon indicating copy to clipboard operation
adr copied to clipboard

[Proposal] Naming

Open 1xX69 opened this issue 3 months ago • 19 comments

Naming

  • State: Draft
  • Replaced by:
  • Created:
  • Tags:

Context

The two primary options are:

  • ActionResource (for example, CreateBook)
  • ResourceAction (for example, BookCreate)

With ResourceAction:

AccountCreate
AccountDelete
AccountGet
AccountSuspend
ApiTokenCreate
ApiTokenRefresh
ApiTokenRevoke

With ActionResource:

CreateAccount
CreateApiToken
DeleteAccount
GetAccount
RefreshApiToken
RevokeApiToken
SuspendAccount

Resolution

...

1xX69 avatar Sep 20 '25 09:09 1xX69

  • Hacksoftware chose^1 the ResourceAction pattern.
  • Google chose^2 the ActionResource pattern.

1xX69 avatar Sep 20 '25 09:09 1xX69

Would this be for GraphQL, gRPC, JSON API? Regardless, my first immediate take is that, after so many years, I prefer to do ResourceAction so that things naturally stick to the proper grouping.

This reminds an issue from 2017 😭 https://github.com/graphql/graphql-spec/issues/376

yordis avatar Sep 20 '25 20:09 yordis

Would this be for GraphQL, gRPC, JSON API? Regardless, my first immediate take is that, after so many years, I prefer to do ResourceAction so that things naturally stick to the proper grouping.

This reminds an issue from 2017 😭 graphql/graphql-spec#376

Yeah, alphabetical sorting is handy for grouping API methods.

We could also use resource-specific services to handle that naturally. For example, resource-specific AdUnitService vs broader domain-based ChatService.

I haven't worked with GraphQL or JSON API, so can't really weigh in there 😭

1xX69 avatar Sep 21 '25 05:09 1xX69

With resource-specific services:

iam.example.com/
├── AccountService/
│   ├── create_account()
│   ├── delete_account()
│   ├── get_account()
│   └── suspend_account()
└── ApiTokenService/
    ├── create_api_token()
    ├── refresh_api_token()
    └── revoke_api_token()

1xX69 avatar Sep 21 '25 07:09 1xX69

This is a tricky subject, before we even get to the naming, we would need to agree on what "Resource" actually means AND after that what "Service" means.

It is not that obvious based on my experience.

Especially when Phyisical Boundaries and Logical Boundaries are ignored.

Lastly, I am trying to avoid creating yet another spec; so we are leaning on Google AIP as much as we can.

yordis avatar Sep 21 '25 18:09 yordis

Sometimes, the names for request messages can become a little weird, for example SearchSearchAds360StreamRequest

1xX69 avatar Sep 22 '25 03:09 1xX69

The messages can also have super-duper-long names, for example VideoActionRecognitionPredictionInstance (this one is used indirectly though, under PredictRequest)

1xX69 avatar Sep 22 '25 03:09 1xX69

I do not like the fact that they aren't following something obvious: https://github.com/googleapis/googleapis/blob/3acfdb6166a7101691cdf812bf8f8a67a2084fde/google/ads/searchads360/v0/services/search_ads360_service.proto#L111-L129

Like [service name][rpc name][Request|Response] that is a very confusing name

yordis avatar Sep 22 '25 03:09 yordis

That's the longest method name I've found -- UpdateSecurityHealthAnalyticsCustomModule

1xX69 avatar Sep 22 '25 04:09 1xX69

I do not like the fact that they aren't following something obvious: https://github.com/googleapis/googleapis/blob/3acfdb6166a7101691cdf812bf8f8a67a2084fde/google/ads/searchads360/v0/services/search_ads360_service.proto#L111-L129

Like [service name][rpc name][Request|Response] that is a very confusing name

Seems relevant: https://github.com/aip-dev/google.aip.dev/issues/1085

1xX69 avatar Sep 22 '25 04:09 1xX69

Should be I feel adding Stream is a good idea, Streaming is long ... I do not see the point.

Some of those long names may be related to what I said before "Physical Boundaries and Logical Boundaries"

"Physically", you may want one port behind 1 service, but logically, that service has many resources ... so, you end up with such long naming.

You could create more "services" but may means multiple ports, sometimes, or other infra level stuff people dont want to deal with.

Assumptions. A lot of assumptions.


What I definitely do not support is doing Create or Update based on the service name, so always include the "resource" in the rpc, Create[x] or Update[x]

yordis avatar Sep 22 '25 04:09 yordis

I propose to standardize the following terminology:

From https://github.com/aip-dev/google.aip.dev/blob/d7c7bdb37ef84c0bce8250a5129b134617b5dfcd/aip/general/0009.md?plain=1#L50:

API Interface

The element of an API specification IDL that groups API methods, such as a Protocol Buffers service definition. It is typically mapped to a similar high level grouping mechanism in most programming languages, like a class or interface.

From https://github.com/aip-dev/google.aip.dev/blob/d7c7bdb37ef84c0bce8250a5129b134617b5dfcd/aip/general/0009.md?plain=1#L76:

API Service

A deployed implementation of one or more APIs, exposed on one or more network addresses, such as the Cloud Pub/Sub API.

From https://github.com/aip-dev/google.aip.dev/blob/d7c7bdb37ef84c0bce8250a5129b134617b5dfcd/aip/general/0009.md?plain=1#L87:

API Service Endpoint

Refers to a network address that an API service uses to handle incoming API requests. One API service may have multiple API service endpoints, such as https://pubsub.googleapis.com and https://content-pubsub.googleapis.com.

Given the new terminology, I was suggesting that developers should be encouraged to create ~resource-specific services~ resource-scoped API interfaces so that things could be grouped naturally.

I'm not confident about this suggestion anymore though because it might bloat the boilerplate code.

Just imagine that you have some APIServiceEndpointGrpcClient. Such struct would have to store a single API Interface per field which might be cumbersome if we have tens or hundreds of other API interfaces sitting alongside.

The broader domain-based ChatService approach has an advantage in this context.

Yeah, we'll have a lot of irrelevant results when typing something like grpc_client.chat_service.c in the text editor but I assume the modern IDEs should be smart enough to convert grpc_client.chat_service.c into grpc_client.chat_service.create_ rather than into grpc_client.chat_service.create_{first_alphabetically_sorted_match} when there's multiple entries in results and all of them start with grpc_client.chat_service.create_

Given that, I think that resource-scoped API interfaces like AdUniService is a bad practice unless proven otherwise and it should be discouraged.

1xX69 avatar Nov 12 '25 08:11 1xX69

"Physically", you may want one port behind 1 service, but logically, that service has many resources ... so, you end up with such long naming.

You may host multiple API Interfaces under single API Service Endpoint which may have only one port. gRPC supports that but I'm not sure about other IDLs you mentioned. Shouldn't we discourage the use of any other IDLs rather than gRPC?

1xX69 avatar Nov 12 '25 08:11 1xX69

I am onboard with almost everything, my only curiosity it is around subdomain vs URL paths,

https://pubsub.googleapis.com and https://content-pubsub.googleapis.com

vs.

https://whatever.googleapis.com/googleapis.pubsub.v1.Service and https://whatever.googleapis.com/googleapis.content-pubsub.v1.Service

yordis avatar Nov 12 '25 19:11 yordis

What exactly are you comparing? I'm not sure if I understand. But it seems like you also have to include the API Interface Method name to make the URI complete.

1xX69 avatar Nov 13 '25 05:11 1xX69

From https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#requests:

Path → ":path" "/" Service-Name "/" {method name} ; But see note below.

Path is case-sensitive. Some gRPC implementations may allow the Path format shown above to be overridden, but this functionality is strongly discouraged. gRPC does not go out of its way to break users that are using this kind of override, but we do not actively support it, and some functionality (e.g., service config support) will not work when the path is not of the form shown above.

1xX69 avatar Nov 13 '25 05:11 1xX69

ooooh nevermind then! nothing burger from my end.

You can move the conversation to a PR, I think we are in alignment here, and ultimate, following Googles' AIP would be a decent route I think

yordis avatar Nov 13 '25 18:11 yordis

I'm a bit hesitant about making a PR just yet. I have the feeling like something is missing.

Yeah, we'll have a lot of irrelevant results when typing something like grpc_client.chat_service.c in the text editor but I assume the modern IDEs should be smart enough to convert grpc_client.chat_service.c into grpc_client.chat_service.create_ rather than into grpc_client.chat_service.create_{first_alphabetically_sorted_match} when there's multiple entries in results and all of them start with grpc_client.chat_service.create_

Given that, I think that resource-scoped API interfaces like AdUniService is a bad practice unless proven otherwise and it should be discouraged.

For example, there's definitely more than a one way to represent automatically generated API Methods. Previously, I was using the pattern {grpc_client}.{api_service_name}.{method_name} but it's also possible to represent the methods as Rust traits which will result in even shorter boilerplate:

mod googleapis_codegen {
  pub struct GrpcClient;

  mod ads {
    mod ad_manager {
      pub trait AdUnitService {
        fn get_ad_unit(&self);
      }

      impl AdUnitService for GrpcClient {
        // sends a request to https://ads.googleapis.com/googleapis.ads.admanager.v1.AdUnitService/get_ad_unit
        fn get_ad_unit(&self) {
          ...
        }
      }
    }
  }
}

use googleapis_codegen::GrpcClient;
use googleapis_codegen::ads::ad_manager::AdUnitService;  // the name of the trait

grpc_client: GrpcClient;
// use the trait implicitly
grpc_client.get_ad_unit(...)
// use the trait explicitly
<grpc_client as AdUnitService>.get_ad_unit(...)

So it might be not reasonable to state that resource-scoped API interfaces is a bad practice. Further research is needed.

1xX69 avatar Nov 14 '25 05:11 1xX69

Do you have any thoughts on codegen for golang/elixir/whatever? Would be super good if we could use something as effective as Rust traits but for different languages.

1xX69 avatar Nov 14 '25 05:11 1xX69

In terms of gRPC, I tent to use the official code-gen, so they would create a directory structure based on the package name

yordis avatar Nov 15 '25 16:11 yordis