Skip to content

Commit

Permalink
Add API design guidelines (denoland#119)
Browse files Browse the repository at this point in the history
  • Loading branch information
piscisaureus committed Jan 28, 2019
1 parent c758845 commit bef7ba1
Showing 1 changed file with 104 additions and 0 deletions.
104 changes: 104 additions & 0 deletions README.md
Expand Up @@ -59,6 +59,110 @@ Example: Instead of `file-server.ts` use `file_server.ts`.
More specifically, code should be wrapped at 80 columns and use 2-space
indentation and use camel-case. Use `//format.ts` to invoke prettier.

### Exported functions: max 2 args, put the rest into an options object.

When designing function interfaces, stick to the following rules.

1. A function that is part of the public API takes 0-2 required arguments,
plus (if necessary) an options object (so max 3 total).

2. Optional parameters should generally go into the options object.

An optional parameter that's not in an options object might be acceptable
if there is only one, and it seems inconceivable that we would add more
optional parameters in the future.

3. The 'options' argument is the only argument that is a regular 'Object'.

Other arguments can be objects, but they must be distinguishable from a
'plain' Object runtime, by having either:

- a distinguishing prototype (e.g. `Array`, `Map`, `Date`, `class MyThing`)
- a well-known symbol property (e.g. an iterable with `Symbol.iterator`).

This allows the API to evolve in a backwards compatible way, even when the
position of the options object changes.

```ts
// BAD: optional parameters not part of options object. (#2)
export function resolve(
hostname: string,
family?: "ipv4" | "ipv6",
timeout?: number
): IPAddress[] {}

// GOOD.
export interface ResolveOptions {
family?: "ipv4" | "ipv6";
timeout?: number;
}
export function resolve(
hostname: string,
options: ResolveOptions = {}
): IPAddress[] {}
```

```ts
export interface Environment {
[key: string]: string;
}

// BAD: `env` could be a regular Object and is therefore indistinguishable
// from an options object. (#3)
export function runShellWithEnv(cmdline: string, env: Environment): string {}

// GOOD.
export interface RunShellOptions {
env: Environment;
}
export function runShellWithEnv(
cmdline: string,
options: RunShellOptions
): string {}
```

```ts
// BAD: more than 3 arguments (#1), multiple optional parameters (#2).
export function renameSync(
oldname: string,
newname: string,
replaceExisting?: boolean,
followLinks?: boolean
) {}

// GOOD.
interface RenameOptions {
replaceExisting?: boolean;
followLinks?: boolean;
}
export function renameSync(
oldname: string,
newname: string,
options: RenameOptions = {}
) {}
```

```ts
// BAD: too many arguments. (#1)
export function pwrite(
fd: number,
buffer: TypedArray,
offset: number,
length: number,
position: number
) {}

// BETTER.
export interface PWrite {
fd: number;
buffer: TypedArray;
offset: number;
length: number;
position: number;
}
export function pwrite(options: PWrite) {}
```

### Use JS Doc to document exported machinery

We strive for complete documentation. Every exported symbol ideally should have
Expand Down

0 comments on commit bef7ba1

Please sign in to comment.