... | ... | @@ -2,16 +2,16 @@ This page documents some Stork coding guidelines. |
|
|
|
|
|
[[_TOC_]]
|
|
|
|
|
|
Refer also to the [Kea coding guidelines](https://gitlab.isc.org/isc-projects/kea/wikis/Processes/coding-guidelines) and [BIND best practices](https://gitlab.isc.org/isc-projects/bind9/wikis/best-practices). They should be used where they do not conflict with the guidelines on this page. This is because we expect some ISC developers work on both versions of code, and in that case it's easier to maintain the code if the styles are as compatible as possible.
|
|
|
Some of the styles derived from BIND 9 that are often forgotten or misunderstood are explicitly mentioned below. Having said that, Stork is a project with radically different environment. We don't need to stick to old rules invented for C code in the 1990s.
|
|
|
Refer also to the [Kea coding guidelines](https://gitlab.isc.org/isc-projects/kea/wikis/Processes/coding-guidelines) and [BIND best practices](https://gitlab.isc.org/isc-projects/bind9/wikis/best-practices). They should be used where they do not conflict with the guidelines on this page. This is because we expect some ISC developers to work on both versions of code, and in that case it's easier to maintain the code if the styles are as compatible as possible.
|
|
|
Some of the styles derived from BIND 9 that are often forgotten or misunderstood are explicitly mentioned below. Having said that, Stork is a project with a radically different environment. We don't need to stick to old rules invented for C code in the 1990s.
|
|
|
|
|
|
# Test-Driven Development
|
|
|
|
|
|
We use [test-driven development](https://en.wikipedia.org/wiki/Test-driven_development) in the Stork project. We require writing unit tests together with the code. The developers should write the unit tests before the implementation and make sure the tests initially fail. Adding the implementation should cause the tests to pass. Writing the tests after the implementation poses a risk that the tests do not correctly validate the implementation. In some cases the tests can always pass, even when the implementation doesn't exist yet.
|
|
|
We use [test-driven development](https://en.wikipedia.org/wiki/Test-driven_development) in the Stork project. We require writing unit tests together with the code. The developers should write the unit tests before the implementation and make sure the tests initially fail. Adding the implementation should cause the tests to pass. Writing the tests after the implementation poses a risk that the tests do not correctly validate the implementation. In some cases, the tests can always pass, even when the implementation doesn't exist yet.
|
|
|
|
|
|
The unit tests should cover all functions, and our linters report errors when they detect missing coverage. However, it is essential to focus on testing all cases of complex functions rather than writing many tests for trivial functions. If a function is a trivial getter it probably doesn't even require a dedicated test. It suffices if the getter function is called in some other tests to get the value it returns.
|
|
|
|
|
|
There are cases when it is hard to write tests for some functions. For example, writing a unit test for a `main()` function may be difficult. It may also be difficult to write a unit test for a function that relies on a specific configuration of the host machine (e.g., existence of some network interfaces). In legitimate cases, it is ok not to write a unit tests for such functions. Testing them should be covered in the system tests instead.
|
|
|
There are cases when it is hard to write tests for some functions. For example, writing a unit test for a `main()` function may be difficult. It may also be difficult to write a unit test for a function that relies on a specific configuration of the host machine (e.g., existence of some network interfaces). In legitimate cases, it is ok not to write unit tests for such functions. Testing them should be covered in the system tests instead.
|
|
|
|
|
|
If writing a unit test is hard or impossible, a developer should consider that the code may have a bad design. Restructuring the code can sometimes allow testing at least selected parts.
|
|
|
|
... | ... | @@ -19,7 +19,7 @@ If writing a unit test is hard or impossible, a developer should consider that t |
|
|
|
|
|
## Line length
|
|
|
|
|
|
The project kicked off in 2019. FullHD is ubiquitous with large 4K displays are getting common. In the general case, the code should have no more than 128 columns. This is let developers display two panels side by side. In some exceptional cases (such as URL), the code may be extended to 160 columns, but this should be rare occurrence.
|
|
|
The project kicked off in 2019. FullHD is ubiquitous with large 4K displays are getting common. In the general case, the code should have no more than 128 columns. This is let developers display two panels side by side. In some exceptional cases (such as URL), the code may be extended to 160 columns, but this should be a rare occurrence.
|
|
|
|
|
|
## User Interface (UI) Guidelines
|
|
|
|
... | ... | @@ -45,13 +45,13 @@ they are not part of the Stork's coding guidelines. |
|
|
|
|
|
## Testing/Documentation addresses and prefixes
|
|
|
|
|
|
Use `192.0.2.0/24` (see [RFC5737](https://tools.ietf.org/html/rfc5737) and `2001:db8::/32` (see [RFC3849](https://tools.ietf.org/html/rfc3849) for purposes like addresses used in test cases or examples in documentation. Likewise, use reserved example domain names such as `example.com`, `.test`, `.example`, etc for domain names used in these cases (see [RFC2606](https://tools.ietf.org/html/rfc2606)). They are reserved by specifications and should be the safest in terms of collision avoidance.
|
|
|
Use `192.0.2.0/24` (see [RFC5737](https://tools.ietf.org/html/rfc5737) and `2001:db8::/32` (see [RFC3849](https://tools.ietf.org/html/rfc3849) for purposes like addresses used in test cases or examples in the documentation. Likewise, use reserved example domain names such as `example.com`, `.test`, `.example`, etc for domain names used in these cases (see [RFC2606](https://tools.ietf.org/html/rfc2606)). They are reserved by specifications and should be the safest in terms of collision avoidance.
|
|
|
|
|
|
## TODO Comments
|
|
|
|
|
|
We sprinkle comments in code with keywords to indicate pending work.
|
|
|
|
|
|
In Stork, `TODO` is preferred. If there is a corresponding ticket, feel free to specify its number in the comment. Unless other wise specified, issue #1234 means a ticket in the Gitlab, available at http://gitlab.isc.org/isc-projects/stork.
|
|
|
In Stork, `TODO` is preferred. If there is a corresponding ticket, feel free to specify its number in the comment. Unless otherwise specified, issue #1234 means a ticket in Gitlab, available at http://gitlab.isc.org/isc-projects/stork.
|
|
|
|
|
|
## Function/method comments
|
|
|
|
... | ... | @@ -59,7 +59,7 @@ In Stork, `TODO` is preferred. If there is a corresponding ticket, feel free to |
|
|
|
|
|
All exported functions MUST be documented. It is strongly recommended to document unexported functions, too, unless the function is trivial and its behavior is obvious without such documentation.
|
|
|
|
|
|
Always describe what the function is doing. If the function has a small number of parameters, and their purpose stems from the function description, there is no need to describe the parameters. However, complex functions with many parameters SHOULD include parameters descriptions highlighting their purpose, if they are mandatory, allowed ranges, and other helpful information.
|
|
|
Always describe what the function is doing. If the function has a small number of parameters, and their purpose stems from the function description, there is no need to describe the parameters. However, complex functions with many parameters SHOULD include parameters' descriptions highlighting their purpose, if they are mandatory, allowed ranges, and other helpful information.
|
|
|
|
|
|
While it's appreciated, the documentation doesn't have to be very thorough. Well expressed sentence is often enough. Make sure the description is as useful as possible. "Returns URL string" is bad. "Returns agent's contact point URL as a string" is much better.
|
|
|
|
... | ... | @@ -89,7 +89,7 @@ We use Python for system tests. Python-specific coding guidelines are currently |
|
|
|
|
|
## User's guide
|
|
|
|
|
|
The [Stork ARM](https://stork.readthedocs.io/en/latest/) SHOULD be updated when a new feature is added. Again, the description doesn't have to be complex or extensive. A paragraph of text briefly announcing new feature or capability will help users (and other developers) a lot.
|
|
|
The [Stork ARM](https://stork.readthedocs.io/en/latest/) SHOULD be updated when a new feature is added. Again, the description doesn't have to be complex or extensive. A paragraph of text briefly announcing a new feature or capability will help users (and other developers) a lot.
|
|
|
|
|
|
# Dead code
|
|
|
|
... | ... | @@ -111,7 +111,7 @@ Stork build system provides the `rake fmt:backend` command to format the Golang |
|
|
|
|
|
## Imports Order
|
|
|
|
|
|
We first include the packages from the standard library, then 3rd party packages, and at the end, our project packages. The packages must be imported in an alphabetical order within each of these groups.
|
|
|
We first include the packages from the standard library, then 3rd party packages, and at the end, our project packages. The packages must be imported in alphabetical order within each of these groups.
|
|
|
|
|
|
## File Names
|
|
|
|
... | ... | @@ -129,17 +129,17 @@ It is ok to have the same structure name in multiple packages because the packag |
|
|
|
|
|
## Interface Names
|
|
|
|
|
|
According to [Effective Go](https://go.dev/doc/effective_go), one-method interfaces are named by the method name plus -er suffix (e.g., Writer, Reader etc). We follow this convention.
|
|
|
According to [Effective Go](https://go.dev/doc/effective_go), one-method interfaces are named by the method name plus the "-er" suffix (e.g., Writer, Reader etc). We follow this convention.
|
|
|
|
|
|
Another frequent case is an interface with multiple methods returning different properties. Such interfaces should be named after the object these properties belong to, plus the word `Accessor`. For example, an interface returning subnet properties can be called `SubnetAccessor`.
|
|
|
|
|
|
## Mock File Names
|
|
|
|
|
|
We often use interface mocking in our unit tests to simulate returning different sets of values from the interfaces. The mocks are generated and by the `MockGen` framework, which puts their implementations in the files named by the developers. The developers MUST use the `_test.go` as a suffix for these file names. Note that it implies that the mock implementation must be always stored in the same package as the tests using the mock.
|
|
|
We often use interface mocking in our unit tests to simulate returning different sets of values from the interfaces. The mocks are generated by the `MockGen` framework, which puts their implementations in the files named by the developers. The developers MUST use the `_test.go` as a suffix for these file names. Note that it implies that the mock implementation must be always stored in the same package as the tests using the mock.
|
|
|
|
|
|
## Getters and Setters
|
|
|
|
|
|
We require that getters and setters in Stork include the `Get` and `Set` prefixes. Even though the Effective Go document explicitly says that these prefixes are neither idiomatic nor required, we decided to use them because they make clear distinction between the getters and setters. Also, that's the naming convention used in other ISC projects.
|
|
|
We require that getters and setters in Stork include the `Get` and `Set` prefixes. Even though the Effective Go document explicitly says that these prefixes are neither idiomatic nor required, we decided to use them because they make a clear distinction between the getters and setters. Also, that's the naming convention used in other ISC projects.
|
|
|
|
|
|
For example, declare your getter function like this:
|
|
|
|
... | ... | @@ -190,7 +190,7 @@ The _goto_ statements MUST NOT be used as they generally make the code flow hard |
|
|
|
|
|
## Unit Tests with Large Input
|
|
|
|
|
|
Suppose a test requires a long input string (e.g., an Kea entire configuration). We could could declare it in a constant or create a function returning this string, but the large blobs of text are hard to browse. We recommend moving the input text to a separate text file and using the _Golang embed_ mechanism to read the file into a variable in the test. For example:
|
|
|
Suppose a test requires a long input string (e.g., an Kea entire configuration). We could declare it in a constant or create a function returning this string, but the large blobs of text are hard to browse. We recommend moving the input text to a separate text file and using the _Golang embed_ mechanism to read the file into a variable in the test. For example:
|
|
|
|
|
|
```golang
|
|
|
//go:embed keaconfig_test_dhcp4_all_keys.json
|
... | ... | @@ -253,17 +253,17 @@ The use of inline `styles` SHOULD be avoided. The UI developers should rather us |
|
|
|
|
|
## Naming Conventions in Style Files
|
|
|
|
|
|
There are many conventions for CSS naming to reduce the footprint and improve the styles clarity. We recommend using the [BEM](https://getbem.com/introduction/) naming conventions in the new Stork code. However, using it is not imposed by our coding guidelines. An alternative is to use the class names including the hyphens as word separators, with the object names at the end. For example:
|
|
|
There are many conventions for CSS naming to reduce the footprint and improve the styles' clarity. We recommend using the [BEM](https://getbem.com/introduction/) naming conventions in the new Stork code. However, using it is not imposed by our coding guidelines. An alternative is to use the class names including the hyphens as word separators, with the object names at the end. For example:
|
|
|
|
|
|
- `served-scopes-help-box` - ID of a help box for server scopes
|
|
|
- `stork-version-tooltip` - ID of a tooltip for stork version element
|
|
|
- `stork-version-tooltip` - ID of a tooltip for a Stork version element
|
|
|
- `action-button` - class name for action button
|
|
|
- `dhcp-services-table` - class name for a table with DHCP services
|
|
|
- `section-heading` - class name for section heading
|
|
|
- `section-heading` - class name for a section heading
|
|
|
- `green-colored-panel` - class name for green panel
|
|
|
|
|
|
Examples from PrimeNG:
|
|
|
|
|
|
- `ui-tooltip-arrow` - class name for an arrow in tooltip
|
|
|
- `ui-tooltip-arrow` - class name for an arrow in a tooltip
|
|
|
- `ui-panel-titlebar` - class name for a titlebar in a panel
|
|
|
- `ui-widget-content` - class name of widget content |