General guide for code review.
This covers the @smithy/* scoped core runtime for JavaScript.
We support the Node.js runtime, modern commonly used browsers, and react-native. To ensure that these runtime environments are API-equivalent, we have validation scripts in the core package that require all Node.js/browser/native implementations to have equivalent API surfaces. Exported runtime symbols and type symbols are examined.
- For any usage of the
node:modules or functionality not present in browsers, an alternate implementation must be provided for browsers. The native implementation defaults to the browser one unless a separateindex.native.tsis implemented.- In some cases, there is no alternate in browsers. This must be clearly indicated at the
index.tslevel by the symbolSymbol.for("node-only")when implementing export symbol matching. Examples may be found in@smithy/coresubmodule indexes.
- In some cases, there is no alternate in browsers. This must be clearly indicated at the
- The runtime code must be understood by the stated minimum supported language level.
- We do not use experimental language features that require polyfills or transform steps to run in Node.js.
- caveat: our 3rd supported runtime environment, react-native, may require polyfills. The react-native runtime has historically been non-standard to the point that we should not let it influence the default Node.js implementation.
- Correctness & Security
- Runtime performance
- Brevity and initialization performance
- Readability
When making a trade-off between readability and performance, prioritize performance. Readability can be provided by comments, whereas performance cannot.
Within performance, balance throughput performance and code-size, which affects initialization time. There will also be a point where performance gains are small enough that an optimization should not be made. This is not predefined and left to the reviewer.
- Stable memory allocation: for hot code, pre-allocate the workspace (e.g. a byte array), work within it, and only allow it to be garbage-collected after leaving the hot code path.
- Minimize intermediate collections like
Object.keys(). - Minimize copying like
{ ...data }. - Minimize function scope depth.
- Minimize function scope traversal including recursion.
- Minimize object allocation, including the creation of closures.
We want to provide as small an API surface as possible.
This is not because we don't want to provide features to our users. We want to avoid situations where users have built solutions on top of non-public APIs that we later change or remove.
To ensure that we have visibility on the API surface of the runtime, we have an API snapshot check that runs during integration testing.
export *statements are banned for new code.export *creates an API surface that is not diff-visible between changes, and leads to leaking implementation details.- Things of interest to the reader must appear closer to the top.
- One concrete implementation for this is that in classes,
publicmethods must come beforeprotected.privatecomes last. For non-classes, apply the same reasoning with your own judgment.
- One concrete implementation for this is that in classes,
/**
* Description goes here.
*
* @example
* const impl = new Impl();
*
* @public
*/
export class Impl {
public prop1;
private prop2;
public constructor() {}
/**
* Static factory.
*/
public static method1() {}
/**
* @returns something.
*/
public method2() {}
private static method3() {}
private method4() {}
}
const moduleInternal = () => {};We write code comments to the https://tsdoc.org/ specification.
- The free-form description (optional) of a symbol must come first. All annotations must come below this description.
- For any symbol exported from a package, it must have an access level annotation,
@publicor@internal.- In limited circumstances
@alphaand@betamay be used.
- In limited circumstances
- For non-exported symbols, you do not need to write comments for all symbols. Use your judgment here.
- You may opt to write a free-form description of a method in lieu of writing the API annotations
@paramand@returnsetc. - Comments explain "why?", not "how?". The code says "how".
- Optimize performance by creating cached
KnowledgeIndexobjects. Avoid repeated traversal of a service model for static information. - Avoid excessive logging to the WARN and INFO channels.