Go Packages: Difference between revisions

From NovaOrdis Knowledge Base
Jump to navigation Jump to search
 
(204 intermediate revisions by the same user not shown)
Line 8: Line 8:
Go [[Go_Language_Modularization#Overview|modularization]] is built upon the concept of package.  
Go [[Go_Language_Modularization#Overview|modularization]] is built upon the concept of package.  


A Go package is a collection of [[Go_Language#Constants|constants]], [[Go_Language#Variables|variables]], [[Go_Functions|functions]] and [[Go_Language#Type|type]] definitions, such as [[Go_Structs#Overview|structs]] and [[Go_Interfaces#Override|interfaces]] that are related to each other and provide coherent functionality. These constants, variables, etc. are referred to as members, or features of the package. All elements of a package exist in source files stored in the same directory, and that are compiled together as a unit. No Go source code may exist outside a package.
A Go package is a collection of [[Go_Language#Constants|constants]], [[Go_Language#Variables|variables]], [[Go_Functions|functions]] and [[Go_Language#Type|type]] definitions, such as [[Go_Structs#Overview|structs]] and [[Go_Interfaces#Override|interfaces]] that are connected to each other and provide related functionality. These constants, variables, etc. are referred to as members, or features of the package. All elements of a package exist in source files stored in the same directory, and that are compiled together as a unit. No Go source code may exist outside a package.


The intention behind bundling program elements together is to make the design and the maintenance of large programs practical. Packages are units that can be easier understood and changed, and that can also evolve independently of other packages. These characteristics allow packages to be shared, distributed and reused by different and otherwise independent projects. Packages provide a [[Go_Packages#Packages_as_Namespaces|namespace]] for their members. They also provide an [[Go_Packages#Packages_as_Encapsulation_Mechanism|encapsulation mechanism]] for their code, by hiding implementation details and only exposing features, such as [[Go_Language#Variables|variables]], [[Go_Language#Type|types]] or [[Go_Functions|functions]] that are meant to be publicly consumed. Packages ore one of Go ways of reusing code, according to the [[DRY|Don't Repeat Yourself]] principle. Keeping pre-compiled packages around speeds up the compilation process, because a package whose source code did not change does not need recompilation.
The intention behind bundling program elements together is to make the design and the maintenance of large programs practical. Packages are units that can be easier understood and changed, and that can also evolve independently of other packages. These characteristics allow packages to be shared, distributed and reused by different and otherwise independent projects. As such, a package should contain code that has a single purpose.
 
Write small packages, with a focused API, aimed at performing a single task.
 
Packages provide a [[Go_Packages#Packages_as_Namespaces|namespace]] for their members. They also provide an [[Go_Packages#Packages_as_Encapsulation_Mechanism|encapsulation mechanism]] for their code, by hiding implementation details and only exposing features, such as [[Go_Language#Variables|variables]], [[Go_Language#Type|types]] or [[Go_Functions|functions]] that are meant to be publicly consumed. Packages are one of Go approaches to reusing code, according to the [[DRY|Don't Repeat Yourself]] principle. Keeping pre-compiled packages around speeds up the compilation process, because a package whose source code did not change does not need recompilation.


Packages can be used by themselves, and they also can be published as part of [[Go_Modules|modules]]. Modules have been introduced in Go 1.11.
Packages can be used by themselves, and they also can be published as part of [[Go_Modules|modules]]. Modules have been introduced in Go 1.11.
=How to Organize Packages=
<font color=darkkhaki>
Process this again: https://www.youtube.com/watch?v=MzTcsI6tn-0
If you are going to have multiple packages, it is probably a good idea to orient them around two categories:
# '''business domain types'''
# '''services'''
Use this organization rather than building around of accidents of implementation.
The domain types are types that model the business functionality and objects. Services are packages that operate on or with the domain types.
The packages that contain the domain types should also define the interfaces between your domain types and the rest of the world. These interfaces define the things you want to do with your domain types: <code>Employee</code>,  <code>ProductService</code>, <code>SupplierService</code>, etc.
The domain type package, or the root package of your application should not have any external dependencies. They only exist for the purpose of defining your types and their dependencies.
The implementation of your domain interfaces should be in separate packages (sub-packages), organized by dependency. Dependencies are external data sources, transport logic (http, RPC), etc. You should have one package per dependency.
Other ideas are available here:
* Standard Package Layout https://www.gobeyond.dev/standard-package-layout/
</font>
==Typical Package Layout and Files==
<font size=-2>
.
├── file1.go
├── file1_test.go
├── file2.go
├── file2_test.go
├── ...
├── main.go <font color=teal># The file containing the main() function.</font>
├── doc.go <font color=teal># Package documentation. A separate file is not necessary for small packages.</font>
├── README.md <font color=teal># A README file written in Markdown</font>
├── LICENSE
└── CONTRIBUTING.md
</font>


=Import Path=
=Import Path=


Packages are consumed by importing them with the <code>import</code> keyword.
Packages are consumed by importing them with the <code>import</code> keyword. For details on the syntax, see the [[#Import_Statement|Import Statement]] section, below.


When importing a package, one uses an '''import path''', not the package name:
When importing a package, one uses an '''import path''', not the [[#Package_Name|package name]], which is explained in detail [[#Package_Name|below]]:


<syntaxhighlight lang='go'>
<syntaxhighlight lang='go'>
Line 30: Line 71:
</syntaxhighlight>
</syntaxhighlight>


In the case the package in question is not published as part of a module, the import path is the relative path of the  local file system directory that contains the package source files. All source files that make a package must be stored in the same directory.  
The import path is a file system path-like string that uniquely identifies a package. The language specification does not define what the string means, it is left to the tool that performs the import and compiles the source code to interpret it.
 
In case the package in question is not part of a module, the import path is the '''relative path of the  local file system directory that contains the package source files'''. The path is relative to a <code>src</code> directory whose parent is listed in <code>[[Go_Environment_Variables#GOPATH|GOPATH]]</code>. All source files that make the package must be stored in the same directory, referred to as <span id='Package_Directory'></span>'''package directory'''.  


<font size=-2>
<font size=-2>
  .
  . <font color=teal>← the directory that must be listed in GOPATH to make the package visible to its consumers</font>
  └── src
 
    └── a
└─ src
       └── b
    └─ a
            └── c
       └─ b
                ├─ file1.go
        └─ c
                ├─ file2.go
            ├─ file1.go
                └─ file3.go
            ├─ file2.go
            └─ file3.go
</font>
</font>


where <code>file1.go</code>, <code>file2.go</code> and <code>file3.go</code> are all declared as part of package <code>x</code>. For example, <code>file1.go</code> may look like:
In the example above, <code>file1.go</code>, <code>file2.go</code> and <code>file3.go</code> are all declared as part of package <code>x</code>.  


<code>file1.go</code> may look like:
<syntaxhighlight lang='go'>
<syntaxhighlight lang='go'>
package x
package x


var COLOR = "blue"
var SomeValue = 10


// ...
// ...
</syntaxhighlight>
</syntaxhighlight>


To consume package x, it must be imported
To use the features exported by package <code>x</code>, the consuming package must import the package <code>x</code> using its import path "a/b/c", and must use its features by prefixing them with the [[#Package_Name|package name]]:
 
<syntaxhighlight lang='go'>
package consumer
 
import "a/b/c"


func F() {
  println(x.SomeValue)
}
</syntaxhighlight>
<span id='Package_Name_not_in_Import_Path'></span>As an example, the public variable <code>SomeValue</code> exported by package <code>x</code> is used by prefixing it with the package name. The resulting identifier <code>x.SomeValue</code> is referred to as a <span id='Qualified_Identifier'></span>'''qualified identifier'''.


However, the package name to use for qualified identifiers is not obvious, at least if one looks at the import line. The import path does not have to have anything in common with the package name. This makes this very generic, yet legal case slightly confusing, from a code readability point of view. The reader may ask: where does "x" come from? The package name is known to the compiler, of course, but the programmer must look into a package source file to figure out the name. The readability can be improved with naming conventions and syntax, as shown [[Go_Packages#Conventions_to_Improve_Code_Readability|below]].


If the package is part of a module, the package import path starts with the module path, and continues with the path of the directory that contains the package source files, relative to the root of the module, which is the directory containing the <code>go.mod</code> file.
For more details on declaring packages, see [[#Declaring_Packages|Declaring Packages]] section, below.


The package name is the identifier that follows after the <code>package</code> keyword, in all source files of the package.
If the consumed package is part of a module, the package import path starts with the '''module path, followed by path of the directory containing the package source files, relative to the root of the module'''. Module path is explained [[Go_Modules#Module_Path_.28Module_Name.29|here]]. The root of the module is the directory containing the <code>[[Go.mod#Overview|go.mod]]</code> file.  


What makes this convention slightly confusing is that the import path '''may not''' contain the package name. The package name is known to the compiler, of course, and it actually must be used to qualify the package features, but the programmer must look into the package source file to figure out the name.
<font size=-2>
.
├─ go.mod  <font color=teal># declares that this is module "example.com"</font>
└─ a
    └─ b
       └─ c
         ├─ file1.go
         ├─ file2.go
         └─ file3.go
</font>


=Packages as Namespaces=
The same import and usage syntax applies, the only difference being that the package import path includes the module path:
<syntaxhighlight lang='go'>
package consumer
 
import "example.com/a/b/c"
 
func F() {
  println(x.SomeValue)
}
</syntaxhighlight>
 
As in the previous "package-only" example, the package name does not show anywhere in the import line. To make the code more readable, the following conversions are recommended:
 
* [[#Use_the_Same_Name_for_Package_and_Package_Directory|Use the same name for package and package directory]]
* [[#Declare_the_Package_Name_in_Import_Line|Explicitly declare the package name in the import line]].
 
=<span id='Idiomatic_Package_Conventions'></span><span id='Idiomatic_Package_Naming_Conventions'></span>Package Name=
{{External|https://go.dev/blog/package-names}}
The package name is the [[Go_Language#Identifiers_.28Names.29|identifier]] that follows the <code>package</code> keyword, in all source files of the package:
<syntaxhighlight lang='go'>
package x // the package name
 
...
</syntaxhighlight>
Package names are generally given lowercase, single-word names. Good package names make code better. They should be short, concise and evocative, but not cryptic, and they should lend meaning to the names they export. Avoid long package names. The package name must comply with the Go [[Go_Language#Identifiers_.28Names.29|name requirements]].
 
The contents of a package should be consistent with the name. If you start noticing that a package includes extra logic that has no relationship to the package name, consider exporting it as a separate one, or use a more descriptive package name.  Generally, you will find that the easier it is to give a short and specific self-descriptive name to a package, the better your code composition is. Package names usually take the singular form. Standard library packages <code>bytes</code>, <code>errors</code> and <code>strings</code> use the plural to avoid naming conflicts with the corresponding [[Go_Language#Pre-Declared_Types|pre-declared types]] or keywords. Package names with underscores, hyphens or mixed caps should be avoided. Avoid upper case letters, because not all filesystems are case sensitive.
 
Avoid <code>util</code>, <code>common</code> and the like. These names provide clients with no sense of what the package contains. This makes it harder for clients to use the package and makes it harder for maintainers to keep the package focused. Over time, they accumulate dependencies that can make compilation significantly and unnecessarily slower, especially in large programs. And since such package names are generic, they are more likely to collide with other packages imported by client code, forcing clients to invent names to distinguish them.
 
Don't steal good names from the user. Avoid giving a package a name that is commonly used in client code. For example, the buffered I/O package is called <code>bufio</code> and not <code>but</code> since <code>buf</code> is a good variable name for a buffer.
 
As explained [[#Package_Name_not_in_Import_Path|above]], the package name is not required to show up in the name of any of the file system elements associated with the package, such as [[#Package_Directory|package directory]] or package source files, though code readability can be improved by using the same name for the package and its hosting directory. The [[#Use_the_Same_Name_for_Package_and_Package_Directory|name of the package and its directory should match if possible]], unless you are in one of [[#Last_Segment_Different_than_Package_Name|these situations]].
 
When imported into a consuming package, the package name becomes an accessor for its contents. The names exported by the imported packages are referred with [[#Qualified_Identifier|qualified identifiers]], where the first part is the name of of the imported package and the second is the public feature name itself:
 
<syntaxhighlight lang='go'>
import "quota"
 
...
 
quota.ComputeLimit(...)
</syntaxhighlight>
Knowing this, the exported names in the package can be chosen in such a way to avoid repetition, as described in [[#Try_to_Make_Package_Names_and_Exported_Names_Work_Together|Try to Make Package Names and Exported Names Work Together]], below.
 
The package name can be changed by the consuming code one a per-file basis by [[#Renaming_Imports|renaming the import]].
 
Also see: {{Internal|Go_Style#Naming|Go Style}}
==Conventions to Improve Code Readability==
===Use the Same Name for Package and Package Directory===
If possible, use the same name for the [[#Package_Directory|package directory]] and [[#Package_Name|package name]]. Using different names is legal, but using the same name will improve readability by turning this:
<syntaxhighlight lang='go'>
package consumer
 
import "a/b/c"
 
func F() {
  println(x.SomeValue)
}
</syntaxhighlight>
 
into this:
<syntaxhighlight lang='go'>
package consumer
 
import "a/b/x" // the reader gets a hint where "x" is coming from
 
func F() {
  println(x.SomeValue)
}
</syntaxhighlight>
The second version is slightly reader-friendlier, giving at least a hint where "x" is coming from.
 
<span id='Last_Segment_Different_than_Package_Name'></span>However, there are situations when the name of the package directory must be different from the package name. There are at least three cases when this situation is justified.
 
# Packages defining an executable command. The name of the package has to be <code>[[#The_main_Package|main]]</code>, regardless of the name of the directory containing the command source files. If you can also name the package directory "main", that would be ideal, but that is not always possible.
# <span id='External_Test_Packages'></span>Some files in the directory may have the suffix <code>_test</code> on their package name if the file name ends in <code>_test.go</code>. Such a directory will define two packages: the normal one and an '''external test package'''. The <code>_test</code> suffix signals to <code>[[Go_Tool#test|go test]]</code> that it must build both packages, and specifies which files belong to each packages. Also see [[Go_Testing#Write_a_Unit_Test|Go Testing]].
# Some dependency management tool append version number suffixes to package import paths.
 
===Declare the Package Name in Import Line===
If [[#Use_the_Same_Name_for_Package_and_Package_Directory|using the same name for package and package directory]] is not an option, the readability can be improved by using a syntactic feature of the <code>import</code> statement which allows explicitly specifying the package name in the import statement:
<syntaxhighlight lang='go'>
package consumer
 
import x "a/b/c" // unequivocally clarifies where 'x' is coming from
 
func F() {
  println(x.SomeValue)
}
</syntaxhighlight>
===Try to Make Package Names and Exported Names Work Together===
Since each reference to the member of an imported package uses the imported package name as [[#Qualified_Identifier|qualifier]], the package name will provide context for its content. As such, the burden of describing the package semantics is borne equally by the package name and the member name. Design the names to work together, like in <code>yaml.Reader</code>. <code>yaml.YAMLReader</code> would be redundant. Another example is a buffered reader type in the <code>bufio</code> package, which is is called <code>Reader</code>, not <code>BufferedReader</code> because when imported, it will be referred to as <code>bufio.Reader</code>, and not with the repetitive <code>bufio.BufferedReader</code>.
 
This recommendations apply to [[Go_Constants#Naming|constants]], [[Go_Variables#Naming|variables]], function and type names.
 
However, this is just a guideline. In some cases, duplicating the package name in the name of the exported feature may make sense. Use your judgement.
===Document Packages===
For small packages, add a [[Go_Language#Comments|comment line]] above the package declaration describing its contents, as shown in the [[#Declaring_Packages|Declaring Packages]] section. <font color=darkkhaki>This only works if the package has only one file. What to do if the package has more than one file? Where to place the documentation?</font> For large packages, use a <code>[[doc.go]]</code> file.
 
=Modularization Considerations=
==Packages as Namespaces==
Each package defines a distinct namespace that hosts all its [[Go_Language#Identifiers_.28Names.29|identifiers]]. Within the package namespace, all identifiers must be unique.
Each package defines a distinct namespace that hosts all its [[Go_Language#Identifiers_.28Names.29|identifiers]]. Within the package namespace, all identifiers must be unique.


When a public feature of a package, such as a function or a type is used outside of the package, its name must prefixed with the name of the package, with the intention of making the package and feature name pair unique in the [[Go_Language#Universe_Block|universe block]]. Such a name is referred to as '''qualified identifier'''. For example, the <code>Println()</code> function, exported by the <code>fmt</code> package, is invoked with the <code>fmt.Println</code> qualified identifier:
When a public feature of a package, such as a function or a type is used outside of the package, its name must prefixed with the name of the package, with the intention of making the package and feature name pair unique in the [[Go_Language#Universe_Block|universe block]]. Such a name is referred to as [[#Qualified_Identifier|qualified identifier]]. For example, the <code>Println()</code> function, exported by the <code>fmt</code> package, is invoked with the <code>fmt.Println</code> qualified identifier:
<syntaxhighlight lang='go'>
<syntaxhighlight lang='go'>
import "fmt"
import "fmt"
Line 74: Line 240:
This mechanism allows us to chose short, succinct names for the members of a package, without creating conflicts with other parts of the program.
This mechanism allows us to chose short, succinct names for the members of a package, without creating conflicts with other parts of the program.


=<span id='Encapsulation'></span>Packages as Encapsulation Mechanism=
==<span id='Encapsulation'></span>Packages as Encapsulation Mechanism==
The package is the most important mechanism for encapsulation in Go programs. Packages control which names are visible outside the package, by a simple convention: all names declared in the [[Go_Language#Package_Block|package block]] or [[Go_Structs#Exported_Fields|field names]] and [[Go_Language_Object_Oriented_Programming#Methods|method names]] that start with an uppercase letter are publicly visible. Publicly visible names are referred to as [[#Export|exported]] names.
The package is the most important mechanism for encapsulation in Go programs. Packages control which names are visible outside the package, by a simple convention: all names declared in the [[Go_Language#Package_Block|package block]] or [[Go_Structs#Exported_Fields|field names]] and [[Go_Language_Object_Oriented_Programming#Methods|method names]] that start with an uppercase letter are publicly visible. Publicly visible names are referred to as [[#Export|exported]] names. For more details on exporting package members, see the [[#Exporting_Package_Members|Exporting Package Members]] section below.


Encapsulation is useful because it allows the package maintainer to change the implementation of the hidden members without affecting the public interface of the package, thus allowing the package to evolve without breaking its dependents. Restricting variables' visibility constrains the package clients to access and update them only through exported functions that preserve internal invariants and enforce mutual exclusion in concurrent programs.
Encapsulation is useful because it allows the package maintainer to change the implementation of the hidden members without affecting the public interface of the package, thus allowing the package to evolve without breaking its dependents. Restricting variables' visibility constrains the package clients to access and update them only through exported functions that preserve internal invariants and enforce mutual exclusion in concurrent programs.


=Dependencies=
==Dependencies==
Each import declaration established a dependency from the importing package to the imported package. <code>go build</code> detects dependency cycles:
Each import declaration established a dependency from the importing package to the imported package. <code>go build</code> detects dependency cycles:
<font size=-2>
<font size=-2>
Line 87: Line 253:
  imports b
  imports b
  imports a: import cycle not allowed
  imports a: import cycle not allowed
</font>
=<span id='Idiomatic_Package_Naming_Conventions'></span>Idiomatic Package Conventions=
Write small packages, with a focused API, aimed at performing a single task.
Package names are generally given lowercase, single-word names. They should be short, concise and evocative, but not cryptic. Avoid long package names. The contents of a package should be consistent with the name. If you start noticing that a package includes extra logic that has no relationship to the package name, consider exporting it as a separate one, or use a more descriptive package name.  Generally, you will find that the easier it is to give a short and specific self-descriptive name to a package, the better your code composition is. Package names usually take the singular form. Standard library packages <code>bytes</code>, <code>errors</code> and <code>strings</code> use the plural to avoid naming conflicts with the corresponding [[Go_Language#Pre-Declared_Types|pre-declared types]] or keywords. Package names with underscores, hyphens or mixed caps should be avoided.
The name of the package and the name of the directory hosting the package source files should match if possible, unless you are in one of [[#Last_Segment_Different_than_Package_Name|these situations]].
The package members intended to be [[#Export|exported]] must start with an uppercase character.  Only expose the package elements that we explicitly want other package to use, and hide everything else.
Since each reference to the member of an imported package uses the imported package name as [[#Package_Name_as_Qualifier|qualifier]], the package name will provide context for its content. As such, the burden of describing the package maker is borne equally by the package name and the member name. Design the names to work together, like in <code>yaml.Reader</code>. <code>yaml.YAMLReader</code> would be redundant.
Every package should have a [[Go_Language#Comments|comment]] describing its contents.
Separate private code using an internal directory.
Also see: {{Internal|Go_Style#Naming|Go Style}}
==Typical Package Layout and Files==
<font size=-2>
.
├── file1.go
├── file1_test.go
├── file2.go
├── file2_test.go
├── ...
├── main.go <font color=teal># The file containing the main() function.</font>
├── doc.go <font color=teal># Package documentation. A separate file is not necessary for small packages.</font>
├── README.md <font color=teal># A README file written in Markdown</font>
├── LICENSE
└── CONTRIBUTING.md
</font>
</font>


Line 122: Line 258:
Packages are defined by writing one or more [[#Source_File|source files]] that declare association with the package. Every source file that is part of the implementation of a package must start with the <code>package</code> statement, which consists of the <code>[[Go_Language#package_keyword|package]]</code> keyword followed by the [[#Package_Name|name]] of the package the source file belongs to.
Packages are defined by writing one or more [[#Source_File|source files]] that declare association with the package. Every source file that is part of the implementation of a package must start with the <code>package</code> statement, which consists of the <code>[[Go_Language#package_keyword|package]]</code> keyword followed by the [[#Package_Name|name]] of the package the source file belongs to.
<syntaxhighlight lang='go'>
<syntaxhighlight lang='go'>
// color is a package that provides functionality around colors
// Package color is a package that provides functionality around colors
package color
package color


Line 128: Line 264:
</syntaxhighlight>
</syntaxhighlight>


All files sharing the same package name form the implementation of the package. The identifiers declared in any of the files of a package are accessible from all other files of the same package. All source files of a package inhabit the same file system directory. <font color=darkkhaki>A package's source files cannot be spread across multiple directories.</font> A directory may not contain source files belonging to different packages. In such a situation, the compiler raises an error:
All files sharing the same package name form the implementation of the package. The identifiers declared in any of the files of a package are accessible from all other files of the same package.  
<font size=-1>
 
All source files of a package inhabit the same file system directory, referred to as [[#Package_Directory|package directory]].  
 
A directory may not contain source files belonging to different packages. In such a situation, the compiler raises an error:
<font size=-2>
  found packages colors (colors.go) and shapes (shapes.go) in .../src/colors
  found packages colors (colors.go) and shapes (shapes.go) in .../src/colors
</font>
</font>
==<span id='Source_File'></span>Source Files==
<font color=darkkhaki>Document source file naming conventions.</font>


==Package Name==
It is legal to declare packages that live in different package directories but use the same package name. They can even be used together, as long as the consuming package is aware they're '''different''' packages and makes that syntactically obvious by using different aliases:
The package name is given by the [[Go_Language#Identifiers_.28Names.29|identifier]] that follows the <code>package</code> keyword. A package name must comply with the Go [[Go_Language#Identifiers_.28Names.29|name requirements]].


Conventionally, the name of the directory that hosts the package's source files should match the package name, but this is not a requirement, and it is not enforced. It is good practice though, otherwise the source code will contain confusing constructs like the one described [[#Last_Segment_Different_than_Package_Name|below]].
<font size=-2>
 
The package name provides the default identifier to use when the package members are accessed from an importing package and it is conventionally the last segment of the package [[#Import_Path|import path]]. Alternative identifiers can be used, as shown in the "[[Go_Packages#Renaming_Imports|Renaming Imports]]" section below.
 
==Import Path==
Each package is identified by a unique string called package import path. The import path follows the <code>import</code> keyword in the [[Go_Packages#Import_Statement|import statement]] when [[#Consuming_Packages|consuming the package]].
 
The language specification does not define what the string means, it is left to the tool that performs the import and compiles the source code to interpret it. In case of the [[Go_Tool#Overview|<code>go</code> tool]], the import path denotes a directory that contains the package's source files, relative to a <code>[[Go_Environment_Variables#GOPATH|GOPATH]]</code> element, which is usually a <code>src</code> directory, a subdirectory of the [[Go_Language_Modularization#src|workspace]]. The last segment of the path is usually the [[#Package_Name|package name]], but this is not enforced in any way, and some times it might not actually be the case.
 
<font size=-1>
  .
  .
  └─ src
  └─ src
    └─ <font color=Firebrick>misc</font>
    ├─ a
        └─ <font color=Firebrick>shapes</font> # the package directory name coincides with the package name
    │  └─ file1.go <font color=teal># declares a package named "x"</font>
            └─ file1.go # the file belongs to the "shapes" package
    ├─ b
    │  └─ file2.go <font color=teal># declares a different package, also named "x"</font>
    └─ consumer
       └─ consumer.go
 
</font>
</font>
In the above example, the import path is <code>misc/shapes</code> and the package name is <code>shapes</code>.
 
<code>file1.go</code>:
<syntaxhighlight lang='go'>
<syntaxhighlight lang='go'>
import "misc/shapes"
package x
...
 
shapes.Apply()
var SomeValue = 10
</syntaxhighlight>
</syntaxhighlight>
The import paths should be globally unique for packages intended for sharing. That is why is good practice to start the import path with the name of the Internet domain of the organization that owns or hosts the package.
<code>file2.go</code>:
<syntaxhighlight lang='go'>
package x


<span id='Last_Segment_Different_than_Package_Name'></span>The fact that the last segment of the import path coincides with the package name is purely conventional. There could be situations where the name of the directory that hosts the package is different from the package name. There are at least three cases when the exceptions are justified. The first are the packages defining an executable command. The name of the package has to be <code>[[#The_main_Package|main]]</code>, regardless of the name of the directory containing the command source files. <span id='External_Test_Packages'></span>The second exception is that some files in the directory may have the suffix <code>_test</code> on their package name if the file name ends in <code>_test.go</code>. Such a directory may define two packages: the normal one and an '''external test package'''. The <code>_test</code> suffix signals to <code>[[Go_Tool#test|go test]]</code> that it must build both packages, and specifies which files belong to each packages. Also see: [[Go_Testing#Write_a_Unit_Test|Go Testing]]. The third exception is that some dependency management tool append version number suffixes to package import paths.
var SomeValue = 20
</syntaxhighlight>
<code>consumer.go</code>:
<syntaxhighlight lang='go'>
package consumer


In all other cases, this situation should be avoided, because the import path is the filesystem path of the package, and the [[#Package_Name|package name]] is the identifier that follows the <code>package</code> keyword in the package files, and we will find ourselves in this situation:
import (
  "a"
  x2 "b"
)


<font size=-1>
func F() {
.
  println(x.SomeValue)
└─ src
  println(x2.SomeValue)
    └─ <font color=Firebrick>weights</font> # the import path is "weights"
}
        └─ file2.go # the file belongs to the "nuances" package
</syntaxhighlight>
<code>F()</code> will print:
<font size=-2>
10
20
</font>
</font>
<syntaxhighlight lang='go'>
import "weights"
...
nuances.Apply() // this is confusing, so don't do it
</syntaxhighlight>


===Modules and Import Path===
=<span id='Export'></span>Exporting Package Members=
If a package belongs to a [[#Go_Modules|module]], the package's import path is the [[Go_Modules#Module_Path|module path]] joined with the package's subdirectory within the module. Packages in the standard library do not have a module path prefix.
 
==<span id='Export'></span>Exporting Package Members==
A package identifier is made publicly visible outside of the package if:
A package identifier is made publicly visible outside of the package if:
# The name is declared in the [[Go_Language#Package_Block|package block]] or is a [[Go_Structs#Exported_Fields|struct field]] or a [[Go_Language_Object_Oriented_Programming#Methods|method]] name.
# The name is declared in the [[Go_Language#Package_Block|package block]] or is a [[Go_Structs#Exported_Fields|struct field]] or a [[Go_Language_Object_Oriented_Programming#Methods|method]] name.
Line 202: Line 340:


Just because an identifier is unexported, it does not mean other packages can't indirectly access it. A function can return the value of an unexported '''type''' and this value is accessible by any calling function, even if the calling function has been declared in a different package. This can be coupled with the short declaration operator <code>[[Go_Language#Short_Variable_Declaration|:=]]</code>, which infers the type of the variable. A variable of that type cannot be explicitly declared with <code>[[Go_Language#var_keyword|var]]</code>.
Just because an identifier is unexported, it does not mean other packages can't indirectly access it. A function can return the value of an unexported '''type''' and this value is accessible by any calling function, even if the calling function has been declared in a different package. This can be coupled with the short declaration operator <code>[[Go_Language#Short_Variable_Declaration|:=]]</code>, which infers the type of the variable. A variable of that type cannot be explicitly declared with <code>[[Go_Language#var_keyword|var]]</code>.
===Embedded Type Export===
==Embedded Type Export==
{{Internal|Go_Structs#Embedded_Type_Export|Structs &#124; Embedded Type Export}}
{{Internal|Go_Structs#Embedded_Type_Export|Structs &#124; Embedded Type Export}}
==Unexported Members==
A package member that is not explicitly exported becomes '''unexported''' automatically.


=Building Packages=
=Building Packages=
Line 215: Line 355:
When a package file changes, the entire package must be recompiled and all the packages that depend on it.
When a package file changes, the entire package must be recompiled and all the packages that depend on it.
==Publishing Locally==
==Publishing Locally==
This section documents compiling and installing packages locally with <code>[[Go_Tool#install|go install]]</code> in absence of module support. For details on compiling and installing package within the context of a module, see: {{Internal|Go_Modules#Publishing_Modules|Publishing Modules Locally}}
{{Internal|go install#Overview|<tt>go install</tt>}}


===Single Package with No Dependencies===
==Publishing Remotely==
A single package is compiled, and its corresponding [[Go_Language#Object_File|object file]] locally installed by executing:


<syntaxhighlight lang='bash'>
go install <package-import-path>
</syntaxhighlight>


We assume that while running the command, the <code>[[Go_Environment_Variables#GOPATH|GOPATH]]</code> environment variable is set to point to a directory that contains the <code>src/<package-import-path></code> directory. For a package whose import path is <code>other/b</code>, the corresponding directory layout is:
<font size=-1>
. <font color=teal>← This is the directory GOPATH points to</font>
│ 
└── src
      └── other
          └── b
              └── b.go
</font>
where the <code>b.go</code> source file contains:
<syntaxhighlight lang='go'>
package b


...
</syntaxhighlight>
We also assume that <code>[[Go_Environment_Variables#GO111MODULE|GO111MODULE]]</code> environment variable is set to "off" or "auto".
The command to compile and install the <code>b</code> package, identified by its <code>other/b</code> import path is:
<syntaxhighlight lang='bash'>
go install other/b
</syntaxhighlight>
The command will compile the source code and install the package object file under <code>${[[Go_Environment_Variables#GOPATH|GOPATH]]}/pkg/${[[Go_Environment_Variables#GOOS|GOOS]]}_${[[Go_Environment_Variables#GOARCH|GOARCH]]}/<import-path></code>:
<font size=-1>
.
├── pkg
│    └── darwin_amd64
│         └── other
│             └── b.a
│ 
└── src
      └── other
          └── b
              └── b.go
</font>
If the <code>pkg</code> subdirectory does not exist, it will be created. The <code>go install</code> can be run from the <code>${GOPATH}</code> directory, or from some any other directory. As long the <code>GOPATH</code> and <code>GO111MODULE</code> are correctly set, the results will be the same.
===<tt>GOPATH</tt> Lists Multiple Directories===
<code>[[Go_Environment_Variables#GOPATH|GOPATH]]</code> may list [[Go_Environment_Variables#Relative_Order|multiple colon-separated directories]]. As long as the package to be installed is declared under one of those directories, it will be correctly located and <code>go install</code> will write the corresponding object files under the <code>pkg</code> subdirectory of the corresponding root.
If multiple <code>GOPATH</code> directories contain package with the same import path, only the first package will be processed.
===Packages with Dependencies===
If a package "a" depends on the package "other/b", the command
<syntaxhighlight lang='bash'>
go install a
</syntaxhighlight>
will correctly locate and compile the source files for both "a" and "other/b" packages, and if deeper dependencies exist, all source files in the transitive dependency tree. If any of the dependencies fails to compile, the installation process will fail. However, only the [[Go_Language#Object_File|object file]] corresponding to the package "a" will be written in <code>${GOPATH}/pkg</code>. To install the object files for the entire dependency chain, the packages have to be explicitly specified on the command line:
<syntaxhighlight lang='bash'>
go install a other/b
</syntaxhighlight>
===Executables===
An executable is produced if the linker detects a <code>[[#The_.22main.22_Package|main]]</code> package in the list of packages to be compiled and installed. <code>[[Go_Tool#install|go install]]</code> compiles all dependencies, links them and installs the binary under the directory specified by the <code>[[Go_Environment_Variables#GOBIN|GOBIN]]</code> environment variable or under the <code>bin</code> subdirectory of the <code>[[Go_Environment_Variables#GOPATH|GOPATH]]</code> directory, depending on which environment variable is defined. If the <code>bin</code> directory does not exist, it is created.
==Publishing Remotely==


=Consuming Packages=
=Consuming Packages=
Line 304: Line 381:
Imported packages may be grouped introducing blank lines. By conventions, the names in a group are sorted alphabetically,
Imported packages may be grouped introducing blank lines. By conventions, the names in a group are sorted alphabetically,


==Package Name as Qualifier==
Once imported, the package names are used as qualifiers in the [[#Qualified_Identifier|qualified identifiers]] of package's exported names. The package names are inferred automatically by the compiler, but they can be locally changed by [[#Renaming_Imports|renaming imports]].
The package name is used by default to qualify the members of the package when used from an importing package:
<syntaxhighlight lang='go'>
import "colors"
...
colors.Apply() // the package name is used
</syntaxhighlight>
===Renaming Imports===
===Renaming Imports===
A different qualifier can be declared, providing an alias for the package name. This is called renaming an import:
The package name is only the default name for imports. It need not be unique across all source code. In case of collision, the consuming package can choose to use a different local name for the imported package. A different qualifier can be declared, providing an alias for the package name. This is called renaming an import:
<syntaxhighlight lang='go'>
<syntaxhighlight lang='go'>
import details "colors"
import myQualifier "a/b/c"
...
...
details.Apply()
println(myQualifier.SomeValue)
</syntaxhighlight>
</syntaxhighlight>
This feature is useful when two packages have the same name when their [[#Import_Path|import paths]] differ. As an example, <code>math/rand</code> and <code>crypto/rand</code> have the same package name <code>rand</code>. In this situation, one of the imports must be renamed. Renaming only affects the importing file.
This feature is useful when two packages have the same name when their [[#Import_Path|import paths]] differ. As an example, <code>math/rand</code> and <code>crypto/rand</code> have the same package name <code>rand</code>. In this situation, one of the imports must be renamed. Renaming only affects the importing file.
==Blank Imports==
==Blank Imports==
It is a compilation error to import a package but not refer to it. However, there are situation when we want to import a package merely for the [[#Package_Initialization|initialization side effects]]. This can be achieved using the [[Go_Language#The_Blank_Identifier_.28_.29|blank identifier]] at import:
It is a compilation error to import a package but not refer to it. However, there are situation when we want to import a package merely for the [[#Package_Initialization|initialization side effects]]. This can be achieved using the [[Go_Language#The_Blank_Identifier_.28_.29|blank identifier]] at import:
Line 325: Line 397:
</syntaxhighlight>
</syntaxhighlight>
This is known as a '''blank import'''.
This is known as a '''blank import'''.
=Package Initialization=
Package initialization begins by importing dependency packages, then initializing the [[#Package_Global_State|package global state]], which consists in all package-level constants and variables, in the order in which they are declared. This process happens recursively for each dependency package. If the package has multiple files, they are initialized in the order in which they are provided to the compiler. The [[Go_Tool#Overview|<code>go</code> tool]] sorts them by name before invoking the compiler. The initialization continues with the invocation of the <code>[[#init|init()]]</code> functions, if they exist.
::[[File:Go_Package_Initialization.png|700px]]
==<span id='init'></span><tt>init()</tt>==
Any file may contain any number of functions with the following signature:
<syntaxhighlight lang='go'>
func init() {
  ...
}
</syntaxhighlight>
Such <code>init()</code> function cannot be called or referenced, but they are automatically executed in the order in which they are declared, as part of package initialization, when the package they're part of is imported. <code>init()</code> is called after the initialization of the package global constants and after all variable declarations in the package have evaluated their initializers. The intention behind allowing <code>init()</code> function is that they can set up whatever state is required. Note that <code>init()</code> will only run if the package is imported.
If multiple <code>init()</code> functions exist in a file, they will be executed in the order in which they were declared in the file.
If there are multiple <code>init()</code> function in a package, declared across multiple files, the files will be sorted in their alphabetical order and <code>init()</code> functions will be executed in that order.
If multiple packages are imported, one package is initialized at a time, the dependencies are fully initialized before the dependents are initialized, so the <code>init()</code> functions in dependencies are executed before the <code>init()</code> functions declared in dependents. Each <code>init()</code> function is executed only once.
The <code>[[#The_main_Package|main]]</code> is the last to be initialized, where the <code>init()</code> functions will be executed after global constant and variable initialization and before the <code>main()</code> function.
==Package Global State==
A package's global state includes the values of all constants and variables declared in the [[Go_Language#Package_Block|package's lexical block]]. We should strive to maintain little to no mutable package global state. [[Go_Component_Design#Explicit_Component_Dependencies|Explicit Component Dependencies]] section gives at least one reason why.  Given the fact that the only purpose of the <code>[[#init|init()]]</code> function is to initialize package global state, using it is a serious red flag in almost any program.


=<span id='main'></span><span id='The_main_Package'></span><span id='The_.22main.22_Package'></span>Executables and the "<tt>main</tt>" Package=
=<span id='main'></span><span id='The_main_Package'></span><span id='The_.22main.22_Package'></span>Executables and the "<tt>main</tt>" Package=
Line 339: Line 436:
* [[Go_Tool#Build_and_Install_an_Executable_with_go_install|Building and installing an executable with <tt>go install</tt>]]
* [[Go_Tool#Build_and_Install_an_Executable_with_go_install|Building and installing an executable with <tt>go install</tt>]]


=Package Initialization=
=Single-Type Packages=
Package initialization begins by initializing package-level variables in the order in which they are declared, resolving the dependencies first. If the package has multiple files, they are initialized in the order in which they are provided to the compiler. The [[Go_Tool#Overview|<code>go</code> tool]] sorts them by name before invoking the compiler.
Packages that expose one principal data type, plus its methods and often a <code>New()</code> function to create instances are referred to as '''single-type packages'''. The enclosed type is always qualified with the package name when used, so the package names should be short.
=<span id='Internal_Packages'></span><span id='Internal_Directory'></span>Internal (Private) Packages=
 
The module's code that should not be imported by the module's dependents should be placed in a directory named <code>internal</code>, or in one of its recursive subdirectories.  The packages declared under a directory named <code>internal</code> are prevented by the compiler to be imported by packages that live outside the source subtree in which the internal packages resides. <code>internal</code> is official, this pattern is enforced by the Go compiler. When the compiler sees an import of a package with <code>internal</code> in its path, it verifies that the package doing this import is within the directory tree rooted '''at the parent''' of the <code>internal</code> directory. Packages declared in <code>internal</code> siblings directories can import.
 
In the following example, the package <code>a</code> can import from package <code>b</code>, because <code>a</code> has the <code>internal</code> '''parent''' as its ancestor:
<font size=-2>
.
├── go.mod
└── level0
    ├── internal
    │   └── b
    │      └── b.go
    └── level1-1
        └── pkg
            └── a
                └── a.go
</font>
 
Normally, the projects use a less convoluted directory structure, where <code>[[Go_Project#internal_dir|internal]]</code> and <code>[[Go_Project#pkg|pkg]]</code> are siblings in the module root directory.
 
This is what happens in GoLang if a package tries to import from an <code>internal</code> directory:
 
:[[File:Go_Internal_Package.png|500px]]


The initialization continues with the invocation of the <code>init()</code> functions, if they exist. Any file may contain any number of functions with the following signature:
<br>
<syntaxhighlight lang='go'>
A typical project layout employs this pattern if it makes sense. See: {{Internal|Go_Project#internal_2|Go Project <tt>internal</tt> directory}}
func init() {
  ...
}
</syntaxhighlight>
Such <code>init()</code> function cannot be called or referenced, but they are automatically executed in the order in which they are declared, when the package they're part of is imported. One package is initialized at a time, the dependencies are fully initialized before the dependents are initialized, so the <code>init()</code> functions in dependencies are executed before the <code>init()</code> functions declared in dependents. The <code>[[#The_main_Package|main]]</code> is the last to be initialized.


=Single-Type Packages=
Packages that expose one principal data type, plus its methods and often a <code>New()</code> function to create instances are referred to as '''single-type packages'''. The enclosed type is always qualified with the package name when used, so the package names should be short.
=<span id='Internal_Packages'></span>Internal (Private) Packages=
<font color=darkkhaki>TODO:
<font color=darkkhaki>TODO:
* https://go.dev/doc/go1.4#internalpackages
* Addison-Wesley The Go Programming Language Section 10.7.5
* Addison-Wesley The Go Programming Language Section 10.7.5
* Microservices with Go. Chapter 2. "Private Packages" https://learning.oreilly.com/library/view/microservices-with-go/9781804617007/B18865_02.xhtml#:-:text=Private%20packages It seems "internal" does not work the way it is described there. I can use code declared in "internal" from outside of that directory.
* Microservices with Go. Chapter 2. "Private Packages" https://learning.oreilly.com/library/view/microservices-with-go/9781804617007/B18865_02.xhtml#:-:text=Private%20packages It seems "internal" does not work the way it is described there. I can use code declared in "internal" from outside of that directory.
</font>
</font>


:[[File:Go_Internal_Package.png|500px]]
=<span id='Hierarchical_Packages'></span><span id='Nested_Packages'></span>Hierarchical (Nested) Packages=
<font color=darkkhaki>
Hierarchical packages or nested packages.
 
When necessary, use a descriptive parent package and several children packages implementing functionality, like the standard library <code>encoding</code> package:


=Hierarchical Packages=
<font size=-2>
Hierarchical packages or nested packages.
./encoding/
=The "pkg" Package=
├── ascii85              <font color=teal> // package ascii85</font>
<font color=darkkhaki>[[MiGo]] Chapter 2 Public packages</font>
│   ├── ascii85.go
│   └── ascii85_test.go
├── asn1
│   ├── asn1.go            <font color=teal>// package asn1</font>
│   ├── asn1_test.go
│   ├── common.go
│   └─ ...
├── base32
│   └── ...
├── ...
├── xml
│    └── ...
└── encoding.go          <font color=teal>// package encoding</font>
</font>
</font>


=Vendoring=
=Vendoring=
Line 375: Line 507:
of <code>crash/bang</code> resolves to <code>foo/vendor/crash/bang</code>, not the top-level <code>crash/bang</code>. Code in vendor directories is not subject to import path checking. When <code>[[Go_Tool#get|go get]]</code> checks out or updates a Git repository, it now also updates submodules. Vendor directories do not affect the placement of new repositories being checked out for the first time by <code>go get</code>. Those are always placed in the main <code>GOPATH</code>, never in a vendor subtree.
of <code>crash/bang</code> resolves to <code>foo/vendor/crash/bang</code>, not the top-level <code>crash/bang</code>. Code in vendor directories is not subject to import path checking. When <code>[[Go_Tool#get|go get]]</code> checks out or updates a Git repository, it now also updates submodules. Vendor directories do not affect the placement of new repositories being checked out for the first time by <code>go get</code>. Those are always placed in the main <code>GOPATH</code>, never in a vendor subtree.
</font>
</font>
=Locating Third-Party Packages=
Use:
{{External|https://pkg.go.dev/}}
to find packages with functionality you might find useful.
=TODO=
=TODO=
<font color=darkkhaki>
<font color=darkkhaki>
* Process: https://medium.com/rungo/everything-you-need-to-know-about-packages-in-go-b8bac62b74cc
* Process: https://medium.com/rungo/everything-you-need-to-know-about-packages-in-go-b8bac62b74cc
</font>
</font>

Latest revision as of 23:18, 12 September 2024

External

Internal

Overview

Go modularization is built upon the concept of package.

A Go package is a collection of constants, variables, functions and type definitions, such as structs and interfaces that are connected to each other and provide related functionality. These constants, variables, etc. are referred to as members, or features of the package. All elements of a package exist in source files stored in the same directory, and that are compiled together as a unit. No Go source code may exist outside a package.

The intention behind bundling program elements together is to make the design and the maintenance of large programs practical. Packages are units that can be easier understood and changed, and that can also evolve independently of other packages. These characteristics allow packages to be shared, distributed and reused by different and otherwise independent projects. As such, a package should contain code that has a single purpose.

Write small packages, with a focused API, aimed at performing a single task.

Packages provide a namespace for their members. They also provide an encapsulation mechanism for their code, by hiding implementation details and only exposing features, such as variables, types or functions that are meant to be publicly consumed. Packages are one of Go approaches to reusing code, according to the Don't Repeat Yourself principle. Keeping pre-compiled packages around speeds up the compilation process, because a package whose source code did not change does not need recompilation.

Packages can be used by themselves, and they also can be published as part of modules. Modules have been introduced in Go 1.11.

How to Organize Packages

Process this again: https://www.youtube.com/watch?v=MzTcsI6tn-0

If you are going to have multiple packages, it is probably a good idea to orient them around two categories:

  1. business domain types
  2. services

Use this organization rather than building around of accidents of implementation.

The domain types are types that model the business functionality and objects. Services are packages that operate on or with the domain types.

The packages that contain the domain types should also define the interfaces between your domain types and the rest of the world. These interfaces define the things you want to do with your domain types: Employee, ProductService, SupplierService, etc.

The domain type package, or the root package of your application should not have any external dependencies. They only exist for the purpose of defining your types and their dependencies.

The implementation of your domain interfaces should be in separate packages (sub-packages), organized by dependency. Dependencies are external data sources, transport logic (http, RPC), etc. You should have one package per dependency.

Other ideas are available here:

Typical Package Layout and Files

.
├── file1.go
├── file1_test.go
├── file2.go
├── file2_test.go
├── ...
├── main.go # The file containing the main() function.
├── doc.go # Package documentation. A separate file is not necessary for small packages.
├── README.md # A README file written in Markdown
├── LICENSE
└── CONTRIBUTING.md

Import Path

Packages are consumed by importing them with the import keyword. For details on the syntax, see the Import Statement section, below.

When importing a package, one uses an import path, not the package name, which is explained in detail below:

import "<import-path>"

Example:

import "a/b/c"

The import path is a file system path-like string that uniquely identifies a package. The language specification does not define what the string means, it is left to the tool that performs the import and compiles the source code to interpret it.

In case the package in question is not part of a module, the import path is the relative path of the local file system directory that contains the package source files. The path is relative to a src directory whose parent is listed in GOPATH. All source files that make the package must be stored in the same directory, referred to as package directory.

. ← the directory that must be listed in GOPATH to make the package visible to its consumers
│ 
└─ src
   └─ a
      └─ b
         └─ c
            ├─ file1.go
            ├─ file2.go
            └─ file3.go

In the example above, file1.go, file2.go and file3.go are all declared as part of package x.

file1.go may look like:

package x

var SomeValue = 10

// ...

To use the features exported by package x, the consuming package must import the package x using its import path "a/b/c", and must use its features by prefixing them with the package name:

package consumer

import "a/b/c"

func F() {
  println(x.SomeValue)
}

As an example, the public variable SomeValue exported by package x is used by prefixing it with the package name. The resulting identifier x.SomeValue is referred to as a qualified identifier.

However, the package name to use for qualified identifiers is not obvious, at least if one looks at the import line. The import path does not have to have anything in common with the package name. This makes this very generic, yet legal case slightly confusing, from a code readability point of view. The reader may ask: where does "x" come from? The package name is known to the compiler, of course, but the programmer must look into a package source file to figure out the name. The readability can be improved with naming conventions and syntax, as shown below.

For more details on declaring packages, see Declaring Packages section, below.

If the consumed package is part of a module, the package import path starts with the module path, followed by path of the directory containing the package source files, relative to the root of the module. Module path is explained here. The root of the module is the directory containing the go.mod file.

.
├─ go.mod  # declares that this is module "example.com"
│
└─ a
   └─ b
      └─ c
         ├─ file1.go
         ├─ file2.go
         └─ file3.go 

The same import and usage syntax applies, the only difference being that the package import path includes the module path:

package consumer

import "example.com/a/b/c"

func F() {
  println(x.SomeValue)
}

As in the previous "package-only" example, the package name does not show anywhere in the import line. To make the code more readable, the following conversions are recommended:

Package Name

https://go.dev/blog/package-names

The package name is the identifier that follows the package keyword, in all source files of the package:

package x // the package name

...

Package names are generally given lowercase, single-word names. Good package names make code better. They should be short, concise and evocative, but not cryptic, and they should lend meaning to the names they export. Avoid long package names. The package name must comply with the Go name requirements.

The contents of a package should be consistent with the name. If you start noticing that a package includes extra logic that has no relationship to the package name, consider exporting it as a separate one, or use a more descriptive package name. Generally, you will find that the easier it is to give a short and specific self-descriptive name to a package, the better your code composition is. Package names usually take the singular form. Standard library packages bytes, errors and strings use the plural to avoid naming conflicts with the corresponding pre-declared types or keywords. Package names with underscores, hyphens or mixed caps should be avoided. Avoid upper case letters, because not all filesystems are case sensitive.

Avoid util, common and the like. These names provide clients with no sense of what the package contains. This makes it harder for clients to use the package and makes it harder for maintainers to keep the package focused. Over time, they accumulate dependencies that can make compilation significantly and unnecessarily slower, especially in large programs. And since such package names are generic, they are more likely to collide with other packages imported by client code, forcing clients to invent names to distinguish them.

Don't steal good names from the user. Avoid giving a package a name that is commonly used in client code. For example, the buffered I/O package is called bufio and not but since buf is a good variable name for a buffer.

As explained above, the package name is not required to show up in the name of any of the file system elements associated with the package, such as package directory or package source files, though code readability can be improved by using the same name for the package and its hosting directory. The name of the package and its directory should match if possible, unless you are in one of these situations.

When imported into a consuming package, the package name becomes an accessor for its contents. The names exported by the imported packages are referred with qualified identifiers, where the first part is the name of of the imported package and the second is the public feature name itself:

import "quota"

...

quota.ComputeLimit(...)

Knowing this, the exported names in the package can be chosen in such a way to avoid repetition, as described in Try to Make Package Names and Exported Names Work Together, below.

The package name can be changed by the consuming code one a per-file basis by renaming the import.

Also see:

Go Style

Conventions to Improve Code Readability

Use the Same Name for Package and Package Directory

If possible, use the same name for the package directory and package name. Using different names is legal, but using the same name will improve readability by turning this:

package consumer

import "a/b/c"

func F() {
  println(x.SomeValue)
}

into this:

package consumer

import "a/b/x" // the reader gets a hint where "x" is coming from

func F() {
  println(x.SomeValue)
}

The second version is slightly reader-friendlier, giving at least a hint where "x" is coming from.

However, there are situations when the name of the package directory must be different from the package name. There are at least three cases when this situation is justified.

  1. Packages defining an executable command. The name of the package has to be main, regardless of the name of the directory containing the command source files. If you can also name the package directory "main", that would be ideal, but that is not always possible.
  2. Some files in the directory may have the suffix _test on their package name if the file name ends in _test.go. Such a directory will define two packages: the normal one and an external test package. The _test suffix signals to go test that it must build both packages, and specifies which files belong to each packages. Also see Go Testing.
  3. Some dependency management tool append version number suffixes to package import paths.

Declare the Package Name in Import Line

If using the same name for package and package directory is not an option, the readability can be improved by using a syntactic feature of the import statement which allows explicitly specifying the package name in the import statement:

package consumer

import x "a/b/c" // unequivocally clarifies where 'x' is coming from

func F() {
  println(x.SomeValue)
}

Try to Make Package Names and Exported Names Work Together

Since each reference to the member of an imported package uses the imported package name as qualifier, the package name will provide context for its content. As such, the burden of describing the package semantics is borne equally by the package name and the member name. Design the names to work together, like in yaml.Reader. yaml.YAMLReader would be redundant. Another example is a buffered reader type in the bufio package, which is is called Reader, not BufferedReader because when imported, it will be referred to as bufio.Reader, and not with the repetitive bufio.BufferedReader.

This recommendations apply to constants, variables, function and type names.

However, this is just a guideline. In some cases, duplicating the package name in the name of the exported feature may make sense. Use your judgement.

Document Packages

For small packages, add a comment line above the package declaration describing its contents, as shown in the Declaring Packages section. This only works if the package has only one file. What to do if the package has more than one file? Where to place the documentation? For large packages, use a doc.go file.

Modularization Considerations

Packages as Namespaces

Each package defines a distinct namespace that hosts all its identifiers. Within the package namespace, all identifiers must be unique.

When a public feature of a package, such as a function or a type is used outside of the package, its name must prefixed with the name of the package, with the intention of making the package and feature name pair unique in the universe block. Such a name is referred to as qualified identifier. For example, the Println() function, exported by the fmt package, is invoked with the fmt.Println qualified identifier:

import "fmt"
...
fmt.Println("hello")

This mechanism allows us to chose short, succinct names for the members of a package, without creating conflicts with other parts of the program.

Packages as Encapsulation Mechanism

The package is the most important mechanism for encapsulation in Go programs. Packages control which names are visible outside the package, by a simple convention: all names declared in the package block or field names and method names that start with an uppercase letter are publicly visible. Publicly visible names are referred to as exported names. For more details on exporting package members, see the Exporting Package Members section below.

Encapsulation is useful because it allows the package maintainer to change the implementation of the hidden members without affecting the public interface of the package, thus allowing the package to evolve without breaking its dependents. Restricting variables' visibility constrains the package clients to access and update them only through exported functions that preserve internal invariants and enforce mutual exclusion in concurrent programs.

Dependencies

Each import declaration established a dependency from the importing package to the imported package. go build detects dependency cycles:

package main
	imports a
	imports c
	imports b
	imports a: import cycle not allowed

Declaring Packages

Packages are defined by writing one or more source files that declare association with the package. Every source file that is part of the implementation of a package must start with the package statement, which consists of the package keyword followed by the name of the package the source file belongs to.

// Package color is a package that provides functionality around colors
package color

...

All files sharing the same package name form the implementation of the package. The identifiers declared in any of the files of a package are accessible from all other files of the same package.

All source files of a package inhabit the same file system directory, referred to as package directory.

A directory may not contain source files belonging to different packages. In such a situation, the compiler raises an error:

found packages colors (colors.go) and shapes (shapes.go) in .../src/colors

It is legal to declare packages that live in different package directories but use the same package name. They can even be used together, as long as the consuming package is aware they're different packages and makes that syntactically obvious by using different aliases:

.
└─ src
    ├─ a
    │  └─ file1.go # declares a package named "x"
    ├─ b
    │  └─ file2.go # declares a different package, also named "x"
    └─ consumer
       └─ consumer.go

file1.go:

package x

var SomeValue = 10

file2.go:

package x

var SomeValue = 20

consumer.go:

package consumer

import (
  "a"
  x2 "b"
)

func F() {
  println(x.SomeValue)
  println(x2.SomeValue)
}

F() will print:

10
20

Exporting Package Members

A package identifier is made publicly visible outside of the package if:

  1. The name is declared in the package block or is a struct field or a method name.
  2. The name starts with uppercase letter.

Publicly visible names are referred to as exported names. Code that belongs to another package and that imports the package can access the exported names. Names that are not explicitly exported are not visible publicly. "Hidden" or "unexported" is also used in this situation. This is how encapsulation is implemented for packages. Note that identifiers, and not values, are exported or unexported. It is a good practice to only expose the package elements that we explicitly want other package to use, and hide everything else. This allows to change the hidden parts later, without affecting the package's consumers.

In the following example:

package color

var color string = "blue"

func Color() string { 
  return color
}

the color variable is private to the package, but it can be accessed with the public Color() function.

Just because an identifier is unexported, it does not mean other packages can't indirectly access it. A function can return the value of an unexported type and this value is accessible by any calling function, even if the calling function has been declared in a different package. This can be coupled with the short declaration operator :=, which infers the type of the variable. A variable of that type cannot be explicitly declared with var.

Embedded Type Export

Structs | Embedded Type Export

Unexported Members

A package member that is not explicitly exported becomes unexported automatically.

Building Packages

Building with go build

go build

Building with GoLand

Building with GoLand

Publishing Packages

When a package file changes, the entire package must be recompiled and all the packages that depend on it.

Publishing Locally

go install

Publishing Remotely

Consuming Packages

Import Statement

https://go.dev/ref/spec#Import_declarations

Packages are consumed with an import statement that consists in the import keyword, followed by one or more import paths. Consuming a package means accessing and using a package's exported identifiers. When a package is imported during compilation, the compiler searches the local filesystem according to the rules specified here:

Module-Aware or GOPATH Mode

Each package can be imported and used individually, so developers can import only the functionality they need. The import statement syntax follows:

import "fmt"
import (
	"bufio"
	"fmt"
	"os"
	"strings"
)

Imported packages may be grouped introducing blank lines. By conventions, the names in a group are sorted alphabetically,

Once imported, the package names are used as qualifiers in the qualified identifiers of package's exported names. The package names are inferred automatically by the compiler, but they can be locally changed by renaming imports.

Renaming Imports

The package name is only the default name for imports. It need not be unique across all source code. In case of collision, the consuming package can choose to use a different local name for the imported package. A different qualifier can be declared, providing an alias for the package name. This is called renaming an import:

import myQualifier "a/b/c"
...
println(myQualifier.SomeValue)

This feature is useful when two packages have the same name when their import paths differ. As an example, math/rand and crypto/rand have the same package name rand. In this situation, one of the imports must be renamed. Renaming only affects the importing file.

Blank Imports

It is a compilation error to import a package but not refer to it. However, there are situation when we want to import a package merely for the initialization side effects. This can be achieved using the blank identifier at import:

import _ "image/png" // this registers the PNG decoding upon import

This is known as a blank import.

Package Initialization

Package initialization begins by importing dependency packages, then initializing the package global state, which consists in all package-level constants and variables, in the order in which they are declared. This process happens recursively for each dependency package. If the package has multiple files, they are initialized in the order in which they are provided to the compiler. The go tool sorts them by name before invoking the compiler. The initialization continues with the invocation of the init() functions, if they exist.

Go Package Initialization.png

init()

Any file may contain any number of functions with the following signature:

func init() {
  ...
}

Such init() function cannot be called or referenced, but they are automatically executed in the order in which they are declared, as part of package initialization, when the package they're part of is imported. init() is called after the initialization of the package global constants and after all variable declarations in the package have evaluated their initializers. The intention behind allowing init() function is that they can set up whatever state is required. Note that init() will only run if the package is imported.

If multiple init() functions exist in a file, they will be executed in the order in which they were declared in the file.

If there are multiple init() function in a package, declared across multiple files, the files will be sorted in their alphabetical order and init() functions will be executed in that order.

If multiple packages are imported, one package is initialized at a time, the dependencies are fully initialized before the dependents are initialized, so the init() functions in dependencies are executed before the init() functions declared in dependents. Each init() function is executed only once.

The main is the last to be initialized, where the init() functions will be executed after global constant and variable initialization and before the main() function.

Package Global State

A package's global state includes the values of all constants and variables declared in the package's lexical block. We should strive to maintain little to no mutable package global state. Explicit Component Dependencies section gives at least one reason why. Given the fact that the only purpose of the init() function is to initialize package global state, using it is a serious red flag in almost any program.

Executables and the "main" Package

A binary executable is created when the Go build tool encounters a main package among the packages provided as arguments to go build or go install. In both cases, the build logic invokes the linker that assembles a self-contained executable from the main package and its dependencies. The linker uses the package as a start point and walks the package dependency graph when building the binary executable.

The main package must declare a main() function, otherwise the compiler raises an error:

runtime.main_main·f: function main is undeclared in the main package

The executable will be named differently, and placed in different locations depending on command line arguments and the value of certain environment variables when the build logic is executed. For more details see:

Single-Type Packages

Packages that expose one principal data type, plus its methods and often a New() function to create instances are referred to as single-type packages. The enclosed type is always qualified with the package name when used, so the package names should be short.

Internal (Private) Packages

The module's code that should not be imported by the module's dependents should be placed in a directory named internal, or in one of its recursive subdirectories. The packages declared under a directory named internal are prevented by the compiler to be imported by packages that live outside the source subtree in which the internal packages resides. internal is official, this pattern is enforced by the Go compiler. When the compiler sees an import of a package with internal in its path, it verifies that the package doing this import is within the directory tree rooted at the parent of the internal directory. Packages declared in internal siblings directories can import.

In the following example, the package a can import from package b, because a has the internal parent as its ancestor:

.
├── go.mod
└── level0
    ├── internal
    │   └── b
    │       └── b.go
    └── level1-1
        └── pkg
            └── a
                └── a.go

Normally, the projects use a less convoluted directory structure, where internal and pkg are siblings in the module root directory.

This is what happens in GoLang if a package tries to import from an internal directory:

Go Internal Package.png


A typical project layout employs this pattern if it makes sense. See:

Go Project internal directory

TODO:

Hierarchical (Nested) Packages

Hierarchical packages or nested packages.

When necessary, use a descriptive parent package and several children packages implementing functionality, like the standard library encoding package:

./encoding/
├── ascii85                // package ascii85
│   ├── ascii85.go
│   └── ascii85_test.go
├── asn1
│   ├── asn1.go            // package asn1
│   ├── asn1_test.go
│   ├── common.go
│   └─ ...
├── base32
│   └── ...
├── ...
├── xml
│    └── ...
│
└── encoding.go           // package encoding

Vendoring

"Vendoring" is the act of making a local copy of a third party dependency package a project depends on. This copy is traditionally placed inside the project source tree and then saved in the project repository.

The associated directory structure is described here:

Workspace

The code below a directory named "vendor" is importable only by code in the directory tree rooted at the parent of "vendor", and only using an import path that omits the prefix up to and including the vendor element. Code in vendor directories deeper in the source tree shadows code in higher directories. Within the subtree rooted at foo, an import of crash/bang resolves to foo/vendor/crash/bang, not the top-level crash/bang. Code in vendor directories is not subject to import path checking. When go get checks out or updates a Git repository, it now also updates submodules. Vendor directories do not affect the placement of new repositories being checked out for the first time by go get. Those are always placed in the main GOPATH, never in a vendor subtree.

Locating Third-Party Packages

Use:

https://pkg.go.dev/

to find packages with functionality you might find useful.

TODO