▲ 18 ▼ Go style guidelines for packages
Go is about naming and organization as much as everything else in the language. Well-organized Go code is easy to discover, use and read. Well-organized code is as critical as well designed APIs. The location, name, and the structure of your packages are the first elements your users see and interact with.
Register to comment or vote on this story
I don't agree with all of these but it is an important topic, and I think they are better than the other recommendation - Standard Package Layout I've seen on this.
There are a few areas that I'm not sure about here, because the author doesn't explain why she thinks they are a bad idea, and a lot of this is quite subjective, I'm not even sure we need to be so prescriptive about one way to write go code (for example on singulars and plurals, or using src in your paths, why not if you have a good reason for it?). A few examples of areas I disagree with:
I do like the pattern for a package per top-level resource that your app deals with as it makes it easy to find where, particularly when partnered with a restish url scheme with resources under a url which reflects their name. I really don't mind the stutter this causes and don't think it's a big problem, as you rarely need users.User - most of the time you'll be doing users.New() or users.Find(), and never see the possible stutter. I much prefer this approach to lumping all these different models (which are often the most important part of the domain in CRUD apps) into one huge main package (as recommended by the Standard Package Layout link above) - to me that's very similar to just having a models package - the effects are the same, there is no separation between models, and your main package will quickly grow to be unmanageable for any sizeable application.
Overall, a great set of guidelines though.
Do you ever run into issues with cyclical dependencies when doing per-resource packages? That was always my issue - there would occasionally creep up a situation where the two packages referenced one another and it stopped working. As a result, lumping them all together ended up suiting my needs better.
I don't necessarily hate this approach and could work within an app designed this way with no qualms, but I don't think it should be a hard rule/style guideline because it could lead to confusion when it doesn't work.
Having tried a few different ways, I actually think it's a good thing to separate the resources and have come to value the discipline imposed by no cyclic dependencies between resources. The key is separating resources (managing state and logic about say users) and handlers (responding to http requests about users). So there are a few reasons you might want a resource to know about another one - so it can populate a list of joined resources (like tags on a page), so you can update other resources, so you can check something on another resource (like user permissions). While it is tempting to do this, I think it leads you down a path where all your resources are intimately tied together, which inhibits reuse, and more importantly makes it hard to know where a resource might have been mutated or what effect something like page.Update() will actually have.
The position I've come to is that it's better to keep resources isolated, and then allow handlers to pull in resources as they need them and either show or modify them explicitly in the handler. So this means handlers need to be in a separate package (I use a subpackage of the resource package to keep it all together). This has a few advantages:
The other objection to this is stutter, but I think if you typically use users.New() or users.Find() rather than users.User{} it's not a huge issue, and to me it's not a huge deal, other people might object more strongly to that. I think the advantages outweigh the disadvantages for the particular apps I'm making (web apps), but of course for different projects and people there are different trade-offs. I'm not convinced there is one-size fits all for package guidelines, but it is interesting to hear how other people structure their projects, particularly larger ones. Some problems with organisation only really show up when the project is large enough.
Interesting. I'll have to give this a try with the next app I make. Perhaps I'll be convinced to change my ways :)
Hello!
@Kenny, if you allow me to say that you have 'right' on some points. I believe that we should have some 'common rules' about package naming but it's totally personal decision which depends on the needs of the package or the app you developing.
I do agree with a large number of the author's points, not all of them, we can't say explicit 'DONT USE THAT', they are just recommendation, nothing more, right?
If I may, let me put some words from my experience on very big packages with many dependencies, believe me, I tried a lot of designs to solve my issues and I believe that the 'big' question is NOT 'where' to put the packages because developers ,eventually, soon or later can understand where to put the packages, it's a matter of app architecture and we have many examples to learn from if we are not sure 'where' to put a package.
At my opinion the real question when designing an api's language(package naming, structs, funcs and so on) with golang(and general) is the 'singular' vs 'plural'. I know it can sound like a 'slight detail' but we are all come to this moment when we are thinking 'is it right to keep it plural but is it right to rename it singular? and reverse'.
So I came up with one 'solution':
- When all structs inside a package have a 'common interface', they are all tasks or adaptors or drivers for example, and they do the same thing or trying to solve a specific thing with the same 'policies' and they are NOT depend between their selves (a task1 struct cannot depend on task2 struct) and the client/user is allowed to use more than one of these, then the package name 'should' be plural (: 'tasks', 'adaptors' ,'drivers'). The rest of the packages 'should' be named 'singular'.
More:
- A collection of the same object and the object itself 'should' be inside a 'singular' package name, for example user collection shouldn't be named UsersCollection and it 'should' be inside a 'user' package, so we can name it like 'Collection' and refer to it like 'user.Collection' which will return a user collection ( or a collection of users, point of perspective).
- @Kenny, at your example you use 'users.Find/New` I think (my personal opinion, nothing more, nothing less) that it will be better to be named as 'user.Find -> find a User`, `user.New -> create new User` , `user.SelectMany(procedure func(...)) -> collect and return a collection of User` but as we told, it depends. For example here: https://github.com/kennygrant/gohackernews/blob/master/src/comments/actions/create.go#L24 I see 'view.New' and 'comments.New', 'view' is coming from 'fragmenta' and 'comments' from the app itself, isn't it confusing to have different style for the same thing (:create and return a Single object)?
- Plural package naming when the client(user) is able/can use more than one 'features/structs' from the package for the same scope, example name: 'plugins'. This may looks like conflict with the first of my recommendations, but it's not(think it).
- As the author says, and I agree, we should avoid using so generic package names but sometimes we need to or we don't care because they aren't used by the client, so things like utilities and commons 'should' be named singular (:'util', 'common')
- Prefer to use 'user' instead of 'models/user, services/user', so you can refer 'user.Service, user.User', yes 'user.User' seems 'bad' name but it's expected name from client. But.. wait, so we can't have a package named 'services' ? No, no we still can (if 'services' example name is confusing you can think other name, read below and you'll understand what I am 'trying' to explain) but there we 'should' put services that should work as 'middleware' or 'adaptor' between/for all other 'services-like' structs inside parent packages (:'user.Service', 'post.Service') and they, of course, should be not depend on these structs('user.Service', 'post.Service').
- Ok, what about a service which should work with both 'user' and 'post', where we 'should' to put that? The answer depends on your schema, if the user is the parent of/owns the 'post' then I recommend putting that in the 'user' package, but avoid that too: try to minimize the dependencies inside your app, so we can create a totally new package which will 'bridge' the 'user' package's features/responsibilities with the 'post' package.
Final notes:
- Try to design your internal application like you design an open-source LIBRARY for other developers.
- Think the 'client/user' side before start writing the 'server' code(: think how you would prefer to call that package's struct/function)
Again, my personal opinions. Sorry for my writing style, I'm not a native english speaker, if I didn't make things clear for something or if you thing that I 'miss' the point, please leave a comment below!
Thanks,
kataras
Thanks for the thoughtful comment. I don't have a really strong opinion on the plural/singular thing, but I use plural for resources because it matches the urls and database tables I use (so path /users/1, table users etc). I don't feel this is a hugely important point though, and frankly don't think there should be a prescription either way. The important thing is to be consistent in the way that you use these things within a given app, and I think you have found a few more exceptions here to the rule that packages should be singular, I'm really not sure that there has to be a choice between one or the other as the article pretends.
Re user Collections, I typically just work with slices rather than a custom collection type, so I'd be doing users.FindAll -> []*Users and users.Find -> *User and users.New -> *User, but yes what you suggest sounds reasonable - again I don't think there has to be a hard and fast rule here because different projects are structured differently.
Re a service which works with both user and post, I find most of my handlers end up pulling in several resources, so I prefer to have handlers or other services in separate packages from resources, which solves this problem -I don't think resources should know all about other resources, if you start down that path, every resource has to know about every other and all your code becomes difficult to reason about and intertwined.
Re the view.New you mention, I've actually changed this recently to view.NewRenderer as I think it makes it clearer what it does. comments.New I'm reasonably happy with - I think there's a convention in Go now that mypackage.New will return an instance of the primary type for that package, but equally given a different app/project I see nothing wrong with comment.Collection if used consistently.
PS Re use of english, it would make your writing a lot more legible to simply omit your use of quotes - they are rarely appropriate unless directly quoting someone else, though they are often misused in this way (e.g. I 'miss' the point). I wouldn't normally mention this but did so because of your last paragraph - your English is actually very good, so no need to apologise.
Thanks for the answer, I agree with you each app has different structure, it depends on many things I just share my way of thinking on these things.
As for my english, thanks for your nice words, but the quotes I used on 'miss' or 'wrong' they don't have the use of " some one told that phrase", I use them more about things that can be written with other words, so the 'wrong' it doesn't means that is wrong, but it means that I think it's like a wrong, I know it's not the best way and you're right I'll fix that on my future comments!