github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/doc/contributions/style-guide.md (about) 1 # Style Guide 2 3 This document is a guide to aid juju-core reviewers. 4 5 ## Contents 6 7 1. [Pull Requests](#pull-requests) 8 2. [Comments](#comments) 9 3. [Functions](#functions) 10 4. [Errors](#errors) 11 5. [Tests](#tests) 12 6. [API](#api) 13 14 15 ## Pull Requests 16 17 A Pull Request (PR) needs a justification. This could be: 18 19 - An issue, ie “Fixes LP 12349000” 20 - A description justifying the change 21 - A link to a design document or mailing list discussion 22 23 PRs should be kept as small as possible, addressing one discrete issue outlined 24 in the PR justification. The smaller the diff, the better. 25 26 If your PR addresses several distinct issues, please split the proposal into one 27 PR per issue. 28 29 Once your PR is up and reviewed, don't comment on the review saying "i've done this", 30 unless you've pushed the code. 31 32 ## Comments 33 34 Comments should be full sentences with one space after a period, proper 35 capitalization and punctuation. 36 37 Avoid adding non-useful information, such as flagging the layout of a file with 38 block comments or explaining behaviour that is immediately evident from reading 39 the code. 40 41 All exported symbols (names, functions, methods, types, constants and variables) 42 must have a comment. 43 44 Anything that is unintuitive at first sight must have a comment, such as: 45 46 - non-trivial unexported type or function declarations (e.g. if a type implements an interface) 47 - the breaking of a convention (e.g. not handling an error) 48 - a particular behaviour the reader would have to think 'more deeply' about to understand 49 50 Top-level name comments start with the name of the thing. For example, a top-level, 51 exported function: 52 53 ```go 54 // AwesomeFunc does awesome stuff. 55 func AwesomeFunc(i int) (string, error) { 56 // ... 57 return someString, err 58 } 59 ``` 60 61 A TODO comment needs to be linked to a bug in Launchpad. Include the lp bug number in 62 the TODO with the following format: 63 64 ```go 65 // TODO(username) yyyy-mm-dd bug #1234567 66 ``` 67 68 e.g. 69 70 ```go 71 // TODO(axw) 2013-09-24 bug #1229507 72 ``` 73 74 Each file must have a copyright notice. The copyright notice must include the year 75 the file was created and the year the file was last updated, if applicable. 76 77 ```go 78 // Copyright 2013-2014 Canonical Ltd. 79 // Licensed under the AGPLv3, see LICENCE file for details. 80 81 package main 82 ``` 83 84 Note that the blank line following the notice separates the comment from the package, 85 ensuring it does not appear in godoc. 86 87 ## Functions 88 89 What the function does should be evident by its signature. 90 91 How a function does what it does should also be clear. If a function is large 92 and verbose, breaking apart it's business logic into helper functions can make 93 it easier to read. Conversely, if the function's logic is too opaque, 94 in-lining a helper function or variable may help. 95 96 If a function has named return values in it's signature, it should use a 97 bare return: 98 99 ```go 100 func AwesomeFunc(i int) (s string, err error) { 101 // ... 102 return // Don't use the statement: "return s, err" 103 } 104 ``` 105 106 ## Errors 107 108 The text of error messages should be lower case without a trailing period, 109 because they are often included in other strings or output: 110 111 ```go 112 // INCORRECT 113 return fmt.Errorf("Cannot read config %q.", configFilePath) 114 115 // CORRECT 116 return fmt.Errorf("cannot read agent config %q", configFilePath) 117 ``` 118 119 If a function call only returns an error, assign and handle the error 120 within one if statement (unless doing so makes the code harder to read, 121 for example if the line of the function call is already quite long): 122 123 ```go 124 // PREFERRED 125 if err := someFunc(); err != nil { 126 return err 127 } 128 ``` 129 130 If an error is thrown away, why it is not handled is explained in a comment. 131 132 The juju/errors package should be used to handle errors: 133 134 ```go 135 // Trace always returns an annotated error. Trace records the 136 // location of the Trace call, and adds it to the annotation stack. 137 // If the error argument is nil, the result of Trace is nil. 138 if err := SomeFunc(); err != nil { 139 return errors.Trace(err) 140 } 141 142 // Errorf creates a new annotated error and records the location that the 143 // error is created. This should be a drop in replacement for fmt.Errorf. 144 return errors.Errorf("validation failed: %s", message) 145 146 // Annotate is used to add extra context to an existing error. The location of 147 // the Annotate call is recorded with the annotations. The file, line and 148 // function are also recorded. 149 // If the error argument is nil, the result of Annotate is nil. 150 if err := SomeFunc(); err != nil { 151 return errors.Annotate(err, "failed to frombulate") 152 } 153 ``` 154 155 See [github.com/juju/errors/annotation.go](github.com/juju/errors/annotation.go) for more error handling functions. 156 157 ## Tests 158 159 - Please read ../doc/how-to-write-tests.txt 160 - Each test should test a discrete behaviour and is idempotent 161 - The behaviour being tested is clear from the function name, as well as the 162 expected result (i.e. success or failure) 163 - Repeated boilerplate in test functions is extracted into it's own 164 helper function or `SetUpTest` 165 - External functionality, that is not being directly tested, should be mocked out 166 167 Variables in other modules are not patched directly. Instead, create a local 168 variable and patch that: 169 170 ```go 171 // ---------- 172 // in main.go 173 // ---------- 174 175 import somepackage 176 177 var someVar = somepackage.SomeVar 178 179 // --------------- 180 // in main_test.go 181 // --------------- 182 183 func (s *SomeSuite) TestSomethingc(c *gc.C) { 184 s.PatchValue(someVar, newValue) 185 // .... 186 } 187 ``` 188 189 If your test functions are under `package_test` and they need to test something 190 from package that is not exported, create an exported alias to it in `export_test.go`: 191 192 ```go 193 // ----------- 194 // in tools.go 195 // ----------- 196 197 package tools 198 199 var someVar = value 200 201 // ----------------- 202 // in export_test.go 203 // ----------------- 204 205 package tools 206 207 var SomeVar = someVar 208 209 // --------------- 210 // in main_test.go 211 // --------------- 212 213 package tools_test 214 215 import tools 216 217 func (s *SomeSuite) TestSomethingc(c *gc.C) { 218 s.PatchValue(tools.SomeVar, newValue) 219 // ... 220 } 221 ``` 222 223 ## Layout 224 225 Imports are grouped into 3 sections: standard library, 3rd party libraries, juju/juju library: 226 227 ```go 228 import ( 229 "fmt" 230 "io" 231 232 "github.com/juju/errors" 233 "github.com/juju/loggo" 234 gc "gopkg.in/check.v1" 235 236 "github.com/juju/juju/environs" 237 "github.com/juju/juju/environs/config" 238 ) 239 ``` 240 241 ## API 242 243 - Please read ../doc/api.txt 244 - Client calls to the API are, by default, batch calls