Skip to content

spear-ai/citizen

Repository files navigation

Logo

Spear AI Code Citizen

“This code is what it is because of its citizens” — Plato

Goal

Decrease net cognitive complexity for developers.

Assumptions

The rules in this guide are intended to work well with the following assumptions:

Read & Write

Developers read code more than they write code, and internal developers write code more than outside collaborators:

  • Readability > Writeability
  • Writeability for internal developers > Writeability for outside collaborators

Screen size

Developers have medium–large displays:

  • Long lines are less likely to wrap

GitHub

Developers use GitHub:

  • Side-by-side line diffs are 119 characters long

VSCode

Developers use VSCode:

  • Auto-formatting, auto-linting, and code completion reduces keystrokes

Legend

[!] Indicates that a rule is controversial.

Rules

Language

TODO: Fill me out

Files

TODO: Fill me out

Writing

¶ cE.Cx: DO use informal tone

Do use a friendly and informal tone. Other rules follow from this one, such as using contractions.

¶ cE.kI: DO use contractions

Do use contractions.

# ✓ Good
def panic():
    # Panic if we can’t recover from an error.
# ✗ Bad
def panic():
    # Panic if we cannot recover from an error.

¶ cE.j5: DO use curly quotes

Do use curly single and double quotes instead of straight quotes.

// ✓ Good
let race; // “Xel’Naga”, “Terran”
// ✗ Bad
let race; // "Xel'Naga", "Terran"

¶ cE.4x: DO use ellipsis instead of dots

Do use the ellipses character instead of 3 dots.

// ✓ Good
let series; // 1, 2, 3, …, 10
// ✗ Bad
let series; // 1, 2, 3, ..., 10

¶ cE.jF: DO use en dashes instead of hypens

Do use an en dash when appropriate such as for spans and ranges.

// ✓ Good
let duration; // 2–3 weeks
// ✗ Bad
let duration; // 2-3 weeks, 2 to 3 weeks

¶ cE.Cc: DO use em dashes instead of hypens

Do use an em dash when appropriate such as replacing commas, parentheses, or colons⁠.

// ✓ Good
let sentence; // Lorem ipsum — foo bar — dolor.
// ✗ Bad
let sentence; // Lorem ipsum -- foo bar -- dolor.

¶ cE.eB: DO use glyphs instead of characters or words

// ✓ Good
let symbols; // ←, →
let angle; // 45°
// ✗ Bad
let symbols; // <-, ->
let angle; // 45 degrees

¶ cE.BR: DO use canonical spelling of proper nouns

Do use the canonical spelling of proper nouns⁠.

# ✓ Good
words # Python, Node.js, PostgreSQL
# ✗ Bad
words # python, node, postgres

¶ cE.K6: DO begin type descriptions with an indefinite article

Do begin type descriptions with an indefinite article (“a/an”).

SQL:

-- ✓ Good
COMMENT ON TABLE unit IS 'A unit.';
-- ✗ Bad
COMMENT ON TABLE unit IS 'The unit.';

GraphQL:

# ✓ Good
type Unit {
  """A unit."""
}
# ✗ Bad
type Unit {
  """The unit."""
}

¶ cE.re: DO begin type property descriptions with a definite article

Do begin type property descriptions with a definite article (“the”) when applicable.

SQL:

-- ✓ Good
COMMENT ON COLUMN unit.name IS 'The name of a unit.';
-- ✗ Bad
COMMENT ON COLUMN unit.name IS 'A name of a unit.';

GraphQL:

# ✓ Good
type Unit {
  name: String
  """The name of a unit."""
}
# ✗ Bad
type Unit {
  name: String
  """A name of a unit."""
}

python:

# ✓ Good
class Unit:
    name: str  # The name of a unit.
# ✗ Bad
class Unit:
    name: str  # A name of a unit.

Comments

¶ LW.n8: DO communicate intent

Comments should explain intent rather than what the code is doing. If it’s unclear what the code is doing then it should be refactored.

¶ LW.8j: DO show examples

Do show examples when possible.

js:

// Get a unit’s globally unique identifier.
//
// (“Zumwalt-class destroyer”, 3) → “Unit:ZumwaltClassDestroyer:3”
// (“Leopard 2A7”, 5) → “Unit:Leopard2a7:5”
const getUnitId = (name: string, index: number) => `Unit:${pascalCase(name)}:${index}`;

¶ LW.pb: DO follow writing styles

Do follow all writing style rules when also writing comments.

// ✓ Good
// e.g. “John Doe” → “john_doe”
const toKebabCase = () => {};
// ✗ Bad
// e.g. "John Doe" => john_doe
const toKebabCase = () => {};

¶ LW.hb: DO use sentence case

Do use sentence case in comments.

py:

# ✓ Good
# Use Manhattan distance because it performs better on high dimensional data
distance = scipy.spatial.distance.cdist(tensor_a, tensor_b, metric='cityblock')
# ✗ Bad
# use manhattan distance because it performs better on high dimensional data
distance = scipy.spatial.distance.cdist(tensor_a, tensor_b, metric='cityblock')

js:

// ✓ Good
// Choose a background color that matches the sky
const backgroundColor = "#2ebde5"; // Azure
/* eslint-disable capitalized-comments */

// ✗ Bad
// choose a background color that matches the sky
const backgroundColor = "#2ebde5"; // azure

¶ LW.nA: DO punctuate block comments

Do use punctuation on block comments.

py:

# ✓ Good
# Lorem ipsum dolor sit amet.
placeholder_text =
# ✗ Bad
# Lorem ipsum dolor sit amet
placeholder_text =# ✗ Bad
# Lorem ipsum dolor sit amet
# Consectetur adipiscing elit
placeholder_text =

¶ LW.XU: DO punctuate documentation-as-code

Do punctuate comments that serve as documentation. These comments often generate Website docs, CLI arguments, etc.

@dataclass
class FetchBananasResponse
    """The API response for fetching bananas."""

    banana_list: List[Banana]
    """A list of varying length bananas."""

@dataclass
class Banana:
    """An irresistible fruit loved by all the great apes."""

    is_ripe: bool
    """Whether it is ready for eating."""

    length: float
    """The length in meters."""

¶ LW.Sf: DO inline short comments

# ✓ Good
speed =# Meters per hour
time =# Minutes
distance = speed / (time / 60)  # Meters

Variables

¶ 9W.1Q. DO follow casing conventions

Follow the casing conventions of the current language.

json:

{
  "boundingBox": [0, 0, 10, 10]
}

python:

bounding_box = [0, 0, 10, 10]

rust:

let bounding_box: [i32; 4] = [0, 0, 10, 10];

typescript:

const boundingBox = [0, 0, 10, 10];

¶ 9W.MX: DO favor readability to brevity

Do favor readability to brevity.

# ✓ Good
docker_image = "huggingface/transformers-pytorch-gpu"
ec2_instance_type = "p3.8xlarge"
# ✗ Bad
image = "huggingface/transformers-pytorch-gpu"
type = "p3.8xlarge"

¶ 9W.qP: DO group with a prefix

Do group related variables with a prefix.

# ✓ Good
cnn_kernel = (3, 3)
cnn_stride = (1, 1)
learning_rate = 1e-05
# ✗ Bad
kernel = (3, 3)
stride = (1, 1)
learning_rate = 1e-05

¶ 9W.hn: DO space out pre/post-fixes

Do insert a space before postfixes and after prefixes.

# ✓ Good
hidden_layer_0 = "…"
hidden_layer_1 = "…"
# ✗ Bad
hidden_layer0 = "…"
hidden_layer1 = "…"
// ✓ Good
const userName = "…";
const userPassword = "…";
// ✗ Bad
const username = "…";
const userPassword = "…";

¶ 9W.qV: DON’T use abbreviations

Don’t use abbreviations. They’re more likely to encounter naming conflicts. They must also be be learned and memorized.

# ✓ Good
directory = "data/units"
unit_direction = "NORTH"
# ✗ Bad
dir = "data/units"
unit_dir = "NORTH"
# ✓ Good
result, error = action()
response = request()
# ✗ Bad
res, err = action()
res = req()
# ✓ Good
sagemaker = SageMaker()
hyperparameters = {…}
# ✗ Bad
sm = SageMaker()
hparams = {…}

¶ 9W.cv: DO use acronyms

Do use acronyms and initialisms. They’re well known and less verbose. Moreover, the words they represent are often unknown; so must be learned and memorized

# ✓ Good
nato_classification = "cosmic"
radar_range = 1_700_150  # meters
# ✗ Bad
north_atlantic_treaty_organization_classification = "cosmic"
radio_detection_and_ranging_range = 1_700_150  # meters
# ✓ Good
s3_bucket = "secret-stuff"
website_url = "https://spear.ai"
# ✗ Bad
simple_storage_service_bucket = "secret-stuff"
website_uniform_resource_locator = "https://spear.ai"

¶ 9W.MM: DON’T invent acronyms

Don’t invent acronyms or initialisms unless external users would find it easier to use. They’re not well known; so must be learned and memorized.

# ✓ Good
aerial_unit_direction = (0.8, 0.45)
aerial_unit_speed = 329
# ✗ Bad
au_direction = (0.8, 0.45)
au_speed = 329
# ✓ Good
rcn_has_attention = True
rcn_hidden_layer_size = 40
# ✗ Bad
really_cool_network_has_attention = True
really_cool_network_hidden_layer_size = 40

¶ 9W.Co: DO add type hints

Do add type hints. Many names are ambiguous and can be confused for booleans, dates, functions, etc.

python:

# ✓ Good
created_date = "1776-07-04T04:56:02.000Z"
was_published = True
# ✗ Bad
created = "1776-07-04T04:56:02.000Z"
published = True

typescript:

// ✓ Good
fileBuffer = await fs.readFile("…");
isLoaded = false;
// ✗ Bad
file = await fs.readFile("…");
loaded = false;

¶ 9W.4R: DO differentiate types

Do differentiate types. It’s hard to switch between contexts when variable names represent different types.

# ✓ Good
url = "https://spear.ai"
parsed_url = urlparse("https://spear.ai")

# ✓ Good
agent_id = "10"
agent = Agent(id="10", type=Tiger)
# ✗ Bad
url = "https://spear.ai"
url = urlparse("https://spear.ai")

# ✗ Bad
agent = "10"
agent = Agent(id="10", type=Tiger)

¶ 9W.6u: DON’T add type definitions

Don’t add type definitions.

Reasons:

  • They add superfluous information
  • They increase refactoring costs
  • They decrease portability
# ✓ Good
age = 21
confidence = 0.9
# ✗ Bad
age_int = 21
confidence_float = 0.9
// ✓ Good
const balance = BigInt(7_301_985);
const mask = new Uint8Array();
// ✗ Bad
const bigIntBalance = BigInt(7_301_985);
const maskUint8Array = new Uint8Array();

¶ 9W.ZX: DO use boolean verbs

Do use appropriate boolean verbs.

# ✓ Good
can_delete = True
has_feature = True
should_reset = True
# ✗ Bad
is_deletable = True
features = True
reset = True

¶ 9W.6t: DO be positive

Do be positive with boolean variables.

python:

# ✓ Good
is_enabled = True
is_visible = False
# ✗ Bad
is_disabled = False
is_not_visible = True

tsx:

// ✓ Good
<Tab isActive>Click me</Tab>
// ✗ Bad
<Tab isInactive={false}>Click me</Tab>

¶ 9W.jt: DO use correct tense

Do use the correct tense.

if was_suspended and not is_suspended:
    print("Welcome back!")

¶ 9W.fY: DON’T pluralize collections [!]

Don’t pluralize collections. Instead, specify the collection type.

Some object names are already plural. Therefore, it’s impossible to name a collection of those objects. Moreover, Uncountable nouns can’t be pluralized.

Specifying the collection type also clarifies which operations are allowed. For example, a List can be appended or prepended to. Whereas, a Set can be added or removed from.

# ✓ Good
equipment_list = [{…}, {…}, …, {…}]
equipment = equipment_list[0]

# ✓ Good
settings_map = {"0": {…}, "1": {…}, …, "n": {…}]
settings = settings_map["0"]

# ✓ Good
id_list = ["0", "1", "2", "0"]
id_list.append("2")
id_set = set(id_list)
# ✗ Bad
equipment = [{…}, {…}, …, {…}]
equipment = equipment[0]

# ✗ Bad
settings = {"0": {…}, "1": {…}, …, "n": {…}]
settings = settings["0"]

# ✗ Bad
ids = ["0", "1", "2", "0"]
ids.append("2")
distinct_ids = set(ids)
# ✓ Good
curl https://api.spear.ai/user
curl https://api.spear.ai/user/5
# ✗ Bad
curl https://api.spear.ai/users
curl https://api.spear.ai/users/5
-- ✓ Good
SELECT * FROM person
SELECT * FROM person_address
-- ✗ Bad
SELECT * FROM people
SELECT * FROM people_addresses

Data formats

Angle

¶ Eq.nB: DO prefer degrees to radians

Degrees are easier to reason about. Moreover, Radians serialize poorly because they are based on the irrational number π.

// ✓ Good
const turnLeft = (angle: number) => (360 + (angle - 90)) % 360;
turnLeft(0); // ⇒ 270
// ✗ Bad
const turnLeft = (angle: number) => (2 * Math.PI + (angle - 0.5 * Math.PI)) % (2 * Math.PI);
turnLeft(0); // ⇒ 4.71238898038469

Exception: A series of Math functions that require radians.

¶ Eq.z5: DO display degrees with symbol (°)

# ✓ Good
print(f"heading {heading}°")
print(f"{latitude}°, {longitude}°")
# ✗ Bad
print(f"heading {heading}")
print(f"{latitude}, {longitude}")

Color

¶ Ts.Ef: DO prefer hex color codes

// ✓ Good
const successHexColorCode = "#297c3b";
const failureHexColorCode = "#ca3214";
// ✗ Bad
const successRgbColorCode = "rgb(41, 124, 59)";
const failureRgbColorCode = "rgb(202, 50, 20)";

Exception: A library requires another format. Exception: Manipulation is easier in another format. (e.g. HSL)

¶ Ts.8M: DO use longhand hex color codes

// ✓ Good
const color = "#ff0000";
const colorAlpha = "#ff0000ff";
// ✗ Bad
const color = "#f00";
const colorAlpha = "#f00f";

¶ Ts.G0: DO use lowercase hex color codes

// ✓ Good
const backgroundColor = "#edf6ff";
const foregroundColor = "#006adc";
// ✗ Bad
const backgroundColor = "#EDF6FF";
const foregroundColor = "#006ADC";

¶ Ts.2g: DO make color format explict in non-HTML contexts

# ✓ Good
type ClassLabel {
  id: ID!
  hexColorCode: HexColorCode
}
# ✗ Bad
type ClassLabel {
  id: ID!
  color: HexColorCode
}
-- ✓ Good
SELECT id, hex_color_code FROM class_label
-- ✗ Bad
SELECT id, color FROM class_label