icore 1.0.3 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/api.md +466 -0
- package/docs/design.md +80 -0
- package/docs/examples.md +100 -0
- package/docs/git-flow.md +188 -0
- package/docs/policy/abstraction-policy.md +198 -0
- package/docs/policy/change-policy.md +86 -0
- package/docs/policy/comment-policy.md +253 -0
- package/docs/policy/decision-rule-policy.md +41 -0
- package/docs/policy/dependencies-policy.md +49 -0
- package/docs/policy/documentation-policy.md +27 -0
- package/docs/policy/index.md +49 -0
- package/docs/policy/naming-policy.md +18 -0
- package/docs/policy/non-functional-requirements.md +31 -0
- package/docs/policy/scripts-and-build-policy.md +60 -0
- package/docs/policy/testing-policy.md +335 -0
- package/package.json +1 -1
- package/readme.md +31 -525
package/docs/git-flow.md
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
# Git Flow
|
|
2
|
+
|
|
3
|
+
This project uses a classic Git Flow branching model.
|
|
4
|
+
|
|
5
|
+
## Long-Lived Branches
|
|
6
|
+
|
|
7
|
+
### `master`
|
|
8
|
+
|
|
9
|
+
`master` is the production and package provenance branch.
|
|
10
|
+
|
|
11
|
+
- Contains only released code.
|
|
12
|
+
- Receives completed releases from `release/*` branches.
|
|
13
|
+
- Receives urgent production fixes from `hotfix/*` branches.
|
|
14
|
+
- Each release commit on `master` must have a semver tag, for example `v1.1.5`.
|
|
15
|
+
|
|
16
|
+
Do not commit feature work directly to `master`.
|
|
17
|
+
|
|
18
|
+
### `legacy/v1`
|
|
19
|
+
|
|
20
|
+
`legacy/v1` preserves the historical `icore@0.1.38` codebase.
|
|
21
|
+
|
|
22
|
+
- Points to the final legacy package state.
|
|
23
|
+
- Must remain available for provenance and historical reference.
|
|
24
|
+
- Must not receive new development work.
|
|
25
|
+
- Must be pushed before any cleanup or replacement of legacy files.
|
|
26
|
+
|
|
27
|
+
### `develop`
|
|
28
|
+
|
|
29
|
+
`develop` is the integration branch for active development.
|
|
30
|
+
|
|
31
|
+
- New feature branches start from `develop`.
|
|
32
|
+
- Completed feature branches are merged back into `develop`.
|
|
33
|
+
- Release branches start from `develop`.
|
|
34
|
+
- Hotfix branches are merged back into `develop` after they are released from `master`.
|
|
35
|
+
|
|
36
|
+
Do not release directly from `develop`.
|
|
37
|
+
|
|
38
|
+
## Short-Lived Branches
|
|
39
|
+
|
|
40
|
+
### `feature/*`
|
|
41
|
+
|
|
42
|
+
Use `feature/*` branches for regular development work.
|
|
43
|
+
|
|
44
|
+
Naming:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
feature/<short-description>
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Example:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
feature/restore-public-api
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Flow:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
git switch develop
|
|
60
|
+
git switch -c feature/restore-public-api
|
|
61
|
+
|
|
62
|
+
# make changes, run checks
|
|
63
|
+
git add .
|
|
64
|
+
git commit -m "fix: restore public API"
|
|
65
|
+
|
|
66
|
+
git switch develop
|
|
67
|
+
git merge --no-ff feature/restore-public-api
|
|
68
|
+
git branch -d feature/restore-public-api
|
|
69
|
+
git push origin develop
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### `release/*`
|
|
73
|
+
|
|
74
|
+
Use `release/*` branches to prepare a version for release.
|
|
75
|
+
|
|
76
|
+
Naming:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
release/v<major>.<minor>.<patch>
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Example:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
release/v1.1.5
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Allowed changes on a release branch:
|
|
89
|
+
|
|
90
|
+
- Version bump.
|
|
91
|
+
- Changelog or release notes.
|
|
92
|
+
- Release-only documentation.
|
|
93
|
+
- Small release blockers found during validation.
|
|
94
|
+
|
|
95
|
+
Do not add unrelated feature work to a release branch.
|
|
96
|
+
|
|
97
|
+
Flow:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
git switch develop
|
|
101
|
+
git switch -c release/v1.1.5
|
|
102
|
+
|
|
103
|
+
# version/changelog/release fixes, run checks
|
|
104
|
+
git add .
|
|
105
|
+
git commit -m "chore: prepare release v1.1.5"
|
|
106
|
+
|
|
107
|
+
git switch master
|
|
108
|
+
git merge --no-ff release/v1.1.5
|
|
109
|
+
git tag -a v1.1.5 -m "Release v1.1.5"
|
|
110
|
+
|
|
111
|
+
git switch develop
|
|
112
|
+
git merge --no-ff release/v1.1.5
|
|
113
|
+
|
|
114
|
+
git branch -d release/v1.1.5
|
|
115
|
+
git push origin master develop --tags
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### `hotfix/*`
|
|
119
|
+
|
|
120
|
+
Use `hotfix/*` branches only for urgent fixes to released code.
|
|
121
|
+
|
|
122
|
+
Naming:
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
hotfix/v<major>.<minor>.<patch>
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Example:
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
hotfix/v1.1.6
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Flow:
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
git switch master
|
|
138
|
+
git switch -c hotfix/v1.1.6
|
|
139
|
+
|
|
140
|
+
# make fix, run checks
|
|
141
|
+
git add .
|
|
142
|
+
git commit -m "fix: correct production issue"
|
|
143
|
+
|
|
144
|
+
git switch master
|
|
145
|
+
git merge --no-ff hotfix/v1.1.6
|
|
146
|
+
git tag -a v1.1.6 -m "Release v1.1.6"
|
|
147
|
+
|
|
148
|
+
git switch develop
|
|
149
|
+
git merge --no-ff hotfix/v1.1.6
|
|
150
|
+
|
|
151
|
+
git branch -d hotfix/v1.1.6
|
|
152
|
+
git push origin master develop --tags
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Tags
|
|
156
|
+
|
|
157
|
+
Every released version must be tagged from `master`.
|
|
158
|
+
|
|
159
|
+
Use annotated tags:
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
git tag -a v1.1.5 -m "Release v1.1.5"
|
|
163
|
+
git push origin --tags
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Do not move release tags after they have been pushed.
|
|
167
|
+
|
|
168
|
+
## Merge Policy
|
|
169
|
+
|
|
170
|
+
Use merge commits for branch completion:
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
git merge --no-ff <branch>
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
This preserves the historical branch structure and matches the existing repository history.
|
|
177
|
+
|
|
178
|
+
## Current Historical Baseline
|
|
179
|
+
|
|
180
|
+
The repository preserves the old npm package history before new development:
|
|
181
|
+
|
|
182
|
+
- `master` points to the final legacy release commit `3048f15`.
|
|
183
|
+
- `legacy/v1` points to the same final legacy release commit `3048f15`.
|
|
184
|
+
- npm `icore@0.1.38` was published from commit `3048f15`.
|
|
185
|
+
- New alpha development starts from `develop`, not from `master`.
|
|
186
|
+
|
|
187
|
+
Do not delete or rewrite `legacy/v1`. If a future `main` branch is introduced,
|
|
188
|
+
decide that as a separate repository migration and keep `legacy/v1` intact.
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
# Abstraction Policy
|
|
2
|
+
|
|
3
|
+
> Type: Policy. This document defines rules for introducing interfaces, factories, registries, helpers, adapters, and shared modules.
|
|
4
|
+
|
|
5
|
+
## Purpose
|
|
6
|
+
|
|
7
|
+
This policy protects the project from premature generalization.
|
|
8
|
+
|
|
9
|
+
A typical agent mistake is to create an interface, factory, registry, helper, or adapter before real repetition or an architectural boundary appears in the code.
|
|
10
|
+
|
|
11
|
+
## Basic Rule
|
|
12
|
+
|
|
13
|
+
An abstraction appears after repetition or an explicit boundary.
|
|
14
|
+
|
|
15
|
+
Allowed reasons:
|
|
16
|
+
|
|
17
|
+
- the logic is actually repeated;
|
|
18
|
+
- there are two or more implementations;
|
|
19
|
+
- there is an explicit boundary between business logic and infrastructure;
|
|
20
|
+
- the code must be tested separately;
|
|
21
|
+
- the inline solution has become more complex than a named abstraction;
|
|
22
|
+
- the new abstraction reduces coupling between existing parts.
|
|
23
|
+
|
|
24
|
+
Not allowed reasons:
|
|
25
|
+
|
|
26
|
+
- "this is architecturally prettier";
|
|
27
|
+
- "it may be useful later";
|
|
28
|
+
- "other projects do it this way";
|
|
29
|
+
- "a second implementation will appear someday";
|
|
30
|
+
- "I want to hide the details";
|
|
31
|
+
- "the file became visually long".
|
|
32
|
+
|
|
33
|
+
## One Implementation
|
|
34
|
+
|
|
35
|
+
One implementation usually does not require an interface.
|
|
36
|
+
|
|
37
|
+
An interface is allowed with one implementation only if:
|
|
38
|
+
|
|
39
|
+
- it is a public contract;
|
|
40
|
+
- it is a boundary for an infrastructure adapter;
|
|
41
|
+
- it is a test seam without which behavior cannot be checked reliably;
|
|
42
|
+
- it is an external API requirement;
|
|
43
|
+
- there is an owner decision for an extension point.
|
|
44
|
+
|
|
45
|
+
Creating an interface only so that a class "depends on an abstraction" is forbidden.
|
|
46
|
+
|
|
47
|
+
## Helpers
|
|
48
|
+
|
|
49
|
+
A helper is introduced only if it reduces complexity.
|
|
50
|
+
|
|
51
|
+
Allowed:
|
|
52
|
+
|
|
53
|
+
- extract a repeated non-trivial calculation;
|
|
54
|
+
- name a complex condition when the name exposes business meaning;
|
|
55
|
+
- hide an integration detail inside an infrastructure boundary;
|
|
56
|
+
- remove duplication that has already appeared in the current task.
|
|
57
|
+
|
|
58
|
+
Not allowed:
|
|
59
|
+
|
|
60
|
+
- wrap one standard function without a new responsibility;
|
|
61
|
+
- hide an important branch behind a too-generic name;
|
|
62
|
+
- create `utils` for one line;
|
|
63
|
+
- extract two similar lines when the similarity is accidental;
|
|
64
|
+
- create a helper that forces readers to open more files to understand simple behavior.
|
|
65
|
+
|
|
66
|
+
A good helper makes the calling code clearer. A bad helper hides meaning and increases navigation.
|
|
67
|
+
|
|
68
|
+
## Factories
|
|
69
|
+
|
|
70
|
+
A factory is needed when object creation itself has become a separate responsibility.
|
|
71
|
+
|
|
72
|
+
Allowed:
|
|
73
|
+
|
|
74
|
+
- creation logic contains branching;
|
|
75
|
+
- several dependencies must be assembled;
|
|
76
|
+
- there is lifecycle or resource ownership;
|
|
77
|
+
- there are several supported creation modes;
|
|
78
|
+
- the factory is public API.
|
|
79
|
+
|
|
80
|
+
Not allowed:
|
|
81
|
+
|
|
82
|
+
- create `createX()` only instead of `new X()`;
|
|
83
|
+
- extract a constructor call for consistency;
|
|
84
|
+
- create a factory before lifecycle/branching/dependency assembly appears.
|
|
85
|
+
|
|
86
|
+
## Registries
|
|
87
|
+
|
|
88
|
+
A registry is needed only for dynamic selection from a set of known implementations.
|
|
89
|
+
|
|
90
|
+
Allowed:
|
|
91
|
+
|
|
92
|
+
- there are several registered handlers/adapters;
|
|
93
|
+
- selection happens by runtime key;
|
|
94
|
+
- the set of entries is part of the contract;
|
|
95
|
+
- duplicate switch/lookup logic has already appeared.
|
|
96
|
+
|
|
97
|
+
Not allowed:
|
|
98
|
+
|
|
99
|
+
- a registry with one item;
|
|
100
|
+
- a registry "for the future";
|
|
101
|
+
- a registry instead of a simple import;
|
|
102
|
+
- a registry that hides the dependency graph.
|
|
103
|
+
|
|
104
|
+
## Adapters
|
|
105
|
+
|
|
106
|
+
An adapter is needed at a boundary with an external system, library, filesystem, network, framework, or process API.
|
|
107
|
+
|
|
108
|
+
Allowed:
|
|
109
|
+
|
|
110
|
+
- separate an external type from an internal/public contract;
|
|
111
|
+
- isolate library-specific behavior;
|
|
112
|
+
- normalize external errors;
|
|
113
|
+
- hide environment-specific IO.
|
|
114
|
+
|
|
115
|
+
Not allowed:
|
|
116
|
+
|
|
117
|
+
- create an adapter for an internal function without an external boundary;
|
|
118
|
+
- introduce an adapter only for naming symmetry;
|
|
119
|
+
- leak an adapter type into public API without an owner decision.
|
|
120
|
+
|
|
121
|
+
## Shared / Common / Utils
|
|
122
|
+
|
|
123
|
+
A shared module is allowed only when it has a clear responsibility.
|
|
124
|
+
|
|
125
|
+
Forbidden:
|
|
126
|
+
|
|
127
|
+
- create `utils`, `common`, `shared`, or `helpers` as a dumping ground;
|
|
128
|
+
- move code there without naming the responsibility;
|
|
129
|
+
- mix unrelated helpers;
|
|
130
|
+
- use a shared module as a bypass around an architectural boundary.
|
|
131
|
+
|
|
132
|
+
If shared code is needed, the name must describe the role:
|
|
133
|
+
|
|
134
|
+
```text
|
|
135
|
+
config/
|
|
136
|
+
filesystem/
|
|
137
|
+
diagnostics/
|
|
138
|
+
formatting/
|
|
139
|
+
runtime/
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
If the role cannot be named more precisely, the abstraction is not ready yet.
|
|
143
|
+
|
|
144
|
+
## Local Simplicity
|
|
145
|
+
|
|
146
|
+
Local simplicity is more important than "beautiful architecture".
|
|
147
|
+
|
|
148
|
+
Preferred:
|
|
149
|
+
|
|
150
|
+
- keep a simple condition inline;
|
|
151
|
+
- keep one implementation without an interface;
|
|
152
|
+
- keep a direct call without a factory;
|
|
153
|
+
- keep an explicit import without a registry;
|
|
154
|
+
- keep local code next to its only usage.
|
|
155
|
+
|
|
156
|
+
Architectural form is justified only when it reduces current complexity, not when it demonstrates future design.
|
|
157
|
+
|
|
158
|
+
## Frequency Rule
|
|
159
|
+
|
|
160
|
+
Practical rule:
|
|
161
|
+
|
|
162
|
+
```text
|
|
163
|
+
1 use -> inline
|
|
164
|
+
2 uses -> consider whether this is the same responsibility
|
|
165
|
+
3+ uses -> consider an abstraction
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
The number of repetitions is not enough by itself. The repetition must have the same reason to change.
|
|
169
|
+
|
|
170
|
+
## Check Before Extracting
|
|
171
|
+
|
|
172
|
+
Before adding an abstraction, answer:
|
|
173
|
+
|
|
174
|
+
1. What current problem is being solved?
|
|
175
|
+
2. Is there repetition or an explicit boundary?
|
|
176
|
+
3. Will the code be easier to read at the call site?
|
|
177
|
+
4. Is important behavior being hidden?
|
|
178
|
+
5. Is a public/internal contract being created for the future?
|
|
179
|
+
6. Can the task be solved locally in a simpler way?
|
|
180
|
+
|
|
181
|
+
If the answers are unconvincing, the abstraction is not added.
|
|
182
|
+
|
|
183
|
+
## Examples
|
|
184
|
+
|
|
185
|
+
Allowed:
|
|
186
|
+
|
|
187
|
+
- extract repeated config parsing when it is used in several modules with the same reason to change;
|
|
188
|
+
- create an adapter for filesystem access when business logic must not know about IO;
|
|
189
|
+
- create a factory when runtime object creation requires lifecycle and several dependencies;
|
|
190
|
+
- create a registry when there are several handlers and runtime dispatch.
|
|
191
|
+
|
|
192
|
+
Not allowed:
|
|
193
|
+
|
|
194
|
+
- create `utils.ts` for one function;
|
|
195
|
+
- create an interface for a single class without a boundary;
|
|
196
|
+
- create a registry with one handler;
|
|
197
|
+
- create a factory that only calls a constructor;
|
|
198
|
+
- extract a helper so that the calling code loses important context.
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Change Policy
|
|
2
|
+
|
|
3
|
+
> Type: Policy. This document defines the minimum acceptable change rule.
|
|
4
|
+
|
|
5
|
+
## Minimality Of Changes
|
|
6
|
+
|
|
7
|
+
A change must be the minimum necessary change required to solve the task.
|
|
8
|
+
|
|
9
|
+
Forbidden:
|
|
10
|
+
|
|
11
|
+
- large refactoring without a request
|
|
12
|
+
- cleanup without justification
|
|
13
|
+
- style changes outside the affected area
|
|
14
|
+
- architecture changes
|
|
15
|
+
- pipeline changes
|
|
16
|
+
- scripts changes
|
|
17
|
+
- behavior changes
|
|
18
|
+
|
|
19
|
+
Existing code is considered intentional.
|
|
20
|
+
|
|
21
|
+
If a change is not required, it must not be made.
|
|
22
|
+
|
|
23
|
+
## Local Improvements
|
|
24
|
+
|
|
25
|
+
Small improvements are allowed when they are directly related to the current task.
|
|
26
|
+
|
|
27
|
+
Allowed:
|
|
28
|
+
|
|
29
|
+
- remove local duplication
|
|
30
|
+
- improve naming
|
|
31
|
+
- simplify readability
|
|
32
|
+
- fix a nearby obvious defect
|
|
33
|
+
- improve local tests
|
|
34
|
+
|
|
35
|
+
Conditions:
|
|
36
|
+
|
|
37
|
+
- behavior does not change
|
|
38
|
+
- the change remains local
|
|
39
|
+
- the improvement helps solve the current task
|
|
40
|
+
|
|
41
|
+
If an improvement is outside the task scope, it should be proposed separately.
|
|
42
|
+
|
|
43
|
+
## Examples
|
|
44
|
+
|
|
45
|
+
Allowed:
|
|
46
|
+
|
|
47
|
+
- fix a specific defect in one module without rewriting neighboring code
|
|
48
|
+
- add a focused test for a new requirement without changing the existing test pipeline
|
|
49
|
+
- extend an existing `if` branch when this is directly required by the task
|
|
50
|
+
|
|
51
|
+
Not allowed:
|
|
52
|
+
|
|
53
|
+
- mass-rename entities for consistency
|
|
54
|
+
- perform unrelated renames in other modules
|
|
55
|
+
- rewrite an entire module instead of fixing one branch
|
|
56
|
+
- change file structure only because it seems more convenient
|
|
57
|
+
|
|
58
|
+
## Module Boundaries
|
|
59
|
+
|
|
60
|
+
Bad structure should not be preserved only because it already exists.
|
|
61
|
+
|
|
62
|
+
Allowed:
|
|
63
|
+
|
|
64
|
+
- fix poor local naming
|
|
65
|
+
- clarify a module responsibility
|
|
66
|
+
- move a small amount of code to improve readability
|
|
67
|
+
|
|
68
|
+
Not allowed:
|
|
69
|
+
|
|
70
|
+
- redesign a subsystem
|
|
71
|
+
- move files in bulk
|
|
72
|
+
- change architectural boundaries without a request
|
|
73
|
+
|
|
74
|
+
## Good Practices
|
|
75
|
+
|
|
76
|
+
- before making a change, state which exact task requirement the change covers
|
|
77
|
+
- limit the diff to files that actually participate in the task
|
|
78
|
+
- check whether the change affects behavior outside the required scenario
|
|
79
|
+
|
|
80
|
+
## Environment Issues
|
|
81
|
+
|
|
82
|
+
Environment issues are not a reason to change project code.
|
|
83
|
+
|
|
84
|
+
If validation cannot run because of missing tools, permissions, cache, shell utilities, or container limitations, stop, describe the problem, and ask for a decision.
|
|
85
|
+
|
|
86
|
+
Using a local temporary cache/workdir is allowed only if it does not change the project and does not expand the task scope.
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
# Comment Policy
|
|
2
|
+
|
|
3
|
+
> Type: Policy. This document defines rules for writing comments in production code.
|
|
4
|
+
|
|
5
|
+
## Purpose
|
|
6
|
+
|
|
7
|
+
Comments must explain a module role, responsibility boundaries, invariants, and reasons for non-obvious decisions.
|
|
8
|
+
|
|
9
|
+
A comment must not duplicate code, retell obvious mechanics, or replace clear names for functions, types, and variables.
|
|
10
|
+
|
|
11
|
+
The main question a comment must answer is:
|
|
12
|
+
|
|
13
|
+
> Why does this decision exist in this form?
|
|
14
|
+
|
|
15
|
+
Not:
|
|
16
|
+
|
|
17
|
+
> What does this line do?
|
|
18
|
+
|
|
19
|
+
## Basic Rules
|
|
20
|
+
|
|
21
|
+
1. In each production module that participates in runtime, defines a layer contract, or contains non-trivial responsibility, one top architectural block is allowed.
|
|
22
|
+
2. The top architectural block must describe the file role, allowed responsibility, module boundaries, and foreign responsibility that must not be moved into this file.
|
|
23
|
+
3. Local comments are allowed only where the reason for a decision, invariant, or constraint would be lost without them.
|
|
24
|
+
4. Obvious code actions do not need comments.
|
|
25
|
+
5. JSDoc for public contracts must be short and specific.
|
|
26
|
+
6. Within one file, comment style and language must be consistent.
|
|
27
|
+
7. A comment must explain "why" or "for what purpose", not retell "what the line does".
|
|
28
|
+
8. A comment must not expand the code contract. If a comment mentions a guarantee, invariant, execution order, safety, or constraint, it must be backed by code, types, or tests.
|
|
29
|
+
9. Refactoring history must not be stored in comments. Git, ADRs, and design notes exist for that.
|
|
30
|
+
|
|
31
|
+
## Top Architectural Block
|
|
32
|
+
|
|
33
|
+
The top block describes the whole module, not individual functions.
|
|
34
|
+
|
|
35
|
+
Template:
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
/**
|
|
39
|
+
* Module <role of the file in the system>.
|
|
40
|
+
*
|
|
41
|
+
* Allowed here:
|
|
42
|
+
* - <responsibility 1>;
|
|
43
|
+
* - <responsibility 2>;
|
|
44
|
+
* - <responsibility 3>;
|
|
45
|
+
*
|
|
46
|
+
* This file must not contain <foreign responsibility>.
|
|
47
|
+
*/
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Good example:
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
/**
|
|
54
|
+
* The runtime config resolver module transforms external input
|
|
55
|
+
* into normalized launch settings.
|
|
56
|
+
*
|
|
57
|
+
* Allowed here:
|
|
58
|
+
* - reading supported config sources;
|
|
59
|
+
* - validating required fields;
|
|
60
|
+
* - forming diagnostics for invalid config;
|
|
61
|
+
*
|
|
62
|
+
* This file must not contain CLI rendering or network calls.
|
|
63
|
+
*/
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Bad example:
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
/**
|
|
70
|
+
* Config file.
|
|
71
|
+
*/
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
This comment does not explain the module role, boundaries, or allowed responsibility.
|
|
75
|
+
|
|
76
|
+
## Exceptions
|
|
77
|
+
|
|
78
|
+
A top architectural block is not required for:
|
|
79
|
+
|
|
80
|
+
- barrel export files;
|
|
81
|
+
- build output;
|
|
82
|
+
- test fixtures;
|
|
83
|
+
- trivial files with one obvious type or constant;
|
|
84
|
+
- temporary local helper files that do not define a production contract.
|
|
85
|
+
|
|
86
|
+
If a file participates in production runtime, defines a layer contract, or contains non-trivial responsibility, a top block is allowed and must be meaningful.
|
|
87
|
+
|
|
88
|
+
## Public Contract JSDoc
|
|
89
|
+
|
|
90
|
+
JSDoc for public functions, classes, and interfaces must describe only contract-level details:
|
|
91
|
+
|
|
92
|
+
- input constraints;
|
|
93
|
+
- side effects;
|
|
94
|
+
- errors;
|
|
95
|
+
- lifecycle;
|
|
96
|
+
- important guarantees;
|
|
97
|
+
- external API constraints.
|
|
98
|
+
|
|
99
|
+
JSDoc is not needed if the meaning is fully clear from the name, types, and code.
|
|
100
|
+
|
|
101
|
+
Bad:
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
/**
|
|
105
|
+
* Creates a client.
|
|
106
|
+
*/
|
|
107
|
+
createClient(options);
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Better:
|
|
111
|
+
|
|
112
|
+
```ts
|
|
113
|
+
/**
|
|
114
|
+
* Creates a runtime client from already validated options.
|
|
115
|
+
*
|
|
116
|
+
* The function does not read environment and does not change global state.
|
|
117
|
+
*/
|
|
118
|
+
createClient(options);
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
But if this is already obvious from the interface and tests, it is better not to add the comment.
|
|
122
|
+
|
|
123
|
+
## When A Comment Is Needed
|
|
124
|
+
|
|
125
|
+
A comment is needed if it explains:
|
|
126
|
+
|
|
127
|
+
- module role;
|
|
128
|
+
- layer boundary;
|
|
129
|
+
- non-obvious invariant;
|
|
130
|
+
- workaround;
|
|
131
|
+
- external API constraint;
|
|
132
|
+
- reason for an unusual branch;
|
|
133
|
+
- reason for cleanup logic;
|
|
134
|
+
- preservation of compatibility with an old contract;
|
|
135
|
+
- an intentionally empty branch when absence of action is part of the logic;
|
|
136
|
+
- placeholder / TODO with an explanation of why implementation is absent for now;
|
|
137
|
+
- reason why a dependency is allowed exactly here.
|
|
138
|
+
|
|
139
|
+
Good:
|
|
140
|
+
|
|
141
|
+
```ts
|
|
142
|
+
// CLI config has priority over file config:
|
|
143
|
+
// this is an observable command contract, not a merge order detail.
|
|
144
|
+
const options = mergeConfig(fileConfig, cliConfig);
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Good:
|
|
148
|
+
|
|
149
|
+
```ts
|
|
150
|
+
// The first request is not delayed:
|
|
151
|
+
// the rate limit constrains the interval between subsequent calls.
|
|
152
|
+
if (lastRequestTime === undefined) {
|
|
153
|
+
lastRequestTime = now;
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## When A Comment Is Not Needed
|
|
159
|
+
|
|
160
|
+
A comment is not needed if:
|
|
161
|
+
|
|
162
|
+
- the meaning is already clear from the function name, type, and code;
|
|
163
|
+
- the comment repeats the line literally;
|
|
164
|
+
- the comment describes trivial mechanics;
|
|
165
|
+
- the comment explains language syntax;
|
|
166
|
+
- the comment becomes outdated faster than the code itself;
|
|
167
|
+
- the comment stores change history instead of the current reason for the decision.
|
|
168
|
+
|
|
169
|
+
Bad:
|
|
170
|
+
|
|
171
|
+
```ts
|
|
172
|
+
// Create options
|
|
173
|
+
const options = resolveOptions(input);
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Bad:
|
|
177
|
+
|
|
178
|
+
```ts
|
|
179
|
+
// Check if path exists
|
|
180
|
+
if (fs.existsSync(file)) {
|
|
181
|
+
return file;
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## TODO And Placeholder
|
|
186
|
+
|
|
187
|
+
A TODO is allowed only if it explains:
|
|
188
|
+
|
|
189
|
+
- why implementation is deferred;
|
|
190
|
+
- which constraint is currently in effect;
|
|
191
|
+
- under which condition this place must be revisited.
|
|
192
|
+
|
|
193
|
+
Good:
|
|
194
|
+
|
|
195
|
+
```ts
|
|
196
|
+
// TODO: streaming mode requires an owner decision on public API shape.
|
|
197
|
+
// Revisit after supported options are approved.
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Bad:
|
|
201
|
+
|
|
202
|
+
```ts
|
|
203
|
+
// TODO: fix later
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
If a module or branch is intentionally empty, this must be stated explicitly.
|
|
207
|
+
|
|
208
|
+
## Workaround Comments
|
|
209
|
+
|
|
210
|
+
A workaround must explain the external constraint, not just describe the workaround.
|
|
211
|
+
|
|
212
|
+
Good:
|
|
213
|
+
|
|
214
|
+
```ts
|
|
215
|
+
// External API returns an empty string instead of a missing value:
|
|
216
|
+
// normalize it here so the domain layer works only with undefined.
|
|
217
|
+
const value = rawValue === '' ? undefined : rawValue;
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Bad:
|
|
221
|
+
|
|
222
|
+
```ts
|
|
223
|
+
// Normalize value
|
|
224
|
+
const value = rawValue === '' ? undefined : rawValue;
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
## Comments And Architectural Boundaries
|
|
228
|
+
|
|
229
|
+
A comment is useful when it protects a layer boundary.
|
|
230
|
+
|
|
231
|
+
Good:
|
|
232
|
+
|
|
233
|
+
```ts
|
|
234
|
+
/**
|
|
235
|
+
* CLI entrypoint only transforms argv into command input.
|
|
236
|
+
*
|
|
237
|
+
* This file must not contain domain behavior, persistence, or network orchestration.
|
|
238
|
+
*/
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
This block helps avoid mixing bootstrap and runtime behavior.
|
|
242
|
+
|
|
243
|
+
## Good Comment Criterion
|
|
244
|
+
|
|
245
|
+
A good comment helps understand:
|
|
246
|
+
|
|
247
|
+
- why the module exists;
|
|
248
|
+
- where its boundaries are;
|
|
249
|
+
- which invariant must not be violated;
|
|
250
|
+
- which external constraint affected the code;
|
|
251
|
+
- why an obviously strange decision is actually intentional.
|
|
252
|
+
|
|
253
|
+
If a comment can be deleted without losing meaning, it should be deleted.
|