Text file talks/2014/organizeio.slide

     1  Organizing Go code
     2  
     3  David Crawshaw
     4  crawshaw@golang.org
     5  
     6  
     7  * Packages
     8  
     9  * Go programs are made up of packages
    10  
    11  All Go source is part of a package.
    12  Every file begins with a package statement.
    13  Programs start in package main.
    14  
    15  .play organizeio/hello.go
    16  
    17  For very small programs, `main` is the only package you need to write.
    18  
    19  The hello world program _imports_ package `fmt`.
    20  
    21  The function `Println` is defined in the fmt package.
    22  
    23  * An example package: fmt
    24  
    25  	// Package fmt implements formatted I/O.
    26  	package fmt
    27  
    28  	// Println formats using the default formats for its
    29  	// operands and writes to standard output.
    30  	func Println(a ...interface{}) (n int, err error) {
    31  		...
    32  	}
    33  
    34  	func newPrinter() *pp {
    35  		...
    36  	}
    37  
    38  The `Println` function is _exported_. It starts with an upper case
    39  letter, which means other packages are allowed to call it.
    40  
    41  The `newPrinter` function is _unexported_. It starts with a lower
    42  case letter, so it can only be used inside the fmt package.
    43  
    44  * The shape of a package
    45  
    46  Packages collect related code.
    47  
    48  They can be big or small,
    49  and may be spread across multiple files.
    50  
    51  All the files in a package live in a single directory.
    52  
    53  The `net/http` package exports more than 100 names. (18 files)
    54  The `errors` package exports just one. (1 file)
    55  
    56  * The name of a package
    57  
    58  Keep package names short and meaningful.
    59  Don't use underscores, they make package names long.
    60  
    61  - `io/ioutil` not `io/util`
    62  - `suffixarray` not `suffix_array`
    63  
    64  Don't overgeneralize. A `util` package could be anything.
    65  
    66  The name of a package is part of its type and function names.
    67  On its own, type `Buffer` is ambiguous. But users see:
    68  
    69  	buf := new(bytes.Buffer)
    70  
    71  Choose package names carefully.
    72  
    73  Choose good names for users.
    74  
    75  * The testing of a package
    76  
    77  Tests are distinguished by file name. Test files end in `_test.go`.
    78  
    79  	package fmt
    80  
    81  	import "testing"
    82  
    83  	var fmtTests = []fmtTest{
    84  		{"%d", 12345, "12345"},
    85  		{"%v", 12345, "12345"},
    86  		{"%t", true, "true"},
    87  	}
    88  
    89  	func TestSprintf(t *testing.T) {
    90  		for _, tt := range fmtTests {
    91  			if s := Sprintf(tt.fmt, tt.val); s != tt.out {
    92  				t.Errorf("...")
    93  			}
    94  		}
    95  	}
    96  
    97  Test well.
    98  
    99  * Code organization
   100  
   101  * Introducing workspaces
   102  
   103  Your Go code is kept in a _workspace_.
   104  
   105  A workspace contains _many_ source repositories (git, hg).
   106  
   107  The Go tool understands the layout of a workspace.
   108  You don't need a `Makefile`. The file layout is everything.
   109  
   110  Change the file layout, change the build.
   111  
   112  	$GOPATH/
   113  		src/
   114  			github.com/user/repo/
   115  				mypkg/
   116  					mysrc1.go
   117  					mysrc2.go
   118  				cmd/mycmd/
   119  					main.go
   120  		bin/
   121  			mycmd
   122  
   123  
   124  * Let's make a workspace
   125  
   126  	mkdir /tmp/gows
   127  	GOPATH=/tmp/gows
   128  
   129  The `GOPATH` environment variable tells the Go tool where your workspace is located.
   130  
   131  	go get github.com/dsymonds/fixhub/cmd/fixhub
   132  
   133  The `go` `get` command fetches source repositories from the internet and places them in your workspace.
   134  
   135  Package paths matter to the Go tool. Using "github.com/..."
   136  means the tool knows how to fetch your repository.
   137  
   138  	go install github.com/dsymonds/fixhub/cmd/fixhub
   139  
   140  The go install command builds a binary and places it in `$GOPATH/bin/fixhub`.
   141  
   142  * Our workspace
   143  
   144  	$GOPATH/
   145  		bin/fixhub                              # installed binary
   146  		pkg/darwin_amd64/                       # compiled archives
   147  			code.google.com/p/goauth2/oauth.a
   148  			github.com/...
   149  		src/                                    # source repositories
   150  			code.google.com/p/goauth2/
   151  				.hg
   152  				oauth                           # used by package go-github
   153  				...
   154  			github.com/
   155  				golang/lint/...                 # used by package fixhub
   156  					.git
   157  				google/go-github/...            # used by package fixhub
   158  					.git
   159  				dsymonds/fixhub/
   160  					.git
   161  					client.go
   162  					cmd/fixhub/fixhub.go        # package main
   163  
   164  `go` `get` fetched many repositories.
   165  `go` `install` built a binary out of them.
   166  
   167  
   168  * Why prescribe file layout?
   169  
   170  Using file layout for builds means less configuration.
   171  In fact, it means no configuration.
   172  No `Makefile`, no `build.xml`.
   173  
   174  Less time configuring means more time programming.
   175  
   176  Everyone in the community uses the same layout.
   177  This makes it easier to share code.
   178  
   179  The Go tool helps build the Go community.
   180  
   181  * Where's your workspace?
   182  
   183  It is possible to have multiple workspaces, but most people just use one.
   184  
   185  So where do you point your `GOPATH`? A common preference:
   186  
   187  .image organizeio/home.png
   188  
   189  This puts `src`, `bin`, and `pkg` directories in your home directory.
   190  
   191  (Convenient, because `$HOME/bin` is probably already in your `PATH`.)
   192  
   193  
   194  * Working with workspaces
   195  
   196  Unix eschews typing:
   197  
   198  	CDPATH=$GOPATH/src/github.com:$GOPATH/src/code.google.com/p
   199  
   200  	$ cd dsymonds/fixhub
   201  	/tmp/gows/src/github.com/dsymonds/fixhub
   202  	$ cd goauth2
   203  	/tmp/gows/src/code.google.com/p/goauth2
   204  	$
   205  
   206  A shell function for your `~/.profile`:
   207  
   208  	gocd () { cd `go list -f '{{.Dir}}' $1` }
   209  
   210  This lets you move around using the Go tool's path names:
   211  
   212  	$ gocd .../lint
   213  	/tmp/gows/src/github.com/golang/lint
   214  	$
   215  
   216  
   217  * The Go tool's many talents
   218  
   219  	$ go help
   220  	Go is a tool for managing Go source code.
   221  
   222  	Usage:
   223  
   224  		go command [arguments]
   225  
   226  	The commands are:
   227  
   228  Worth exploring! Some highlights:
   229  
   230  	    build       compile packages and dependencies
   231  	    get         download and install packages and dependencies
   232  	    install     compile and install packages and dependencies
   233  	    test        test packages
   234  
   235  There are more useful subcommands. Check out `vet` and `fmt`.
   236  
   237  * Dependency management
   238  
   239  * In production, versions matter.
   240  
   241  `go` `get` always fetches the latest code, even if your build breaks.
   242  
   243  .image organizeio/gogetversion.png
   244  
   245  That's fine when developing. It's not fine when releasing.
   246  We need other tools.
   247  
   248  * Versioning
   249  
   250  My favorite technique: vendoring.
   251  
   252  For building binaries, import the packages you care about
   253  into a `_vendor` workspace.
   254  
   255  	GOPATH=/tmp/gows/_vendor:/tmp/gows
   256  
   257  For building libraries, import the packages you care about
   258  into your repository. Rename the imports to:
   259  
   260  	import "github.com/you/proj/vendor/github.com/them/lib"
   261  
   262  Long paths, but trivial to automate. Write a Go program!
   263  
   264  Another technique: [[http://gopkg.in][gopkg.in]], provides versioned package paths:
   265  
   266  	gopkg.in/user/pkg.v3 -> github.com/user/pkg (branch/tag v3, v3.N, or v.3.N.M)
   267  
   268  * Naming
   269  
   270  * Names matter
   271  
   272  Programs are full of names. Names have costs and benefits.
   273  
   274  *Costs*: *space* *and* *time*
   275  Names need to be in short term memory when reading code.
   276  You can only fit so many. Longer names take up more space.
   277  
   278  *Benefits:* *information*
   279  A good name is not only a referent, it conveys information.
   280  
   281  Use the shortest name that carries the right amount of information in its context.
   282  
   283  Devote time to naming.
   284  
   285  * Name style
   286  
   287  Use `camelCase`, `not_underscores`.
   288  Local variable names should be short, typically one or two characters.
   289  
   290  Package names are usually one lowercase word.
   291  
   292  Global variables should have longer names.
   293  
   294  Don't stutter.
   295  
   296  - `bytes.Buffer` not `bytes.ByteBuffer`
   297  - `zip.Reader` not `zip.ZipReader`
   298  - `errors.New` not `errors.NewError`
   299  - `r` not `bytesReader`
   300  - `i` not `loopIterator`
   301  
   302  * Doc comments
   303  
   304  Doc comments precede the declaration of an exported identifier:
   305  
   306          // Join concatenates the elements of elem to create a single string.
   307          // The separator string sep is placed between elements in the resulting string.
   308          func Join(elem []string, sep string) string {
   309  
   310  The godoc tool extracts such comments and presents them on the web:
   311  
   312  .image organizeio/godoc.png
   313  
   314  * Writing doc comments
   315  
   316  Doc comments should be English sentences and paragraphs.
   317  They use no special formatting beyond indentation for preformatted text.
   318  
   319  Doc comments should begin with the noun they describe.
   320  
   321  	// Join concatenates… 		good
   322  	// This function… 			bad
   323  
   324  Package docs go above the package declaration:
   325  
   326  	// Package fmt…
   327  	package fmt
   328  
   329  Read the world's Go docs on [[https://pkg.go.dev]]. E.g.
   330  
   331  [[https://pkg.go.dev/golang.org/x/tools/txtar]]
   332  
   333  * Questions?
   334  

View as plain text