Issue
I've seen a lots of Go codes in my company's codebase, just like the example on Gin's Group routes, use this functionality to grouping the RESTful endpoints by just pulling some common prefix out for the sake of "reusability":
router := gin.Default()
api := router.Group("/api")
api.POST("/login", loginEndpoint)
api.POST("/submit", submitEndpoint)
api.POST("/read", readEndpoint)
Even more, when we defined more RESTful manipulations on a resource, we will more likely to refactor our code like this:
api := router.Group("/api")
// ...
notification := api.Group("/notification")
notification.GET("/register", registerEndpoint)
notification.POST("/token/exchange", exchangeTokenEndpoint)
notification.POST("/token/revoke", revokeEndpoint)
// ...
If you ask yourself (or the junior engineer) why you code like this, probably the answer is: I want to increase the code reusability for trying to make the code is better.
So, what kind of problem behind this code?
Reduce debugging time is more valuable for your life
It is very common to receive a bug description like following when your code has been deployed a few weeks:
"Client side receive an unexpected response like following..., when calling /api/notification/token/revoke
endpoint with some request parameters..."
OK, we have a bug need to fix, and you know it is very possible there is a bug in the handling routine on that endpoint.
Thereafter, you open your IDE (e.g. VSCode) and search by the given endpoint /api/notification/token/revoke
trying to find what handle function actually handling this endpoint.
But, nothing you got from searching results. Because you split your URL path of endpoint into several parts by using the routes grouping functionality.
So you try to search api
or revoke
word-by-word appeared on your codebase to approach the handle function, these journey is not very happy and time-consuming because you can expect that there are many noises on searching results.
Rethink the benefits of code reusability
The benefits of code reuse is not just only thinking about you seem to reduce the development time when you create more and more routes, but should put more considerations on how you and other codebase maintainers would maintain and troubleshoot these codes.
I assume most programmers are more like coding instead of debugging, especially the bugged codes may be written in an unfamiliar coding style to you. (means the codes may be written by someone else or YOU in a few weeks ago.)
No one are happy to debug an unfamiliar codes, because you need to pay more cognitive loading to understand the codes. This activity is very unhappy and painful.
So, how to ease that pain?
Proposed use cases of routes grouping functionality
From my experience, there is no benefits to group the routes by some prefixes or common path, but increasing the debugging cost in the future.
So, if you have no other actual requirements to use grouping functionality, just define routes in the explicitly way:
router := gin.Default()
// ...
router.GET("/api/notification/register", registerEndpoint)
router.POST("/api/notification/token/exchange", exchangeTokenEndpoint)
router.POST("/api/notification/token/revoke", revokeEndpoint)
// ...
But what scenario you should actually consider to use routes grouping functionality? Some endpoints require authentication and some not.
The use case may looks like:
router := gin.Default()
noAuth := router.Group("/")
noAuth.POST("/api/auth/login", loginEndpoint)
auth := router.Group("/", authMiddleware)
auth.GET("/api/notification/register", registerEndpoint)
auth.POST("/api/notification/token/exchange", exchangeTokenEndpoint)
auth.POST("/api/notification/token/revoke", revokeEndpoint)
// ...
Conclusion
- Re-consider when you want to split your route definition into several parts, does it really gain any benefits?
- Put more considerations on how you would maintain your codes, try to find a way which suitable for you to ease the pain when debugging and troubleshooting
Top comments (4)
it doesn't increase debugging cost if you have a standard convention on creating groups, for example:
now you'll know where to debug when the endpoint has an /api/notification prefix
I agree with that your suggestion is a good refactor strategy.
And, to be honest, I also agreee if our team have more discussions on how to organize our routes definition as standard conventions, the debugging/troubleshooting cost may not increase signifinatly when we have a lots of complex routes.
But the thing I want to emphasize is that any small code reuse strategy should be careful to adopt before you have confirmed that benefits of reducing code complexity are truly exists, both development time and maintainence activity.
If you were using a library that was analogous to flask restful (which I have superficial knowledge of), would you still have the "go restful" add flattened routes or using grouping in the library implementation?
I am not sold on this one. The routes module is usually easy to read through and in an obvious location.