Use when writing, modifying, or reviewing code — especially when adding to files with poor naming, missing structure, or messy patterns that tempt you to match existing style
Universal, language-agnostic code style. These principles apply to every codebase regardless of language or framework. Sibling skills: forge-docs for docs, forge-tests for tests, forge-review for structured audits.
When you touch messy code, improve what you touch. Don't conform to bad patterns. If the surrounding code has generic names, mode-parameter functions, or no structure — fix what's in your path. You are not obligated to match existing bad style.
This means: if you're adding deleteUser to a file that has doStuff(action) and h(email), you rename doStuff to createUser/findUser, rename h to isValidEmail, and add section dividers — not just append your function to the bottom.
Don't rationalize inaction:
Don't build for hypotheticals. Abstraction is a cost — pay it only when repetition forces your hand.
// bad: premature generalization for one use case
function transformData(data, format, options, callback)
// good: do the specific thing
function csvToJson(data)
Names are the first layer of documentation. Describe what something is or does, not how it works.
userRecords not hashMap)isVisible, hasChildren, canEdit)fetchUser, parseConfig)data, item, temp) unless scope is under ~5 linesuser in one place, don't call it account elsewhere// bad
function process(d)
temp = d.val * 1.1
return temp
// good
function applyTax(price)
taxedPrice = price * 1.1
return taxedPrice
Each unit (function, file, module) has one clear responsibility you can state in a short sentence. If you struggle to name it, it's doing too much.
// bad: two functions taped together
function handleUser(user, mode)
if mode == "create" ...
if mode == "update" ...
// good
function createUser(user)
function updateUser(user)
A file's organization lets a reader scan its shape and find what they need without reading every line.
types file unless shared// ── Section Name ────────────────────────────// ── Public API ──────────────────────────
function createEditor(config)
function destroyEditor(editor)
// ── Internal ────────────────────────────
function initBuffer(config)
function attachListeners(editor)
Comments explain why, never what. If the code needs a comment to explain what it does, improve the code first.
// increment counter above counter++// bad: narrates the obvious
// loop through users and check if active
for user in users
if user.isActive ...
// good: explains a non-obvious data quirk
// expired trials still show as "active" in the DB;
// filter by lastLogin to catch actual usage
for user in users
if user.isActive and user.lastLogin > cutoff ...
Each commit is a self-contained, reviewable unit. The message says what changed in as few words as possible.
+ new, - removal, ~ tweak, > larger change, ! bugfix, @ docs/config+ (auth) session tokens// bad
fixed stuff
Updated code
// good
! (parser) off-by-one in heading detection
+ (api) rate limiting middleware
A directory should reflect a decision, not an accident. The enemy isn't any specific topology — Rails-style layer-slicing, Clean Architecture layering, data-oriented clustering, and kernel-style deep taxonomies all work when chosen on purpose. The enemy is "I didn't know where else to put it."
Four diagnostic questions for any directory:
parser/, auth/, users/) survive refactors. Directories named after what they do (managers/, handlers/, services/, providers/, utils/) don't — architectural roles drift. Anything ending in -ers or -ors is usually a shelf, not a boundary.// bad: accidents dressed as decisions
src/
utils/ — dumping ground
helpers/ — second dumping ground, overlaps with utils/
managers/ — role-named; what do they manage?
common/ — third dumping ground
// good: decisions you can defend
src/
auth/ — everything that touches identity
billing/ — everything that touches payments
parser/ — everything that touches the syntax tree
| Principle | One-line rule |
|---|---|
| Cardinal Rule | Improve what you touch — don't match existing bad style |
| Simplicity | No abstraction until the third repetition |
| Naming | Name by role/purpose, not implementation |
| Decomposition | If you'd say "and" to describe it, split it |
| File Structure | Section dividers, public API at top, colocate types |
| Comments | Explain why, never what |
| Commits | Symbol prefix, lowercase, one logical change per commit |
| Directory Structure | A directory should reflect a decision, not an accident |
| Mistake | Fix |
|---|---|
| Matching existing bad style when adding code | Improve what you touch — rename, restructure, add dividers |
| Commenting what code does instead of why | Delete the comment or improve the code |
Creating utils/helpers/common dumping grounds | Name the concept that lives there (auth, parser, billing), or move each file next to its real consumer |
| Over-documenting with JSDoc on every function | Doc comments only where the signature doesn't tell the story |
| Giant orchestrator function that "does one thing" | If it's 50+ lines of sequential steps, the steps are the functions |
Role-named directories (managers/, services/, handlers/, providers/) | Name by the noun that lives there. Anything ending in -ers or -ors is probably a shelf, not a boundary |