Skip to content

Commit baaed65

Browse files
committed
init
0 parents  commit baaed65

82 files changed

Lines changed: 15667 additions & 0 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/mlc_config.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"ignorePatterns": [
3+
{ "pattern": "^https://github.com/yunbow/" }
4+
],
5+
"timeout": "10s",
6+
"retryOn429": true,
7+
"aliveStatusCodes": [200, 206, 301, 302, 403]
8+
}

.github/workflows/lint.yml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
name: Lint & Link Check
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
jobs:
10+
markdown-lint:
11+
runs-on: ubuntu-latest
12+
steps:
13+
- uses: actions/checkout@v4
14+
- uses: DavidAnson/markdownlint-cli2-action@v19
15+
with:
16+
globs: "**/*.md"
17+
config: |
18+
{
19+
"MD013": false,
20+
"MD033": false,
21+
"MD041": false
22+
}
23+
24+
link-check:
25+
runs-on: ubuntu-latest
26+
steps:
27+
- uses: actions/checkout@v4
28+
- uses: gaurav-nelson/github-action-markdown-link-check@v1
29+
with:
30+
use-quiet-mode: 'yes'
31+
config-file: '.github/mlc_config.json'
32+
33+
yaml-lint:
34+
runs-on: ubuntu-latest
35+
steps:
36+
- uses: actions/checkout@v4
37+
- name: Check YAML/JSON syntax
38+
run: |
39+
find . -name "*.json" -not -path "./.git/*" -exec python3 -c "import json,sys; json.load(open(sys.argv[1]))" {} \;

.gitignore

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# OS
2+
.DS_Store
3+
Thumbs.db
4+
Desktop.ini
5+
6+
# Editor
7+
.vscode/
8+
.idea/
9+
*.swp
10+
*.swo
11+
*~
12+
13+
# AI coding tools (user-local config — not part of this repo)
14+
.claude/
15+
CLAUDE.md
16+
.kiro/
17+
AGENTS.md
18+
.cursor/
19+
.cursorrules

01_philosophy/anti-patterns.md

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
$NOTE
2+
# Anti-Patterns
3+
4+
This defines repeatedly observed "do not do this" patterns.
5+
Each anti-pattern includes why it is problematic and what should be done instead.
6+
7+
---
8+
9+
## 1. Abandoning Type Safety
10+
11+
### Careless Use of `any`
12+
13+
```ts
14+
// ❌ Creates a hole in the type system
15+
const data: any = await fetchData()
16+
data.nonExistent.property // Explodes at runtime
17+
18+
// ✔ unknown + type guard
19+
const data: unknown = await fetchData()
20+
if (isValidResponse(data)) {
21+
data.property // Type-safe
22+
}
23+
```
24+
25+
**Why it's problematic**: `any` disables type checking. A single `any` propagates through type inference — when `any` is assigned to a variable, every expression derived from it also becomes `any`, silently disabling checks across the entire call chain without compiler warnings.
26+
**Exception**: Inadequate type definitions in external libraries. Always attach a `// FIXME` comment.
27+
28+
---
29+
30+
## 2. Architectural Deviation
31+
32+
### Direct fetch from Components
33+
34+
```ts
35+
// ❌ Skipping layers
36+
function MyComponent() {
37+
const [data, setData] = useState(null)
38+
useEffect(() => {
39+
fetch('/api/data').then(r => r.json()).then(setData)
40+
}, [])
41+
}
42+
43+
// ✔ Go through Server Actions + Hooks
44+
function MyComponent() {
45+
const { data } = useListData({ action: fetchDataAction })
46+
}
47+
```
48+
49+
**Why it's problematic**: Error handling, authentication, caching, and loading states end up implemented inconsistently.
50+
51+
### Introducing Global State Management Libraries
52+
53+
```ts
54+
// ❌ Introducing Redux / Zustand / Recoil
55+
const useStore = create((set) => ({ ... }))
56+
57+
// ✔ Server Actions + local state + Feature Context
58+
const [state, setState] = useState(initial)
59+
// or
60+
const { value } = useFeatureContext()
61+
```
62+
63+
**Why it's problematic**: Global state obscures the origin of data and undermines Server Actions' role as the source of truth.
64+
65+
---
66+
67+
## 3. Lack of Observability
68+
69+
### Missing Trace ID
70+
71+
```ts
72+
// ❌ Cannot identify which request caused the error
73+
logger.error('Something went wrong')
74+
75+
// ✔ Traceable via Request ID
76+
logger.error({ requestId, userId, action }, 'Payment processing failed')
77+
```
78+
79+
**Why it's problematic**: Without it, requests cannot be traced end-to-end during incident investigation.
80+
81+
---
82+
83+
## 4. Over-Abstraction
84+
85+
### Generalizing Code Used in Only One Place
86+
87+
```ts
88+
// ❌ Abstraction with no reuse potential
89+
function useSinglePurposeHook() { ... }
90+
function createSingleUseFactory(options) { ... }
91+
92+
// ✔ Write it inline. Extract only after duplication appears in 3 places
93+
```
94+
95+
**Why it's problematic**: Abstraction increases comprehension cost. An abstraction used in only one place leaves only the cost.
96+
97+
### Bloated Configuration Objects
98+
99+
```ts
100+
// ❌ Too many options = the abstraction is heading in the wrong direction
101+
createAction({
102+
schema, handler, middleware, errorHandler,
103+
retryPolicy, cachePolicy, rateLimitPolicy, auditPolicy,
104+
})
105+
106+
// ✔ Extract only the common parts; implement special cases individually
107+
const baseAction = createBaseAction(commonOptions)
108+
const specialAction = async (input) => {
109+
// Special logic
110+
return baseAction(transformedInput)
111+
}
112+
```
113+
114+
**Why it's problematic**: A massive configuration object is not "flexible" — it is "complex."
115+
116+
---
117+
118+
## 5. Misuse of Naming and Comments
119+
120+
### Vague Naming
121+
122+
```ts
123+
//
124+
const data = await getData()
125+
const result = process(data)
126+
function handleClick() { ... }
127+
128+
//
129+
const userProfile = await fetchUserProfile()
130+
const validatedOrder = validateOrder(rawOrder)
131+
function handleSubmitPayment() { ... }
132+
```
133+
134+
**Why it's problematic**: `data`, `result`, `handle` convey nothing. The reader must read the implementation to understand the intent.
135+
136+
### Comments That Explain Logic
137+
138+
```ts
139+
// ❌ Stating what the code already says
140+
// Check if user's age is 18 or above
141+
if (user.age >= 18) { ... }
142+
143+
// ✔ Document intent, exceptions, and side effects
144+
// Legal requirement: minors cannot use the payment feature (see compliance guidelines)
145+
if (user.age >= 18) { ... }
146+
```
147+
148+
**Why it's problematic**: "What it does" is told by the code. Comments should tell "why it does it that way."
149+
150+
---
151+
152+
## 6. Inadequate Testing
153+
154+
### Testing Only the Happy Path
155+
156+
```ts
157+
// ❌ Only the success case
158+
test('creates user', async () => {
159+
const user = await createUser(validInput)
160+
expect(user).toBeDefined()
161+
})
162+
163+
// ✔ Include error cases, boundary values, and security scenarios
164+
test('rejects invalid email', ...)
165+
test('prevents IDOR access', ...)
166+
test('handles duplicate webhook events', ...)
167+
test('fails gracefully on external API timeout', ...)
168+
```
169+
170+
**Why it's problematic**: Bugs occur in error cases and at boundary values, not in the happy path.
171+
172+
---
173+
174+
## 7. Ignoring Performance
175+
176+
### Huge Bundle Sizes
177+
178+
```ts
179+
// ❌ Importing the entire library
180+
import _ from 'lodash'
181+
import moment from 'moment'
182+
183+
// ✔ Import only the needed functions / use lightweight alternatives
184+
import { debounce } from 'lodash/debounce'
185+
import dayjs from 'dayjs'
186+
```
187+
188+
**Why it's problematic**: Bundle size directly impacts load time. As a reference point, research by Google and Akamai shows that each additional 100KB of JavaScript adds roughly 100–300ms of parse/compile time on mobile devices, and pages loading in over 3 seconds see significantly higher bounce rates. Tree-shaking and selective imports keep the bundle lean.

01_philosophy/mental-models.md

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
$NOTE
2+
# Mental Models
3+
4+
This defines the thinking patterns behind design decisions.
5+
These are not concrete rules, but the "way of thinking" from which rules emerge.
6+
7+
---
8+
9+
## 1. Constraints Breed Creativity
10+
11+
**"Fewer choices lead to faster and better decisions."**
12+
13+
"Faster" here means reduced decision fatigue and shorter code review cycles. When the framework constrains choices (e.g., "no Enums, use union literals"), developers spend zero time debating the approach and reviewers can focus on logic rather than style. This compounds across a team — eliminating 10 micro-decisions per PR across 50 PRs/week saves meaningful cognitive load.
14+
15+
- `strict: true` doesn't take away freedom — it takes away room for bugs
16+
- Tailwind's utility classes don't restrict expression — they guarantee consistency
17+
- "No Enums," "No any," "No console.log" are constraints that raise the quality floor
18+
19+
> Unlimited freedom produces unlimited inconsistency.
20+
21+
---
22+
23+
## 2. Code is Read More Than Written
24+
25+
**"Write code that the future reader (including yourself 3 months later) can understand as quickly as possible."**
26+
27+
- Write code where intent is clear, rather than optimizing for brevity or shortness
28+
- Consistency in naming conventions directly impacts searchability and refactoring ease
29+
- Don't use comments to explain logic. Only document intent, side effects, and edge cases
30+
31+
> The code should convey not "what it does" but "why it does it this way."
32+
33+
---
34+
35+
## 3. Validate at Boundaries
36+
37+
**"Trust the internals, but validate strictly at the points of contact with the outside."**
38+
39+
```
40+
External Input ──[Zod]──▶ Server Action ──[Types]──▶ Business Logic ──[ORM]──▶ DB
41+
↑ Guard here ↑ Protected by types here
42+
```
43+
44+
- User input, API requests, webhooks, environment variables — all are "external"
45+
- Once data passes validation, trust the types internally
46+
- Don't over-apply defensive programming inside internal code (types provide the guarantee)
47+
48+
---
49+
50+
## 4. Design for Failure
51+
52+
**"Don't write code that assumes things work. Design with the assumption that things break."**
53+
54+
- All external communication can fail (timeouts, rate limits, service outages)
55+
- Use types to prevent forgetting error handling (discriminated union `ActionResult<T>`)
56+
- Design so that non-critical feature failures don't take down critical features. To distinguish: **critical features** are those whose failure prevents the user from completing the core task they came to do (e.g., authentication, checkout, data saving). **Non-critical features** are enhancements that improve the experience but whose absence doesn't block the core workflow (e.g., analytics, notifications, recommendations, avatar display). When in doubt, ask: "Can the user still accomplish their primary goal without this feature?"
57+
- Distinguish between retryable and non-retryable errors
58+
59+
> Testing only the happy path is like selling umbrellas only on sunny days.
60+
61+
---
62+
63+
## 5. Unidirectional Data Flow
64+
65+
**"If the data flow is consistent, most bugs disappear."**
66+
67+
```
68+
Server Actions (commands)
69+
70+
Hooks (state management / side-effect control)
71+
72+
Components (rendering / event firing)
73+
74+
Callbacks (events bubble up)
75+
76+
Server Actions (next command)
77+
```
78+
79+
- Data flows down (Props), events flow up (Callbacks)
80+
- Do not use global state management libraries (Redux, Zustand, etc.)
81+
- Server Actions are the single source of truth. Hooks are thin coordination layers
82+
83+
> Two-way data binding is convenient in small apps but creates chaos in large ones.
84+
85+
---
86+
87+
## 6. Rule of Three (Timing of Abstraction)
88+
89+
**"Premature abstraction leads to wrong abstraction."**
90+
91+
| Occurrences | Action |
92+
|-------------|--------|
93+
| 1 place | Write it inline — you have no evidence of a pattern yet |
94+
| 2 places | Extract to a helper function if the logic is identical — but remain skeptical; two instances may be coincidentally similar |
95+
| 3+ places | Extract to a factory function if the pattern is identical — three occurrences give enough evidence to identify the true common pattern and design a stable interface |
96+
| 5+ places | Consider creating a base component if the components are similar — at this scale the maintenance cost of duplication exceeds the comprehension cost of abstraction |
97+
98+
- An abstraction used in only one place only adds comprehension cost
99+
- If the configuration object keeps growing, it's a sign the abstraction is heading in the wrong direction
100+
101+
---
102+
103+
## 7. Make the Implicit Explicit
104+
105+
**"Implicit agreements become bugs the moment the team changes."**
106+
107+
- Make side effects clear through naming (`fetchUser()` = has side effects, `calculatePrice()` = pure)
108+
- Always include a reason with `eslint-disable`
109+
- Make technical debt visible with `FIXME` comments — don't leave it untracked
110+
- Convey intent through naming convention suffixes and prefixes (`*-actions.ts`, `use*`, `create*Schema`)

0 commit comments

Comments
 (0)