mirror of https://github.com/hyperledger/besu
Added Coding Conventions (#342)
* Added Coding Conventions Signed-off-by: Adrian Sutton <adrian.sutton@consensys.net>pull/2/head
parent
21ffa54558
commit
73be129182
@ -0,0 +1,220 @@ |
||||
# Contents |
||||
* [1 Introduction](#1-introduction) |
||||
* [2 General Design Philosophy](#2-general-design-philosophy) |
||||
* [3 Specific Design Techniques](#3-specific-design-techniques) |
||||
* [4 Java](#4-java) |
||||
* [5 Logging](#5-logging) |
||||
|
||||
# 1 Introduction |
||||
|
||||
This document contains guidelines (some stricter than others) so we can be consistent and spend more time solving the bigger and more interesting issues. |
||||
|
||||
The guidelines are intended to facilitate working together not to facilitate reviews that criticize without adding value. |
||||
|
||||
Some guidelines are personal opinion. The idea being we make a decision once, document it, and apply it for consistency. Again, we can then spend more time on the interesting issues and less time discussing coding conventions :-) |
||||
|
||||
# 2 General Design Philosophy |
||||
|
||||
The key principles are: |
||||
* Keep It Simple |
||||
* Idiomatic Java |
||||
* YAGNI (You Ain't Gonna Need It) |
||||
|
||||
## 2.1 Keep It Simple |
||||
|
||||
Simple does not mean the fewest lines of code. Simple code is: |
||||
* Easy to understand |
||||
* Self-documenting and not dependent on comments to explain what it does |
||||
* Understandable even to inexperienced Java developers |
||||
* Dependent on selecting the right data structures for the task |
||||
* Usually the most performant. Without data showing another approach is faster, stick with the simple design |
||||
* Not simplistic: |
||||
|
||||
- Ethereum is complex and Pantheon must handle this complexity and operate correctly and securely |
||||
- Pantheon code should align with well-established Ethereum abstractions and terminology used in Ethereum specifications |
||||
- Aim to make the code as simple as possible but no simpler |
||||
|
||||
## 2.2 Idiomatic Java |
||||
|
||||
Pantheon embraces typical Java idioms including using an Object Oriented approach to design. This includes: |
||||
* Providing alternate behaviours via polymorphism instead of having conditional logic scattered through the codebase. For example, `ProtocolSpec` provides a standard interface to blockchain operations and multiple implementations define the different behaviours for each Ethereum milestone. |
||||
* Encapsulating behaviour and data together in classes. For example, `BytesValue` encapsulates byte data and methods operating on the byte data. `BytesValue.isZero()` is an instance method instead of accepting a `BytesValue` parameter. |
||||
|
||||
`ProtocolSpec` is an exception and does not hold the blockchain data on which it operates. This is because that blockchain data is widely shared and not specifically owned by `ProtocolSpec`. |
||||
* Embracing modern Java features like Optional, Streams and lambdas when they make code simpler and clearer. |
||||
|
||||
- Do use Streams and map with lambdas to convert values in a list to a different form. |
||||
- Don't pass lambdas into executors because it makes it harder to identify the threading interactions. The lambda makes the code shorter but not clearer. Instead use a separate class or extract a method. |
||||
* For good examples, refer to the APIs the JDK itself exposes. |
||||
|
||||
>**Note** If you're not sure what idiomatic Java looks like, start by following the typical patterns and naming used in Pantheon. |
||||
|
||||
## 2.3 You Ain't Gonna Need It (YAGNI) |
||||
|
||||
The Pantheon design prioritizes meeting current requirements in the simplest, clearest way over attempting to anticipate future functionality. As a result, Pantheon’s design: |
||||
* Is not set in stone as a big upfront design. The design is adjusted through constant refactoring as new requirements are added and understood. |
||||
* Uses abstraction only where it aids understanding of the current code. Abstraction is not used where it only supports future needs. |
||||
* Avoids over-engineering. |
||||
|
||||
# 3 Specific Design Techniques |
||||
|
||||
# 3.1 Creating Understandable, Self-documenting Code |
||||
|
||||
To create understandable, self-documenting code: |
||||
- Use clear naming for variables, methods, and classes |
||||
- Use US spelling instead of UK. For example, synchronize instead of synchronise |
||||
- Keep methods and classes short and focused on a single responsibility. Preferable maximum lengths: |
||||
* Lambdas: 1 - 3 lines |
||||
* Methods: less than 50 lines |
||||
* Anonymous classes: less than a 50 lines |
||||
* Inner classes: not much more than 50 lines |
||||
* Classes: a few hundred lines |
||||
- Be thoughtfully organised in terms of method order, package structure, and module usage |
||||
- Follow well-established patterns and conventions |
||||
- Be consistent |
||||
- Make it easy to follow the control flow by _clicking through_ in an IDE |
||||
- Make it easier to use the right way than the wrong way |
||||
- Avoid abbreviations. We are a global team and when English is a second language abbreviations reduce readability. The following abbreviations are exceptions: |
||||
* tx -> Transaction |
||||
* IBFT -> Istanbul Byzantine Fault Tolerant (a consensus protocol) |
||||
* EVM -> Ethereum Virtual Machine |
||||
|
||||
# 3.2 Creating Code for Constant Refactoring and Evolving Design |
||||
|
||||
So the code can cope with constant refactoring and evolving design, write code that: |
||||
* Is well tested. |
||||
|
||||
Avoid test cases requiring detailed interactions with mocks because these are often brittle. |
||||
|
||||
* Avoids duplication. |
||||
|
||||
* Follows the Single Responsibility Principle where each class is responsible for one thing. |
||||
|
||||
>**Note** It is important to scope the responsibility wisely. Responsibilities that are: |
||||
> * Too small lead to an explosion of classes making things hard to follow |
||||
> * Too large lead to the class becoming big and unwieldy |
||||
|
||||
* Favors composition over inheritance. You can use inheritance, but prefer composition |
||||
|
||||
* Uses dependency injection |
||||
- Constructors should be simple, with dependencies passed in rather than built in the constructor |
||||
- Pantheon does not use a dependency injection framework |
||||
|
||||
* Validates method parameters for public methods using the Guava `Preconditions` class. Avoid validating parameters in private methods |
||||
|
||||
* Generally avoids interfaces with a single implementation unless they are explicitly being used to narrow the exposed API |
||||
|
||||
* Uses the most general applicable type. For example, `List` or `Collection` instead of `ArrayList` |
||||
|
||||
## 3.3 Additional Design Elements |
||||
|
||||
|
||||
* Use Optional rather than returning null when not having a value is a normal case |
||||
|
||||
* Consider exception and error handling as part of the overall design. Pantheon avoids checked exceptions |
||||
|
||||
* Give threads meaningful names. For example: |
||||
`Executors.newFixedThreadPool(1, new ThreadFactoryBuilder().setNameFormat(“Ibft”).build())` |
||||
|
||||
|
||||
# 4 Java |
||||
|
||||
## 4.1 Style Guide |
||||
|
||||
Pantheon follows the [Google code style](https://google.github.io/styleguide/javaguide.html) and uses spotless to ensure consistency of formatting. |
||||
|
||||
To automatically reformat the code before creating a pull request, run: |
||||
|
||||
```json |
||||
./gradlew spotlessApply |
||||
``` |
||||
|
||||
### 4.1.1 Install Google Style Settings |
||||
|
||||
The Google style settings can be installed in [Intellij](https://github.com/google/google-java-format#intellij) and [Eclipse](https://github.com/google/google-java-format#eclipse). |
||||
|
||||
## 4.2 Additional Java Style Guidelines |
||||
|
||||
## 4.2.1 Fields |
||||
|
||||
Class-level fields are generally not separated by blank lines but can use a blank line to separate them into logical groupings. |
||||
|
||||
## 4.2.2 Final Keyword |
||||
|
||||
Method parameters must be final. Class level and local fields should be final whenever possible. |
||||
|
||||
## 4.2.3 Common Methods |
||||
|
||||
* Getters follow idiomatic format with `get` prefix. For example, `getBlock()` gets a block property. |
||||
* Setters follow idiomatic format with `set` prefix. For example, `setBlock(Block block)` sets a block property. |
||||
* For `toString methods`, use the Guava 18+ `MoreObjects.toStringHelper` |
||||
* Equals and `hashCode()` methods use the `Object.equals` and `Object.hash` methods (this is the _Java 7+_ template in IntelliJ. Don’t accept subclasses and don’t use getters) |
||||
|
||||
## 4.2.4 Testing |
||||
|
||||
* Don't use a fixed prefix (for example, `test`). Do explain the expected behaviour not just the situation |
||||
|
||||
Good: `returnTrueWhenValueIsXyz()` |
||||
|
||||
Bad: `valueIsXyz()` |
||||
|
||||
* Use AssertJ for assertions in preference to JUnit’s Assert class. |
||||
|
||||
To help future-proof the code (avoiding libraries that may be deprecated in the near future), avoid assertions from the `org.assertj.core.api.Java6Assertions` class. Java6 in the name is a concerning signal |
||||
|
||||
## 4.2.5 Miscellaneous |
||||
|
||||
* When creating loggers it should be the first declaration in the class with: |
||||
|
||||
`private static final Logger LOG = getLogger();` |
||||
|
||||
* Ternary operators are acceptable when they make the code clearer but should never be nested |
||||
|
||||
* Avoid TODO comments. Log a ticket instead |
||||
|
||||
* Specify Gradle dependency versions in `versions.gradle` |
||||
|
||||
* Don't use two or more blank lines in a row |
||||
|
||||
# 5 Logging |
||||
|
||||
Logging is important for understanding what Pantheon is doing at any given time (for example, progress while synchronizing) and investigating defects. During development, add logging to aid in these cases. |
||||
|
||||
## 5.1 Log Messages |
||||
|
||||
Make log messages: |
||||
* Not so frequent they are overwhelming in the log output |
||||
* At the appropriate level |
||||
* Detailed enough to understand what actually happened. For example: |
||||
|
||||
`Insufficient validators. Expected 10 but found 4` |
||||
|
||||
* As succinct as possible while still being clear. |
||||
|
||||
* Bad: `Insufficient validators. Expected 10 but got: [address, address, address, address, address, address]` |
||||
|
||||
## 5.2 Log Levels |
||||
|
||||
* _Trace_ |
||||
|
||||
Extremely detailed view showing the execution flow. Likely only useful to developers |
||||
|
||||
* _Debug_ |
||||
|
||||
Information that is diagnostically helpful to a wider group than just developers (for example, sysadmins) |
||||
|
||||
* _Info_ |
||||
|
||||
Generally useful information to log (for example, service start/stop, configuration assumptions). Default logging level |
||||
|
||||
* _Warn_ |
||||
|
||||
Anything that can potentially cause application oddities but from which Pantheon automatically recovers |
||||
|
||||
* _Error_ |
||||
|
||||
Any error which is fatal to the operation, but not Pantheon itself (for example, missing data) |
||||
|
||||
* _Fatal_ |
||||
|
||||
An error that forces a shutdown of Pantheon |
Loading…
Reference in new issue