9db968f7d8c9d2c46d02dcfaab9c82eb275f9686
[csit.git] / docs / bash_code_style.rst
1 ..
2    Copyright (c) 2019 Cisco and/or its affiliates.
3    Licensed under the Apache License, Version 2.0 (the "License");
4    you may not use this file except in compliance with the License.
5    You may obtain a copy of the License at:
6 ..
7        http://www.apache.org/licenses/LICENSE-2.0
8 ..
9    Unless required by applicable law or agreed to in writing, software
10    distributed under the License is distributed on an "AS IS" BASIS,
11    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12    See the License for the specific language governing permissions and
13    limitations under the License.
14
15
16 The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
17 "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED",
18 "MAY", and "OPTIONAL" in this document are to be interpreted as
19 described in `BCP 14 <https://tools.ietf.org/html/bcp14>`_
20 `[RFC2119] <https://tools.ietf.org/html/rfc2119>`_
21 `[RFC8174] <https://tools.ietf.org/html/rfc8174>`_
22 when, and only when, they appear in all capitals, as shown here.
23
24 This document SHALL describe guidelines for writing reliable, maintainable,
25 reusable and readable code for CSIT.
26
27 Motivation
28 ^^^^^^^^^^
29
30 TODO: List reasons why we need code style document for Bash.
31
32 Proposed style
33 ^^^^^^^^^^^^^^
34
35 File types
36 ~~~~~~~~~~
37
38 Bash files SHOULD NOT be monolithic. Generally, this document
39 considers two types of bash files:
40
41 + Entry script: Assumed to be called by user,
42   or a script "external" in some way.
43
44   + Sources bash libraries and calls functions defined there.
45
46 + Library file: To be sourced by entry scipts, possibly also by other libraries.
47
48   + Sources other libraries for functions it needs.
49
50     + Or relies on a related file already having sourced that.
51
52     + Documentation SHALL imply which case it is.
53
54   + Defines multiple functions other scripts can call.
55
56 Safety
57 ~~~~~~
58
59 + Variable expansions MUST be quoted, to prevent word splitting.
60
61   + This includes special "variables" such as "${1}".
62
63     + RECOMMENDED even if the value is safe, as in "$?" and "$#".
64
65   + It is RECOMMENDED to quote strings in general,
66     so text editors can syntax-highlight them.
67
68     + Even if the string is a numeric value.
69
70     + Commands and known options can get their own highlight, no need to quote.
71
72       + Example: You do not need to quote every word of
73         "pip install --upgrade virtualenv".
74
75   + Code SHALL NOT quote glob characters you need to expand (obviously).
76
77     + OPTIONALLY do not quote adjacent characters (such as dot or fore-slash),
78       so that syntax highlighting makes them stand out compared to surrounding
79       ordinary strings.
80
81     + Example: cp "logs"/*."log" "."/
82
83     + TODO: Consider giving examples both for good and bad usage.
84
85   + Command substitution on right hand side of assignment should be safe
86     without quotes.
87
88     + But still, quotes are RECOMMENDED, unless line length is a concern.
89
90     + Note that command substitution limits the scope for quotes,
91       so it is NOT REQUIRED to escape the quotes in deeper levels.
92
93     + TODO: Do we prefer backtics, or "dollar round-bracket"?
94
95       + Backticks are more readable, especially when there are
96         round brackets in the surrounding text.
97
98       + Backticks do not allow nested command substitution.
99
100         + But we might want to avoid nested command substitution anyway?
101
102   + Code SHOULD NOT be structured in a way where
103     word splitting is intended.
104
105     + Example: Variable holding string of multiple command lines arguments.
106
107     + Solution: Array variable should be used in this case.
108
109     + Expansion MUST use quotes then: "${name[@]}".
110
111     + Word splitting MAY be used when creating arrays from command substitution.
112
113 + Code MUST always check the exit code of commands.
114
115   + Traditionally, error code checking is done either by "set -e"
116     or by appending "|| die" after each command.
117     The first is unreliable, due to many rules affecting "set -e" behavior
118     (see <https://mywiki.wooledge.org/BashFAQ/105>), but "|| die"
119     relies on humans identifying each command, which is also unreliable.
120     When was the last time you checked error code of "echo" command,
121     for example?
122
123   + As there is no reliable method of error detection, and there are two
124     largely independent unreliable methods, the best what we can do
125     is to apply both. So, code SHOULD explicitly
126     check each command (with "|| die" and similar) AND have "set -e" applied.
127
128   + Code MUST explicitly check each command, unless the command is well known,
129     and considered safe (such as the aforementioned "echo").
130
131     + The well known commands MUST still be checked implicitly via "set -e".
132
133   + See below for specific "set -e" recommendations.
134
135 + Code SHOULD use "readlink -e" (or "-f" if target does not exist yet)
136   to normalize any path value to absolute path without symlinks.
137   It helps with debugging and identifies malformed paths.
138
139 + Code SHOULD use such normalized paths for sourcing.
140
141 + When exiting on a known error, code MUST print a longer, helpful message,
142   in order for the user to fix their situation if possible.
143
144 + When error happens at an unexpected place, it is RECOMMENDED for the message
145   to be short and generic, instead of speculative.
146
147 Bash options
148 ~~~~~~~~~~~~
149
150 + Code MUST apply "-x" to make debugging easier.
151
152   + Code MAY temporarily supress such output in order to avoid spam
153     (e.g. in long busy loops), but it is still NOT RECOMMENDED to do so.
154
155 + Code MUST apply "-e" for early error detection.
156
157   + But code still SHOULD use "|| die" for most commands,
158     as "-e" has numerous rules and exceptions.
159
160   + Code MAY apply "+e" temporarily for commands which (possibly nonzero)
161     exit code it interested in.
162
163     + Code MUST to store "$?" and call "set -e" immediatelly afterwards.
164
165   + Code MAY append "|| true" for benign commands,
166     when it is clear non-zero exit codes make no difference.
167
168 + Code MUST apply "-u" as unset variable is generally a typo, thus an error.
169
170   + Code MAY temporarily apply "+u" if a command needs that to pass.
171
172     + Virtualenv activation is the only known example so far.
173
174 + Code MUST apply "-o pipefail" to make sure "-e" picks errors
175   inside piped construct.
176
177   + Code MAY use "|| true" inside a pipe construct, in the (inprobable) case
178     when non-zero exit code still results in a meaningful pipe output.
179
180 + All together: "set -exuo pipefail".
181
182   + Code MUST put that line near start of every file, so we are sure
183     the options are applied no matter what.
184
185     + "Near start" means "before any nontrivial code".
186
187     + Basically only copyright and long high-level comments are
188       RECOMMENDED to appear before.
189
190   + Also code MUST put the line near start of function bodies
191     and subshell invocations.
192
193 Functions
194 ~~~~~~~~~
195
196 There are (at least) two possibilities how a code from an external file
197 can be executed. Either the file contains a code block to execute
198 on each "source" invocation, or the file just defines functions
199 which have to be called separately.
200
201 This document considers the "function way" to be better,
202 here are some pros and cons:
203
204 + Cons:
205
206   + The function way takes more space. Files have more lines,
207     and the code in function body is one indent deeper.
208
209   + It is not easy to create functions for low-level argument manipulation,
210     as "shift" command in the function code does not affect the caller context.
211
212   + Call sites frequently refer to code two times,
213     when sourcing the definition and when executing the function.
214
215   + It is not clear when a library can rely on its relative
216     to have performed the sourcing already.
217
218   + Ideally, each library should detect if it has been sourced already
219     and return early, which takes even more space.
220
221 + Pros:
222
223   + Some code blocks are more useful when used as function,
224     to make call site shorter.
225
226     + Examples: Trap functions, "die" function.
227
228   + The "import" part and "function" part usually have different side effects,
229     making the documentation more focused (even if longer overall).
230
231   + There is zero risk of argument-less invocation picking arguments
232     from parent context.
233
234     + This safety feature is the main reason for chosing the "function way".
235
236     + This allows code blocks to support optional arguments.
237
238 TODO: Translate the "function way" into list of rules.
239
240 Variables
241 ~~~~~~~~~
242
243 This document describes two kinds of variables: called "local" and "global".
244
245 TODO: Find better adjectives for the two kinds defined here,
246 if the usual bash meaning makes reader forget other specifics.
247 For example, variable with lowercase name and not marked by "local" builtin,
248 is cosidered "global" from bash point of view, but "local" from this document
249 point of view.
250
251 + Local variables:
252
253   + Variable name MUST contain only lower case letters, digits and underscores.
254
255   + Code MUST NOT export local variables.
256
257   + Code MUST NOT rely on local variables set in different contexts.
258
259   + Documentation is NOT REQUIRED.
260
261     + Variable name SHOULD be descriptive enough.
262
263   + Local variable MUST be initialized before first use.
264
265     + Code SHOULD have a comment if a reader might have missed
266       the initialization.
267
268   + TODO: Agree on level of defensiveness (against local values
269     being influenced by other functions) needed.
270     Possible alternatives / additions to the "always initialize" rule:
271
272     + Unset local variables when leaving the function.
273
274     + Explicitly typeset by "local" builtin command.
275
276     + Require strict naming convention, e.g. function_name__variable_name.
277
278 + Global variables:
279
280   + Variable name MUST contain only upper case letters, digits and underscores.
281
282   + They SHOULD NOT be exported, unless external commands need them
283     (e.g. PYTHONPATH).
284
285   + TODO: Propose a strict naming convention, or a central document
286     of all used global variables, to prevent contributors
287     from causing variable name conflicts.
288
289   + Code MUST document if a function (or its inner call)
290     reads a global variable.
291
292   + Code MUST document if a function (or its inner call)
293     sets or rewrites a global variable.
294
295   + If a function "wants to return a value", it SHOULD be implemented
296     as the function setting (or rewriting) a global variable,
297     and the call sites reading that variable.
298
299   + If a function "wants to accept an argument", it IS RECOMMENDED
300     to be implemented as the call sites setting or rewriting global variables,
301     and the function reading that variables.
302     But see below for direct arguments.
303
304 + Code MUST use curly brackets when referencing variables,
305   e.g. "${my_variable}".
306
307   + It makes related constructs (such as ${name:-default}) less surprising.
308
309   + It looks more similar to Robot Framework variables (which is good).
310
311 Arguments
312 ~~~~~~~~~
313
314 Bash scripts and functions MAY accept arguments, named "${1}", "${2}" and so on.
315 As a whole available via "$@".
316 You MAY use "shift" command to consume an argument.
317
318 Contexts
319 ````````
320
321 Functions never have access to parent arguments, but they can read and write
322 variables set or read by parent contexts.
323
324 Arguments or variables
325 ``````````````````````
326
327 + Both arguments and global variables MAY act as an input.
328
329 + In general, if the caller is likely to supply the value already placed
330   in a global variable of known name, it is RECOMMENDED
331   to use that global variable.
332
333 + Construct "${NAME:-value}" can be used equally well for arguments,
334   so default values are possible for both input methods.
335
336 + Arguments are positional, so there are restrictions on which input
337   is optional.
338
339 + Functions SHOULD either look at arguments (possibly also
340   reading global variables to use as defaults), or look at variables only.
341
342 + Code MUST NOT rely on "${0}", it SHOULD use "${BASH_SOURCE[0]}" instead
343   (and apply "readlink -e") to get the current block location.
344
345 + For entry scripts, it is RECOMMENDED to use standard parsing capabilities.
346
347   + For most Linux distros, "getopt" is RECOMMENDED.
348
349 Function size
350 ~~~~~~~~~~~~~
351
352 + In general, code SHOULD follow reasoning similar to how pylint
353   limits code complexity.
354
355 + It is RECOMMENDED to have functions somewhat simpler than Python functions,
356   as Bash is generally more verbose and less readable.
357
358 + If code contains comments in order to partition a block
359   into sub-blocks, the sub-blocks SHOULD be moved into separate functions.
360
361   + Unless the sub-blocks are essentially one-liners,
362     not readable just because external commands do not have
363     obvious enough parameters. Use common sense.
364
365 Documentation
366 ~~~~~~~~~~~~~
367
368 + The library path and filename is visible from source sites. It SHOULD be
369   descriptive enough, so reader do not need to look inside to determine
370   how and why is the sourced file used.
371
372   + If code would use several functions with similar names,
373     it is RECOMMENDED to create a (well-named) sub-library for them.
374
375   + Code MAY create deep library trees if needed, it SHOULD store
376     common path prefixes into global variables to make sourcing easier.
377
378   + Contributors, look at other files in the subdirectory. You SHOULD
379     improve their filenames when adding-removing other filenames.
380
381   + Library files SHOULD NOT have executable flag set.
382
383   + Library files SHOULD have an extension .sh (or perhaps .bash).
384
385   + It is RECOMMENDED for entry scripts to also have executable flag unset
386     and have .sh extension.
387
388 + Each entry script MUST start with a shebang.
389
390   + "#!/bin/usr/env bash" is RECOMMENDED.
391
392   + Code SHOULD put an empty line after shebang.
393
394   + Library files SHOULD NOT contain a shebang, as "source" is the primary
395     method to include them.
396
397 + Following that, there SHOULD be a block of comment lines with copyright.
398
399   + It is a boilerplate, but human eyes are good at ignoring it.
400
401   + Overhead for git is also negligible.
402
403 + Following that, there MUST be "set -exuo pipefail".
404
405   + It acts as an anchor for humans to start paying attention.
406
407 Then it depends on script type.
408
409 Library documentation
410 `````````````````````
411
412 + Following "set -exuo pipefail" SHALL come the "import part" documentation.
413
414 + Then SHALL be the import code
415   ("source" commands and a bare minimum they need).
416
417 + Then SHALL be the function definitions, and inside:
418
419   + "set -exuo pipefail" again.
420
421   + Following that SHALL be the function documentation explaining API contract.
422     Similar to Robot [Documentation] or Python function-level docstring.
423
424     + See below.
425
426   + Following that SHALL be varius TODOs, FIXMEs and code itself.
427
428     + "Code itself" SHALL include comment lines
429       explaining any non-obvious logic.
430
431     + TODO: Document (in an appropriate place) how TODOs differ from FIXMEs.
432
433   + There SHALL be two empty lines before next function definition.
434
435 More details on function documentation:
436
437 Generally, code SHOULD use comments to explain anything
438 not obvious from the funtion name.
439
440 + Function documentation SHOULD start with short description of function
441   operation or motivation, but only if not obvious from function name.
442
443 + Documentation SHOULD continue with listing any non-obvious side effect:
444
445   + Documentation MUST list all read global variables.
446
447     + Documentation SHOULD include descriptions of semantics
448       of global variable values.
449       It is RECOMMENDED to mention which function is supposed to set them.
450
451     + The "include descriptions" part SHOULD apply to other items as well.
452
453   + Documentation MUST list all global variables set, unset, reset,
454     or otherwise updated.
455
456   + It is RECOMMENDED to list all hardcoded values used in code.
457
458     + Not critical, but can hint at future improvements.
459
460   + Documentation MUST list all files or directories read
461     (so caller can make sure their content is ready).
462
463   + Documentation MUST list all files or directories updated
464     (created, deleted, emptied, otherwise edited).
465
466   + Documentation SHOULD list all functions called (so reader can look them up).
467
468     + Documentation SHOULD mention where are the functions defined,
469       if not in the current file.
470
471   + Documentation SHOULD list all external commands executed.
472
473     + Because their behavior can change "out of bounds", meaning
474       the contributor changing the implementation of the extrenal command
475       can be unaware of this particular function interested in its side effects.
476
477   + Documentation SHOULD explain exit code (coming from
478     the last executed command).
479
480     + Usually, most functions SHOULD be "pass or die",
481       but some callers MAY be interested in nonzero exit codes
482       without using global variables to store them.
483
484     + Remember, "exit 1" ends not only the function, but all scripts
485       in the source chain, so code MUST NOT use it for other purposes.
486
487       + Code SHOULD call "die" function instead. This way the caller can
488         redefine that function, if there is a good reason for not exiting
489         on function failure.
490
491   + TODO: Programs installed, services started, URLs downloaded from, ...
492
493   + TODO: Add more items when you spot them.
494
495   + TODO: Is the current order recommended?
496
497 Entry script documentation
498 ``````````````````````````
499
500 + After "set -exuo pipefail", high-level description SHALL come.
501
502   + Then TODOs and FIXMEs SHALL be placed (if any).
503
504   + Entry scripts are rarely reused, so detailed side effects
505     are OPTIONAL to document.
506
507   + But code SHOULD document the primary side effects.
508
509 + Then SHALL come few commented lines to import the library with "die" function.
510
511 + Then block of "source" commands for sourcing other libraries needed SHALL be.
512
513   + In alphabetical order, any "special" library SHOULD be
514     in the previous block (for "die").
515
516 + Then block os commands processing arguments SHOULD be (if needed).
517
518 + Then SHALL come block of function calls (with parameters as needed).
519
520 Other general recommendations
521 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
522
523 + Code SHOULD NOT not repeat itself, even in documentation:
524
525   + For hardcoded values, a general description SHOULD be written
526     (instead of copying the value), so when someone edits the value
527     in the code, the description still applies.
528
529   + If affected directory name is taken from a global variable,
530     documentation MAY distribute the directory description
531     over the two items.
532
533   + If most of side effects come from an inner call,
534     documentation MAY point the reader to the documentation
535     of the called function (instead of listing all the side effects).
536
537     + TODO: Composite functions can have large effects. Should we require
538       intermediate functions to actively hide them whenever possible?
539
540 + But documentation SHOULD repeat it if the information crosses functions.
541
542   + Item description MUST NOT be skipped just because the reader
543     should have read parent/child documentation already.
544
545   + Frequently it is RECOMMENDED to copy&paste item descriptions
546     between functions.
547
548   + But sometimes it is RECOMMENDED to vary the descriptions. For example:
549
550     + A global variable setter MAY document how does it figure out the value
551       (without caring about what it will be used for by other functions).
552
553     + A global variable reader MAY document how does it use the value
554       (without caring about how has it been figured out by the setter).
555
556 + When possible, Bash code SHOULD be made to look like Python
557   (or Robot Framework). Those are three primary languages CSIT code relies on,
558   so it is nicer for the readers to see similar expressions when possible.
559   Examples:
560
561   + Code MUST use indentation, 1 level is 4 spaces.
562
563   + Code SHOULD use "if" instead of "&&" constructs.
564
565   + For comparisons, code SHOULD use operators such as "!=" (needs "[[").
566
567 + Code MUST NOT use more than 80 characters per line.
568
569   + If long external command invocations are needed,
570     code SHOULD use array variables to shorten them.
571
572   + If long strings (or arrays) are needed, code SHOULD use "+=" operator
573     to grow the value over multiple lines.
574
575   + If "|| die" does not fit with the command, code SHOULD use curly braces:
576
577     + Current line has "|| {",
578
579     + Next line has the die commands (indented one level deeper),
580
581     + Final line closes with "}" at original intent level.
582
583   + TODO: Recommend what to do with other constructs.
584
585     + For example multiple piped commands.
586
587     + No, "eval" is too unsafe to use.