Browse pages
Engineering Principles
These principles resolve choices no narrower rule covers.
Intent#
These principles resolve choices no narrower rule covers. They favor explicit intent, proven behavior, and evidence over speculative abstractions.
Standards#
Keep one authored source#
ruleguidancelitespec/core/principles#keep-one-authored-sourceA consumer MUST author each record once in its home file and derive every other appearance from the model.
Why
A requirement lives on its product topic page. A rule lives on its home page. A use case lives on its page.
A term lives in the language record. Stack versions live in the manifest. Coverage, history, and status derive from the model and nobody types them by hand.
Checka reviewer answers
Does every fact on the changed pages trace to one authored record?
Marked guidance: a reviewer answers this question.
Locate package versions in the manifest#
rulecheckablelitespec/core/principles#locate-package-versions-in-the-manifestA consumer MUST resolve the standards-selected stack versions from litespec.manifest.json.
Why
The manifest is the version authority for the stack the standards select. Consumer dependencies live in package manifests and lockfiles.
Checkchecked by a tool
litespec consumer fails a project whose stack versions do not resolve from the manifest.
Marked checkable: the tool named here checks it.
Locate record status in frontmatter#
rulecheckablelitespec/core/principles#locate-record-status-in-frontmatterA consumer MUST carry each record's status, owner, decider, and decideBy date in its frontmatter.
Why
Frontmatter exposes status to tools and reviewers. Entries inherit their page's owner and status unless they set their own. The model page defines the common fields.
Checkchecked by a tool
litespec consumer fails a record whose frontmatter is missing a required common field.
Marked checkable: the tool named here checks it.
Prove cases with tests#
ruleguidancelitespec/core/principles#prove-cases-with-testsA test MUST prove a case only by calling its use case and asserting its result.
Why
A passing test that never calls the use case proves nothing. A case without a proving test is a known gap. The model page defines the proves link.
Checka reviewer answers
Does every case have a passing test that calls its use case and asserts its result?
Marked guidance: a reviewer answers this question.
Classify record authority#
rulecheckablelitespec/core/principles#classify-record-authorityA record MUST carry exactly one status of draft, approved, or retired, with a draft naming its decider and decideBy date.
Why
Draft records are under review. Approved records are authoritative. Retired records stay readable as the past.
Checkchecked by a tool
litespec consumer fails a record with a missing or unknown status, or a draft without decider and decideBy date.
Marked checkable: the tool named here checks it.
Refuse approval behind a draft decision#
ruleguidancelitespec/core/principles#refuse-approval-behind-a-draft-decisionA record MUST NOT be approved while a decision behind it is a draft.
Why
Approval on a draft turns an undecided choice into an authoritative record. Approve the decision first, then the records behind it.
Checka reviewer answers
Is every decision behind an approved record approved itself?
Marked guidance: a reviewer answers this question.
Separate authority from implementation#
rulecheckablelitespec/core/principles#separate-authority-from-implementationA record MUST NOT write its own implementation status, because implemented and verified are computed from code and test runs.
Why
An approved planned target is not an implemented behavior claim. The model page defines how the model computes both.
Checkchecked by a tool
litespec consumer fails a record that writes its own implementation status.
Marked checkable: the tool named here checks it.
Retire public behavior deliberately#
rulecheckablelitespec/core/principles#retire-public-behavior-deliberatelyA retired use case MUST name no entry point in reachedThrough.
Why
A retired record cannot own still-supported public behavior. Remove the route before retiring the record.
Checkchecked by a tool
litespec consumer fails a retired use case that still names an entry point.
Marked checkable: the tool named here checks it.
Prove enforceable boundaries mechanically#
ruleguidancelitespec/core/principles#prove-enforceable-boundaries-mechanicallyA consumer MUST prove each enforceable boundary mechanically: a reference, visibility, an architecture test, a behavior test, or a lint rule.
Why
Tools prove structural constraints, while specifications explain decisions that require judgment. Enforceable boundaries are project and package references, compiler visibility, permission guards, store constraints, and lint rules.
Example
A project-reference test proves Domain has no Infrastructure reference.
Checka reviewer answers
Does a reference, test, or lint rule prove each changed enforceable boundary?
Marked guidance: a reviewer answers this question.
Require current complexity activation#
ruleguidancelitespec/core/principles#require-current-complexity-activationA consumer MUST add packages, projects, wrappers, background processes, caches, queues, or distributed patterns only for a current documented requirement.
Why
A use case, a decision, or a requirement names the activation criterion. Preference and possible future need do not establish one.
Checka reviewer answers
Does a current use case, decision, or requirement name the added complexity?
Marked guidance: a reviewer answers this question.
Name boundary intent#
ruleguidancelitespec/core/principles#name-boundary-intentA consumer MUST use business operation names, specific command and query mediators, and business-action external ports at boundaries.
Why
Specific names reveal purpose and ownership at the point where components interact.
Checka reviewer answers
Does every changed boundary name state its business action and owner?
Marked guidance: a reviewer answers this question.
Avoid generic boundary names#
ruleguidancelitespec/core/principles#avoid-generic-boundary-namesA consumer MUST NOT use generic bus, manager, helper, processor, or service names when a narrower name exists.
Why
Generic names obscure the business action and invite unrelated responsibility.
Example
IPostPublicationNotifier names an action; IExternalService does not.
Checka reviewer answers
Does every changed boundary name carry the narrowest accurate business name?
Marked guidance: a reviewer answers this question.
Conventions#
Prefer direct owned dependencies#
conventionguidancelitespec/core/principles#prefer-direct-owned-dependenciesUse a selected framework type directly inside the layer that owns it until an architectural boundary exists.
ReplacementA consumer records an Exception naming this provision, with its reason and review date.
Why
A project-owned abstraction exists for provider replacement, test boundary, stable domain concept, or architecture boundary.
Checka reviewer answers
Is each new abstraction justified by a boundary, or recorded as an exception?
Marked guidance: a reviewer answers this question.
Prefer local code until reuse is real#
conventionguidancelitespec/core/principles#prefer-local-code-until-reuse-is-realKeep operation-specific code in its operation folder until two real consumers need the same precise behavior.
ReplacementA consumer records an Exception naming this provision, with its reason and review date.
Why
Shared code requires a name and responsibility that remain accurate for both consumers.
Checka reviewer answers
Does each new shared unit serve two real consumers under one accurate name?
Marked guidance: a reviewer answers this question.
Reference example#
This informative example demonstrates litespec/core/principles#prefer-direct-owned-dependencies and litespec/core/principles#prefer-local-code-until-reuse-is-real.
- Application query handlers use
IQuerySessionbecause Marten is part of the selected stack. - Domain repository interfaces remain project-owned because persistence cannot enter Domain.
- A two-line mapping used once stays beside its endpoint.
Verification#
| ID | Mark | Evidence |
|---|---|---|
| litespec/core/principles#keep-one-authored-source | guidance | Does every fact on the changed pages trace to one authored record? |
| litespec/core/principles#locate-package-versions-in-the-manifest | checkable | litespec consumer fails a project whose stack versions do not resolve from the manifest. |
| litespec/core/principles#locate-record-status-in-frontmatter | checkable | litespec consumer fails a record whose frontmatter is missing a required common field. |
| litespec/core/principles#prove-cases-with-tests | guidance | Does every case have a passing test that calls its use case and asserts its result? |
| litespec/core/principles#classify-record-authority | checkable | litespec consumer fails a record with a missing or unknown status, or a draft without decider and decideBy date. |
| litespec/core/principles#refuse-approval-behind-a-draft-decision | guidance | Is every decision behind an approved record approved itself? |
| litespec/core/principles#separate-authority-from-implementation | checkable | litespec consumer fails a record that writes its own implementation status. |
| litespec/core/principles#retire-public-behavior-deliberately | checkable | litespec consumer fails a retired use case that still names an entry point. |
| litespec/core/principles#prove-enforceable-boundaries-mechanically | guidance | Does a reference, test, or lint rule prove each changed enforceable boundary? |
| litespec/core/principles#require-current-complexity-activation | guidance | Does a current use case, decision, or requirement name the added complexity? |
| litespec/core/principles#name-boundary-intent | guidance | Does every changed boundary name state its business action and owner? |
| litespec/core/principles#avoid-generic-boundary-names | guidance | Does every changed boundary name carry the narrowest accurate business name? |
| litespec/core/principles#prefer-direct-owned-dependencies | guidance | Is each new abstraction justified by a boundary, or recorded as an exception? |
| litespec/core/principles#prefer-local-code-until-reuse-is-real | guidance | Does each new shared unit serve two real consumers under one accurate name? |