Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 96 additions & 3 deletions coding-guidelines.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ We have a mix of Optional and allowing null values because GraphQL Java was orig

We are aiming to not use Optional moving forward in order to be consistent overall.



### Unit testing and dependencies
All tests are written in [Spock](http://spockframework.org).

Expand Down Expand Up @@ -80,7 +78,7 @@ Every public data class should be:

- Immutable
- having a Builder class
- having a transform method
- having a `transform` method


Every data class should be immutable and contain a `public static class Builder {..}` with a static factory method `newFoo` (not `newBuilder`).
Expand All @@ -91,3 +89,98 @@ The class should also contain a `public Foo transform(Consumer<Builder> builderC

Private classes should follow the same design, but they don't have to.

### Default Collections idiom

The default pattern for using Set, Map and List is:
- `List<Foo> fooList = new ArrayList<>()`
- `Set<Foo> fooSet = new LinkedHashSet<>()`
- `Map<Foo> fooMap = new LinkedHashMap<>()`

By using the generic interface instead of using an implementation we are making sure we
don't rely on anything impl specific.
The default implementations for `Set` and `Map` should be the `LinkedHashSet` and `LinkedHashMap`
because it offers stable iteration order.

### Stream API vs for, index loop etc
Using the Stream API is ok in general, but it must kept simple. Stream maps inside
maps should be avoided and the inner logic should be refactored into a method.

It also ok to use the traditional for loop or other constructs: sometimes it is more readable than
the modern Stream API. The Stream API is not a replacement for all other loops/iterations.


### Maximum Indentation is two
One of the most important rules is to keep the number of indentations as low as possible.
In general the max number should be two. This means a for loop inside a condition is ok.
A condition inside a for loop inside a for loop is not.

Extracting a method is the easy way out.

### Early method exit
Exit the method early to avoid an indentation:

```java
public void foo() {
if(cond) {
return;
}
...do something
}
```
is better than:

```java
public void foo() {
if(!cond) {
...do something
}
}
```

### Maximum line length and multi line statements

We don't have a strict max line length.
But of course every statement should be limited. Not so much in terms of length but much more in terms
of what the statement does.

If a statement is multiple lines long it should be broken down into the same indentation level.

For example this is ok:
```java
return myMap
.entrySet()
.stream()
.map(entry -> mapEntry(entry))
.collect(Collectors.toList());
```
This is not ok:
```java
return fooListOfList.stream().map(
fooList -> fooList.stream()
.sorted((x,y) -> sort(x,y))
.map(foo -> foo.getMyProp())
.collect(toList())
```
It has a lambda in streams in streams. The inside stream should be extracted in a extra method and each
method call should be on a new line:
```java
return fooListOfList
.stream()
.map(this::mapFooList)
.collect(toList());
```

### Every class its own file: avoid inner classes and interfaces
Every class/interface should have its one file in general.
Inner classes are almost never ok (especially public ones). Every class should have its own file to make it easier to read and explore the code.

### Use `graphql.Assert` instead of `Objects`
We maintain our own small set of Assert util methods. Don't use `Objects.requireNonNull` and others in order
to be consistent.

### `FooEnvironment` method arguments for public API
Don't use specific arguments for interface methods but rather a `FooEnvironment` argument. This ensures future
backwards compatibility when new inputs are added.