Skip to content

Commit 9d96803

Browse files
committed
initial code style
1 parent a9213d8 commit 9d96803

1 file changed

Lines changed: 38 additions & 0 deletions

File tree

CODESTYLE.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Go Style Guide
2+
3+
In the URnetwork code, the following Go style is used. A few conventions — notably the `self` receiver name and the verbose usage+type field naming — intentionally diverge from idiomatic Go; they are deliberate house rules, not oversights.
4+
5+
## Naming
6+
7+
- Our receiver name is `self`: `func (self *T) f(...)` (pointer receiver) or `func (self T) f(...)` (value receiver).
8+
- Our canonical name for a `sync.Mutex` guarding state is `stateLock`.
9+
- Field and variable names are slightly more verbose than standard Go, so usage and type can be inferred from the name. The scheme is usage + type:
10+
- Maps: `map[K]V` is `usageKVs` — e.g. `activeNetworkIdUsers` for a `map[Id]User` of active users. Note the plural `s`.
11+
- Slices: `[]T` is `usageTs` — e.g. `activeNetworkIds` for a `[]Id` of active networks. Note the plural `s`.
12+
- Variables: `T` is `usageT` — e.g. `activeNetworkId` for the active network `Id`.
13+
- Use the shortest type token that stays unambiguous: `Id`, not `Identifier`; but never abbreviate past recognition (`activeNetworkIds`, not `activeNetIds`).
14+
- When the value already implies its type, drop the type token: concrete objects are named by usage alone (`session`, `client`, `transferBalance`). Keep the type when dropping it would confuse an identifier with the object it names — an `Id` stays `clientId`, never `client`.
15+
16+
## Comments
17+
18+
- Prefer short inline comments (`// ...` inside a function), 1–2 lines per point unless more is truly needed.
19+
- Put a doc comment at the top of each file, type, and function/method declaration, summarizing the architecture and edge cases. These can be as long as needed, but aim to be concise. Push information that applies throughout a type or file up to the type or file header instead of repeating it — in particular, concurrent goroutine safety.
20+
21+
## Concurrency and goroutine safety
22+
23+
- By default, package-level functions are assumed safe for concurrent use, and a type's methods are assumed NOT safe unless the type documents otherwise.
24+
- Hold a lock across the smallest scope that needs it. The idiom is an immediately-invoked closure: `func() { self.stateLock.Lock(); defer self.stateLock.Unlock(); ... }()`.
25+
- Every potentially infinite loop must take a context (for cancellation) and rate-limit itself (to avoid busy-spinning).
26+
- Use `connect.Reconnect` for reconnect rate limiting.
27+
- Use `connect.Monitor` for change notifications. Grab the channel before reading the monitored state — `update := monitor.NotifyChannel()` — then `select` on it while waiting for changes. Subscribing before the read guarantees an update can't be missed.
28+
29+
## Goroutine lifecycle
30+
31+
- Start a type's internal management goroutines in its constructor, so the returned object is already fully running. The lifecycle loop is conventionally `func (self *T) run()` — lowercase, internal, started by the constructor.
32+
- Exception: when an external manager must clean up after the lifecycle, expose `func (self *T) Run()` (uppercase) instead. The manager calls `Run()` after construction and tears the object down when `Run()` returns. Casing carries the meaning: lowercase `run()` is internal and self-started; uppercase `Run()` is externally driven.
33+
- Wait with `time.After` inside the run loop; we don't use `time.Timer`. Reusing a `time.Timer` would save the per-iteration allocation, but we don't optimize for that.
34+
35+
## Formatting and structure
36+
37+
- Format with `gofmt`.
38+
- Inline single-use helper logic as a local closure at its call site (`f := func(...){ ... }; f()`) rather than pulling it out into a separate named function.

0 commit comments

Comments
 (0)