Current State
In packages/errors/src/error/error.ts:87, the inner function returned by error() is named ErrorFactoryInstance. The name has two problems:
- It is misleading. The function is not an instance — it is the factory that produces instances. The
ErrorFactory type (the contract) and ErrorFactoryInstance (this internal function) are conflated.
- It violates two rules:
- Rule 0013 (Entity-First Naming): the name
ErrorFactoryInstance is a -er shape (Factory) with a redundant suffix. The rule's exception list (test names, brief variables) does not cover local function names.
- Rule 0016 (No Generic Verbs): the name does not encode the transformation.
ErrorFactoryInstance is "factory instance" — a noun phrase, not a verb-noun phrase. A reader cannot tell from the name what the function does, only what it is a kind of.
The function body is 50 lines (lines 87-134) and does several things: format the message, capture the stack, build the instance, attach the cause chain method, attach the addNote method, and mark the factory. A single function doing six things under a generic name is the exact pattern rule 0005 / 0007 warn against.
Located in:
packages/errors/src/error/error.ts:87-134
Proposed State
After refactoring, the inner function is renamed to encode the transformation, and its body is decomposed into named operations consistent with rule 0007 (top-down composition):
export const error = <const T extends Record<string, unknown> = Record<string, never>>(config: {
name: string;
fields?: StandardSchemaV1;
inherits?: ErrorFactory | ErrorFactory[];
message?: string;
}): ErrorFactory<T> => {
const { name, fields, inherits, message } = config;
const createError = (input?: Partial<T>): ErrorInstance<T> => {
const fieldsData = (input || {}) as T;
const instance = buildErrorInstance(name, message, fieldsData);
attachFactoryMarker(instance, createError);
return instance;
};
// ... metadata attachment ...
return createError as ErrorFactory<T>;
};
Where buildErrorInstance, attachFactoryMarker, and the message-formatting helper are extracted into the renamed message-template.ts (see P0 #6) and stack-trace.ts files.
Expected improvements:
- Rule 0013 compliance: the function is named for what it does (
createError), not what it is a kind of.
- Rule 0016 compliance: the verb (
create) is specific; the return type (ErrorInstance<T>) is the result.
- Rule 0007 compliance: the function reads top-down — the first line is the outcome, every subsequent line is a name the reader follows.
- Rule 0005 compliance: the algorithm is named, not inlined.
Motivation
This refactoring is needed because:
- The function name is a smell that the rules explicitly forbid; the library should not be a counter-example to its own rules.
- The function body does six things; decomposing them is the kind of refactor that pays for itself the next time a feature is added.
- The consumer-facing API (
error({ name, fields, inherits, message })) does not change; the refactor is internal.
Triggers for this work:
Risks
Potential risks:
- Risk 1: Renaming the inner function affects the
FACTORY_SYMBOL marker, which is checked at runtime in is/index.ts. — Mitigation: the rename is local; the marker is attached by reference (ErrorFactoryInstance becomes createError); the is() consumer side is unaffected because it only compares references.
- Risk 2: Splitting the body into multiple functions introduces closure overhead. — Mitigation: V8 inlines aggressively for short closures; the named functions are pure utilities with no closure dependencies. Performance is unaffected.
- Risk 3: The
as ErrorFactory<T> cast at the return site requires care. — Mitigation: the cast is already present; the refactor preserves the same shape.
Migration Plan
Migration approach:
- Rename
ErrorFactoryInstance to createError (or buildError, depending on the convention chosen).
- Extract
buildErrorInstance(name, message, fieldsData) as a top-level function in message-template.ts or a new error/build.ts.
- Extract
attachFactoryMarker(instance, factory) as a top-level function in the same file.
- Update the
FACTORY_SYMBOL assignment to use the new name.
- Run the test suite; the public API is unchanged.
Rollback plan: revert the PR.
Backward Compatibility
Scope
Files/Folders affected:
packages/errors/src/error/error.ts (rename + extract)
packages/errors/src/error/types.ts (if new helpers are added)
packages/errors/src/is/index.ts (FACTORY_SYMBOL reference, no behaviour change)
Component(s) Affected
Note: the component_affected dropdown is calibrated for a web template project. The actual affected component is packages/errors.
Priority
Estimated Effort
Test Coverage Requirements
Testing Approach
Testing strategy:
- Unit tests: existing
error() test cases (single, multiple inheritance, with/without fields, with/without message template) should all pass.
- New test: confirm that the
FACTORY_SYMBOL marker is attached by the new attachFactoryMarker helper, and that is(err, factory) still discriminates correctly.
Verification steps:
pnpm --filter @deessejs/errors test:run
pnpm --filter @deessejs/errors type-check
pnpm --filter @deessejs/errors build — public dist/ output matches the previous version's public types.
Related Issues / Pull Requests
Relevant Documentation
- Architecture doc:
docs/engineering/architecture/rules/0013-entity-first-naming.md
- Architecture doc:
docs/engineering/architecture/rules/0016-no-generic-verbs.md
Pre-Submission Checklist
Current State
In
packages/errors/src/error/error.ts:87, the inner function returned byerror()is namedErrorFactoryInstance. The name has two problems:ErrorFactorytype (the contract) andErrorFactoryInstance(this internal function) are conflated.ErrorFactoryInstanceis a-ershape (Factory) with a redundant suffix. The rule's exception list (test names, brief variables) does not cover local function names.ErrorFactoryInstanceis "factory instance" — a noun phrase, not a verb-noun phrase. A reader cannot tell from the name what the function does, only what it is a kind of.The function body is 50 lines (lines 87-134) and does several things: format the message, capture the stack, build the instance, attach the cause chain method, attach the addNote method, and mark the factory. A single function doing six things under a generic name is the exact pattern rule 0005 / 0007 warn against.
Located in:
packages/errors/src/error/error.ts:87-134Proposed State
After refactoring, the inner function is renamed to encode the transformation, and its body is decomposed into named operations consistent with rule 0007 (top-down composition):
Where
buildErrorInstance,attachFactoryMarker, and the message-formatting helper are extracted into the renamedmessage-template.ts(see P0 #6) andstack-trace.tsfiles.Expected improvements:
createError), not what it is a kind of.create) is specific; the return type (ErrorInstance<T>) is the result.Motivation
This refactoring is needed because:
error({ name, fields, inherits, message })) does not change; the refactor is internal.Triggers for this work:
Risks
Potential risks:
FACTORY_SYMBOLmarker, which is checked at runtime inis/index.ts. — Mitigation: the rename is local; the marker is attached by reference (ErrorFactoryInstancebecomescreateError); theis()consumer side is unaffected because it only compares references.as ErrorFactory<T>cast at the return site requires care. — Mitigation: the cast is already present; the refactor preserves the same shape.Migration Plan
Migration approach:
ErrorFactoryInstancetocreateError(orbuildError, depending on the convention chosen).buildErrorInstance(name, message, fieldsData)as a top-level function inmessage-template.tsor a newerror/build.ts.attachFactoryMarker(instance, factory)as a top-level function in the same file.FACTORY_SYMBOLassignment to use the new name.Rollback plan: revert the PR.
Backward Compatibility
Scope
Files/Folders affected:
packages/errors/src/error/error.ts(rename + extract)packages/errors/src/error/types.ts(if new helpers are added)packages/errors/src/is/index.ts(FACTORY_SYMBOL reference, no behaviour change)Component(s) Affected
Note: the
component_affecteddropdown is calibrated for a web template project. The actual affected component ispackages/errors.Priority
Estimated Effort
Test Coverage Requirements
Testing Approach
Testing strategy:
error()test cases (single, multiple inheritance, with/without fields, with/without message template) should all pass.FACTORY_SYMBOLmarker is attached by the newattachFactoryMarkerhelper, and thatis(err, factory)still discriminates correctly.Verification steps:
pnpm --filter @deessejs/errors test:runpnpm --filter @deessejs/errors type-checkpnpm --filter @deessejs/errors build— publicdist/output matches the previous version's public types.Related Issues / Pull Requests
packages/errors/src/against rules 0001-0016, August 2026.docs/engineering/architecture/rules/0013-entity-first-naming.md.docs/engineering/architecture/rules/0016-no-generic-verbs.md.docs/engineering/architecture/rules/0007-top-down-composition.md.Relevant Documentation
docs/engineering/architecture/rules/0013-entity-first-naming.mddocs/engineering/architecture/rules/0016-no-generic-verbs.mdPre-Submission Checklist