1056 lines
40 KiB
Markdown
1056 lines
40 KiB
Markdown
---
|
|
id: The Holy Code Bible
|
|
aliases: []
|
|
tags: []
|
|
---
|
|
|
|
_"Bestowed upon the Devs by the Eternal Syntax,
|
|
to bring order to the chaos of code."_
|
|
|
|
---
|
|
|
|
## 1. The Law of Variables and Functions
|
|
|
|
> "Name with purpose, type with faith, validate without doubt."
|
|
|
|
| Realm | Naming Form | Divine Guidance |
|
|
| -------------- | ------------------------------------ | -------------------------------------------------------------------------------------- |
|
|
| **Go** | `camelCase` | Exported → PascalCase (`UserService`), unexported → camelCase (`userService`) |
|
|
| **TypeScript** | `camelCase` | Strict types everywhere. No `any`. Prefer `interface` over `type` for objects. |
|
|
| **Constants** | `PascalCase` (Go), `UPPER_CASE` (TS) | Go: constants are PascalCase if exported. TS: `UPPER_CASE` for module-level constants. |
|
|
|
|
### Go Edicts
|
|
|
|
- Exported identifiers are PascalCase. Unexported are camelCase.
|
|
- Acronyms are all-caps: `HTTPHandler`, `UserID`, `parseJSON`.
|
|
- No `snake_case` in Go identifiers. Ever.
|
|
- Use short variable names for short scopes (`i`, `n`, `err`).
|
|
- Prefer `var` for zero-value declarations, `:=` for initialization.
|
|
- Always handle errors. No silent discards.
|
|
|
|
### TypeScript Edicts
|
|
|
|
- Prefer `const` over `let`. No `var`.
|
|
- Never use `any`. Use `unknown` if type is truly unknown.
|
|
- Use `strict: true` in tsconfig. No exceptions.
|
|
- Never use `==`, always `===`.
|
|
- Use `interface` for object shapes, `type` for unions/primitives.
|
|
- Use `as const` for literal types and enums.
|
|
- Validate input at function boundaries with Zod or similar.
|
|
|
|
---
|
|
|
|
## 2. The Law of Structure (HTML, CSS, Tailwind)
|
|
|
|
> "Let your style be clean and your markup semantic."
|
|
|
|
| Type | Style | Notes |
|
|
| ------------ | --------------- | ---------------------------------------- |
|
|
| HTML classes | kebab-case | Matches query selectors |
|
|
| IDs | kebab-case | Reserved for unique elements |
|
|
| File names | kebab-case | Components, pages, layouts |
|
|
| Tailwind | utility classes | No custom CSS if Tailwind utility exists |
|
|
|
|
### Sacred Guidelines
|
|
|
|
- Class names must be semantic and reusable.
|
|
- IDs reserved for unique elements and anchor targets.
|
|
- No inline styles within templates — Tailwind utilities only.
|
|
- Custom CSS lives in `assets/css/` with kebab-case filenames.
|
|
- Tailwind theme is centralized in `tailwind.config.ts`. No arbitrary values (`[color]`) in templates unless absolutely necessary.
|
|
- Prefer Tailwind's `@apply` in component classes only for repeated patterns.
|
|
- Responsive design first: mobile-first with Tailwind breakpoints.
|
|
|
|
---
|
|
|
|
## 3. The Law of Files and Folders
|
|
|
|
> "Order in structure is order in mind; chaos begins at the root."
|
|
|
|
### Go Backend Layout
|
|
|
|
```
|
|
service-name/
|
|
├── cmd/
|
|
│ └── server/
|
|
│ └── main.go # Entrypoint only
|
|
├── internal/
|
|
│ ├── handler/ # HTTP handlers (1 file per resource)
|
|
│ ├── service/ # Business logic
|
|
│ ├── repository/ # Database access
|
|
│ ├── middleware/ # Auth, logging, CORS, rate limiting
|
|
│ ├── dto/ # Request/response types
|
|
│ ├── model/ # Domain models
|
|
│ └── config/ # Config struct + loader
|
|
├── migrations/ # SQL migration files
|
|
├── pkg/ # Shared library (if re-usable outside)
|
|
├── Dockerfile
|
|
├── docker-compose.yml
|
|
├── .env.example
|
|
├── Makefile
|
|
└── README.md
|
|
```
|
|
|
|
### Nuxt/Vue Frontend Layout
|
|
|
|
```
|
|
frontend/
|
|
├── app.vue # Root component
|
|
├── nuxt.config.ts
|
|
├── tailwind.config.ts
|
|
├── pages/ # File-based routing
|
|
│ ├── index.vue
|
|
│ ├── video/
|
|
│ │ ├── index.vue
|
|
│ │ └── [id].vue
|
|
│ └── blog/
|
|
├── components/
|
|
│ ├── shared/ # Reusable: Button, Input, Modal
|
|
│ └── <domain>/ # Page-specific: video/Player.vue
|
|
├── composables/ # useApi, useAuth, usePagination
|
|
├── stores/ # Pinia stores: useAuthStore
|
|
├── types/ # TypeScript interfaces
|
|
├── assets/
|
|
│ ├── css/ # Custom CSS files
|
|
│ └── images/
|
|
├── public/
|
|
├── server/ # API routes or middleware (Nuxt server)
|
|
├── .env.example
|
|
└── README.md
|
|
```
|
|
|
|
### Library Package Layout (e.g., video player, blog editor)
|
|
|
|
```
|
|
package-name/
|
|
├── src/
|
|
│ ├── index.ts # Public API
|
|
│ ├── components/
|
|
│ ├── types/
|
|
│ └── utils/
|
|
├── dist/ # Built output
|
|
├── package.json
|
|
├── tsconfig.json
|
|
├── vite.config.ts # or tsup.config.ts
|
|
├── README.md
|
|
└── LICENSE
|
|
```
|
|
|
|
---
|
|
|
|
## 4. The Immutable Rules of Naming
|
|
|
|
> "A name unspoken in truth shall summon bugs."
|
|
|
|
| Context | Style | Example |
|
|
| ----------------------------------- | -------------------- | -------------------------------------------- |
|
|
| Go variables/functions (unexported) | camelCase | `getUser`, `parseToken` |
|
|
| Go functions/types (exported) | PascalCase | `GetUser`, `UserService` |
|
|
| Go acronyms | ALL CAPS | `HTTPHandler`, `UserID`, `parseJSON` |
|
|
| Go files | snake_case | `user_handler.go`, `auth_middleware.go` |
|
|
| Go tests | snake_case | `user_handler_test.go` |
|
|
| TypeScript variables/functions | camelCase | `getUser`, `parseToken` |
|
|
| TypeScript types/interfaces | PascalCase | `UserResponse`, `AuthPayload` |
|
|
| Component files | PascalCase | `VideoPlayer.vue`, `BlogEditor.vue` |
|
|
| Component names | PascalCase | `<VideoPlayer />`, `<BlogEditor />` |
|
|
| CSS classes / IDs | kebab-case | `video-container`, `main-header` |
|
|
| CSS/Tailwind files | kebab-case | `custom-styles.css` |
|
|
| Constants (Go exported) | PascalCase | `MaxRetryCount` |
|
|
| Constants (Go unexported) | camelCase | `maxRetryCount` |
|
|
| Constants (TS) | UPPER_CASE | `MAX_RETRY_COUNT` |
|
|
| Enums (TS) | PascalCase members | `Status.Active`, `Role.Admin` |
|
|
| DB tables/columns | snake_case | `users`, `video_id`, `created_at` |
|
|
| JSON fields | camelCase | `"userId"`, `"videoUrl"` |
|
|
| Environment variables | UPPER_SNAKE_CASE | `DATABASE_URL`, `REDIS_HOST` |
|
|
| Docker image tags | lowercase | `auth-service:1.0.0` |
|
|
| Git branches | kebab-case | `feat/video-transcoding`, `fix/auth-timeout` |
|
|
| Git commits | Conventional Commits | `feat: add video upload endpoint` |
|
|
| Repository names | kebab-case | `video-service`, `nuxt-commons` |
|
|
|
|
One naming style per context, no deviation. Filenames must mirror their contents exactly. No spaces or special characters.
|
|
|
|
---
|
|
|
|
## 5. The Scroll of Static Typing
|
|
|
|
> "Types are truth — doubt them and runtime will punish thee."
|
|
|
|
### Go
|
|
|
|
```go
|
|
package user
|
|
|
|
import "time"
|
|
|
|
type User struct {
|
|
ID string `json:"id"`
|
|
Email string `json:"email"`
|
|
Name string `json:"name"`
|
|
CreatedAt time.Time `json:"createdAt"`
|
|
}
|
|
|
|
// GetUser returns a user by ID.
|
|
// Returns ErrNotFound if the user does not exist.
|
|
func GetUser(id string) (*User, error) {
|
|
// ...
|
|
}
|
|
```
|
|
|
|
- Every function returns `error` when it can fail.
|
|
- Use `error` interface, not custom exception types.
|
|
- Wrap errors: `fmt.Errorf("get user %s: %w", id, err)`.
|
|
- Use `errors.Is()` and `errors.As()` for error inspection.
|
|
- Define sentinel errors: `var ErrNotFound = errors.New("user not found")`.
|
|
|
|
### TypeScript
|
|
|
|
```typescript
|
|
interface User {
|
|
id: string;
|
|
email: string;
|
|
name: string;
|
|
createdAt: string; // ISO 8601
|
|
}
|
|
|
|
// Use Zod for runtime validation
|
|
import { z } from "zod";
|
|
|
|
const CreateUserSchema = z.object({
|
|
email: z.string().email(),
|
|
name: z.string().min(1).max(100),
|
|
});
|
|
|
|
type CreateUserInput = z.infer<typeof CreateUserSchema>;
|
|
|
|
async function createUser(input: CreateUserInput): Promise<User> {
|
|
const validated = CreateUserSchema.parse(input);
|
|
// validated is fully typed
|
|
}
|
|
```
|
|
|
|
- `strict: true` in tsconfig at all times.
|
|
- No `any`. Use `unknown` + type narrowing.
|
|
- Prefer `interface` over `type` for object types.
|
|
- Use `type` for unions, intersections, and primitives.
|
|
- All API responses have a corresponding TypeScript type in `~/types/`.
|
|
- Use Zod for runtime validation of API inputs and env vars.
|
|
|
|
---
|
|
|
|
## 6. The Ten Commandments of Clean Code
|
|
|
|
> "Clean code is the only code that survives the test of time."
|
|
|
|
1. **One responsibility** per function, per type, per file.
|
|
2. **Comment only to explain _why_**, never _what_.
|
|
3. **No silent errors** — handle or propagate every error.
|
|
4. **Always validate and sanitize** all input at the boundary.
|
|
5. **No global state** — use dependency injection.
|
|
6. **Never push secrets, configs, or build artifacts.**
|
|
7. **Consistency outranks cleverness** — write boring code.
|
|
8. **Fail fast, log properly, recover gracefully.**
|
|
9. **No unused code, no dead branches, no commented-out blocks.**
|
|
10. **Every public API must have a meaningful comment** explaining what it does.
|
|
|
|
---
|
|
|
|
## 7. The Standard of the File Header
|
|
|
|
> "Every file must declare its origin before it speaks."
|
|
|
|
### Go
|
|
|
|
```go
|
|
// Package handler provides HTTP handlers for the auth service.
|
|
package handler
|
|
|
|
import (
|
|
"net/http"
|
|
)
|
|
```
|
|
|
|
Go files need only the package comment at the top. No author/date boilerplate — Git already has that.
|
|
|
|
### TypeScript/Vue
|
|
|
|
```typescript
|
|
/**
|
|
* File: VideoPlayer.vue
|
|
* Purpose: Hand-written video player with HLS.js and WebRTC/WHEP support.
|
|
* See: https://git.db123.ir/db/VP
|
|
*/
|
|
```
|
|
|
|
TypeScript/Vue files benefit from a brief purpose comment if the purpose isn't obvious from the name.
|
|
|
|
---
|
|
|
|
## 8. The Law of Comments
|
|
|
|
> "Explain not the obvious, but the reason behind the mystery."
|
|
|
|
```go
|
|
// GOOD: explains why
|
|
// Refresh the token 5 minutes before expiry to avoid race conditions.
|
|
if time.Until(token.ExpiresAt) < 5*time.Minute {
|
|
token, err = refreshToken(token)
|
|
}
|
|
|
|
// BAD: restates the obvious
|
|
// Check if token is about to expire
|
|
if time.Until(token.ExpiresAt) < 5*time.Minute {
|
|
```
|
|
|
|
Same rule applies to TypeScript, Vue, CSS, and all languages:
|
|
|
|
- Explain _intent_ and _context_, not syntax.
|
|
- No comments for self-documenting code.
|
|
- TODO comments must include a ticket/issue reference: `// TODO(db123): handle pagination limits (PROJ-42)`.
|
|
|
|
---
|
|
|
|
## 9. The Law of Strictness (Go)
|
|
|
|
> "Without strictness, your logic shall wander."
|
|
|
|
```go
|
|
package main
|
|
|
|
import (
|
|
"log/slog"
|
|
)
|
|
|
|
func main() {
|
|
slog.SetLogLoggerLevel(slog.LevelDebug)
|
|
// ...
|
|
}
|
|
```
|
|
|
|
- Run `go vet ./...` before every commit.
|
|
- Use `golangci-lint` in CI with strict configuration.
|
|
- `CGO_ENABLED=0` for production builds.
|
|
- No `panic` outside of `main` and initialization.
|
|
- Use `errgroup` or `sync.WaitGroup` for goroutine management. Never use `time.Sleep` for synchronization.
|
|
- Always set `-race` in tests and CI.
|
|
|
|
---
|
|
|
|
## 10. The Law of Strictness (TypeScript)
|
|
|
|
> "An untyped variable is a vessel for chaos."
|
|
|
|
```jsonc
|
|
// tsconfig.json
|
|
{
|
|
"compilerOptions": {
|
|
"strict": true,
|
|
"noUncheckedIndexedAccess": true,
|
|
"noImplicitReturns": true,
|
|
"noFallthroughCasesInSwitch": true,
|
|
"exactOptionalPropertyTypes": true,
|
|
"forceConsistentCasingInFileNames": true,
|
|
},
|
|
}
|
|
```
|
|
|
|
- ESLint with `@typescript-eslint/strict` config.
|
|
- Prettier for formatting — run `prettier --check` in CI.
|
|
- TypeScript 5.x+ with `--strict` mode. No `// @ts-ignore` or `// @ts-expect-error` without a comment explaining why.
|
|
- Use `satisfies` operator for type-safe expressions without widening.
|
|
|
|
---
|
|
|
|
## 11. The Law of Security
|
|
|
|
> "Trust no input, for deceit lives in every form field."
|
|
|
|
### General
|
|
|
|
- Never trust user input. Validate and sanitize at every boundary.
|
|
- Always use prepared statements for SQL. Never concatenate query strings.
|
|
- Escape all HTML output. Use template engines that auto-escape.
|
|
- Validate on both client and server.
|
|
- Rate limit all public endpoints.
|
|
- Disable error display in production. Log instead.
|
|
- Never expose `.env`, secrets, or stack traces to the client.
|
|
- Use TLS everywhere. Auto-renew via Let's Encrypt.
|
|
|
|
### Go Specific
|
|
|
|
- Use `httputil` for reverse proxy security.
|
|
- Set `X-Content-Type-Options: nosniff`, `X-Frame-Options: DENY`, `Content-Security-Policy` headers.
|
|
- Use `bcrypt` or `argon2` for password hashing. Never SHA/MD5.
|
|
- Validate UUIDs with `uuid.Parse()` before DB queries.
|
|
|
|
### TypeScript Specific
|
|
|
|
- Sanitize user input before rendering with `DOMPurify`.
|
|
- Never use `innerHTML` with user content. Use `textContent` or Vue's template binding.
|
|
- For Nuxt: validate API responses with Zod on the client side.
|
|
|
|
---
|
|
|
|
## 12. The Law of Git
|
|
|
|
> "Commits are confessions — be honest, be precise."
|
|
|
|
- Commit atomic changes only.
|
|
- Branch names → kebab-case.
|
|
- One purpose per branch (feature, fix, etc).
|
|
- Commit format:
|
|
|
|
```
|
|
<type>(<scope>): <description>
|
|
|
|
[optional body]
|
|
|
|
[optional footer]
|
|
```
|
|
|
|
**Types:**
|
|
|
|
- `feat:` — New feature
|
|
- `fix:` — Bug fix
|
|
- `docs:` — Documentation only
|
|
- `style:` — Formatting, no logic change
|
|
- `refactor:` — Restructure without behavior change
|
|
- `perf:` — Performance improvement
|
|
- `test:` — Add or fix tests
|
|
- `build:` — Build system or dependency changes
|
|
- `ci:` — CI config or script changes
|
|
- `chore:` — Other changes not in src/test
|
|
- `revert:` — Revert a previous commit
|
|
|
|
**Scope:** Service or package name (e.g., `feat(auth):`, `fix(video-player):`).
|
|
|
|
**Footer (optional):**
|
|
|
|
- `fixes #123`
|
|
- `breaking change: ...`
|
|
|
|
---
|
|
|
|
## 13. The Law of Configuration
|
|
|
|
> "Keep your secrets sacred and your configs pure."
|
|
|
|
- All configuration via environment variables. No config files in the repository (except `.env.example`).
|
|
- Every service has a `.env.example` with all expected vars and sensible defaults.
|
|
- Config is loaded at startup and immutable after boot.
|
|
- Never commit `.env`, secrets, or local overrides.
|
|
|
|
### Go Config Pattern
|
|
|
|
```go
|
|
type Config struct {
|
|
Port string `env:"PORT" envDefault:":8080"`
|
|
DatabaseURL string `env:"DATABASE_URL,required"`
|
|
RedisURL string `env:"REDIS_URL"`
|
|
JWTSecret string `env:"JWT_SECRET,required"`
|
|
}
|
|
```
|
|
|
|
### Nuxt Config Pattern
|
|
|
|
```typescript
|
|
// nuxt.config.ts
|
|
export default defineNuxtConfig({
|
|
runtimeConfig: {
|
|
public: {
|
|
apiBaseUrl: process.env.API_BASE_URL || "http://localhost:8080",
|
|
},
|
|
},
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
## 14. The Law of Performance
|
|
|
|
> "Do not make the machine work in vain."
|
|
|
|
- No unnecessary allocations in hot paths. Pre-allocate slices: `make([]T, 0, n)`.
|
|
- Cache expensive queries with Redis. Set TTL appropriate to the data.
|
|
- Use connection pooling (DB, Redis, HTTP clients).
|
|
- Lazy load Nuxt pages and components.
|
|
- Minify assets for production. Nuxt does this by default.
|
|
- Use streaming for large responses (video, file downloads).
|
|
- Avoid N+1 queries. Use eager loading or batch queries.
|
|
- Profile before optimizing. Don't guess.
|
|
|
|
---
|
|
|
|
## 15. The Law of Naming (Final Recap)
|
|
|
|
> "Every form of name has its place — and none shall trespass."
|
|
|
|
```
|
|
camelCase -> Go vars (unexported), Go file names, TS vars/functions, JSON fields
|
|
PascalCase -> Go exported types/funcs, TS types/interfaces, Vue components
|
|
snake_case -> DB tables, DB columns, migration files
|
|
kebab-case -> HTML classes, CSS files, Git branches, repo names
|
|
UPPER_CASE -> TS constants, env vars (UPPER_SNAKE_CASE)
|
|
ALL CAPS -> Go acronyms in identifiers
|
|
```
|
|
|
|
---
|
|
|
|
## 16. The Law of Environment
|
|
|
|
> "The env is the covenant — never share it, never expose it."
|
|
|
|
- `.env.example` checked into every repository.
|
|
- `.env` in `.gitignore` for every repository.
|
|
- All secrets (DB passwords, API keys, JWT secrets) are environment variables.
|
|
- Docker Compose files reference `${VARIABLE}` with `.env` file.
|
|
- Production secrets injected via Docker secrets or the orchestrator, never in the image.
|
|
|
|
---
|
|
|
|
## 17. The Law of Testing
|
|
|
|
> "Faith without tests is blind."
|
|
|
|
### Go
|
|
|
|
- Unit tests alongside code: `handler_test.go`, `service_test.go`.
|
|
- Use `testing` stdlib + `testify/assert` or `testify/require`.
|
|
- Integration tests in `tests/` — use Testcontainers for PostgreSQL/Redis.
|
|
- Run `go test -race ./...` before every push.
|
|
- Aim for 80%+ coverage on business logic. Critical paths (auth, payments) 90%+.
|
|
|
|
### TypeScript/Vue/Nuxt
|
|
|
|
- Vitest for unit tests. Test composables, stores, and utility functions.
|
|
- Playwright or Vitest browser mode for E2E tests.
|
|
- Component tests with `@vue/test-utils` + Vitest.
|
|
- Run `vitest run` before every push.
|
|
- Test API calls in composables by mocking `$fetch`.
|
|
|
|
### General
|
|
|
|
- All logic must be verifiable by tests.
|
|
- Unit tests for functions, integration tests for systems.
|
|
- Run tests before every merge and deployment.
|
|
- Never rely on manual testing alone.
|
|
|
|
---
|
|
|
|
## 18. The Law of Documentation
|
|
|
|
> "Unwritten knowledge is forgotten truth."
|
|
|
|
- Every repository requires a `README.md` with: purpose, local setup steps, env vars, API endpoints.
|
|
- Every major module or package has a short summary comment.
|
|
- Architecture diagrams in `/docs/architecture.md`.
|
|
- Deployment steps in `/docs/deployment.md`.
|
|
- Update docs immediately after code changes.
|
|
- Use Markdown and simple language.
|
|
|
|
---
|
|
|
|
## 19. The Law of Dependencies
|
|
|
|
> "Rely not on that which you do not control."
|
|
|
|
- Only install what is truly needed.
|
|
- Lock dependency versions.
|
|
- Remove unused or outdated packages.
|
|
- Never commit `vendor/`, `node_modules/`, or build artifacts.
|
|
- Keep dependency files clean and ordered.
|
|
- Pin exact versions in Dockerfiles for base images.
|
|
- Regularly audit for vulnerabilities (`govulncheck`, `npm audit`).
|
|
|
|
---
|
|
|
|
## 20. The Law of Error Handling
|
|
|
|
> "An error unhandled is chaos unleashed."
|
|
|
|
### Go
|
|
|
|
```go
|
|
if err != nil {
|
|
return fmt.Errorf("create user %s: %w", email, err)
|
|
}
|
|
```
|
|
|
|
- Always check errors. No `_ =` error discards.
|
|
- Wrap errors with context.
|
|
- Define typed sentinel errors for expected failures.
|
|
- Use `errors.Is()` and `errors.As()` for inspection.
|
|
- Log errors at the boundary (handler), not deep in business logic.
|
|
|
|
### TypeScript
|
|
|
|
```typescript
|
|
try {
|
|
const user = await createUser(input);
|
|
} catch (error) {
|
|
if (error instanceof ValidationError) {
|
|
// handle validation error
|
|
}
|
|
throw error;
|
|
}
|
|
```
|
|
|
|
- Use typed error classes or Zod validation errors.
|
|
- Never catch and swallow without logging or re-throwing.
|
|
- In Nuxt: use `useError` or error pages for user-facing errors.
|
|
|
|
### General
|
|
|
|
- Define global error and exception handlers in every service.
|
|
- Never expose raw errors to users — return structured error responses.
|
|
- Always fail gracefully with clear internal logs.
|
|
|
|
---
|
|
|
|
## 21. The Law of Review
|
|
|
|
> "Only through scrutiny does code ascend."
|
|
|
|
- All significant merges must be reviewed.
|
|
- No code enters `main` without a second pair of eyes.
|
|
- Review for: correctness, readability, security, consistency, test coverage.
|
|
- A review is protection, not criticism.
|
|
- CI must pass before merge (lint, type check, test, build).
|
|
|
|
---
|
|
|
|
## 22. The Law of Architecture
|
|
|
|
> "Without structure, even perfect code collapses."
|
|
|
|
- Use layered architecture: Handler → Service → Repository. No skipping layers.
|
|
- Each package must be self-contained and dependency-injected.
|
|
- Shared code lives in `/internal/` or `/pkg/` — never copied.
|
|
- Define clear boundaries between layers (API, Business Logic, Data).
|
|
- Avoid circular dependencies — they are heresy.
|
|
- Services communicate only through defined APIs (REST or events).
|
|
|
|
---
|
|
|
|
## 23. The Law of Modularity (Services)
|
|
|
|
> "Divide to conquer, integrate to rule."
|
|
|
|
- Each microservice owns its data. No direct database access across services.
|
|
- Services communicate via REST APIs (synchronous) or RabbitMQ events (asynchronous).
|
|
- Every service has its own PostgreSQL database and Redis instance.
|
|
- API versioning via URL prefix: `/v1/`, `/v2/`.
|
|
- Event contracts are documented and versioned. Breaking changes = new event type.
|
|
- No cross-service shortcuts or "temporary hacks."
|
|
|
|
---
|
|
|
|
## 24. The Law of API Design
|
|
|
|
> "An API is a contract — break it and integrations shall crumble."
|
|
|
|
| Rule | Standard |
|
|
| ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
| Base URL | `https://api.db123.ir/v1/{resource}` |
|
|
| Methods | GET (list/read), POST (create), PUT (replace), PATCH (partial), DELETE (remove) |
|
|
| Success response | `{"data": ..., "meta": {...}}` |
|
|
| Error response | `{"error": {"code": "ERR_CODE", "message": "...", "details": {...}}}` |
|
|
| Pagination | `?page=1&per_page=20` → `{"page": 1, "per_page": 20, "total": 100, "total_pages": 5}` |
|
|
| IDs | UUID v7 for all resource IDs |
|
|
| Timestamps | RFC 3339 (ISO 8601) in UTC |
|
|
| Sorting | `?sort=field:asc,field2:desc` |
|
|
| Filtering | `?filter[field]=value` |
|
|
| Status codes | 200 OK, 201 Created, 204 No Content, 400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found, 409 Conflict, 422 Unprocessable, 429 Too Many, 500 Internal |
|
|
|
|
### Error Codes (Consistent Across All Services)
|
|
|
|
```
|
|
ERR_VALIDATION — Input validation failed
|
|
ERR_NOT_FOUND — Resource not found
|
|
ERR_UNAUTHORIZED — Missing or invalid authentication
|
|
ERR_FORBIDDEN — Authenticated but insufficient permissions
|
|
ERR_CONFLICT — Duplicate resource or state conflict
|
|
ERR_INTERNAL — Unexpected server error
|
|
ERR_RATE_LIMITED — Too many requests
|
|
```
|
|
|
|
Every error payload includes a `details` field with specific field-level errors when applicable.
|
|
|
|
---
|
|
|
|
## 25. The Law of Database
|
|
|
|
> "Guard thy data as thou guardest thy secrets."
|
|
|
|
- Each service owns its own PostgreSQL database. No cross-service DB access.
|
|
- Migration files in `migrations/`, SQL only. Tool: `golang-migrate/migrate`.
|
|
- File naming: `YYYYMMDDHHMMSS_description.up.sql` / `.down.sql`.
|
|
- Connection pool configured via env vars: max open, max idle, max lifetime.
|
|
- Table names: plural snake_case (`users`, `video_metadata`).
|
|
- Column names: snake_case (`created_at`, `updated_at`, `video_id`).
|
|
- Primary keys: UUID v7. Never auto-increment integers for public IDs.
|
|
- Every table has `created_at` and `updated_at` columns (`TIMESTAMPTZ`).
|
|
- Soft deletes: `deleted_at TIMESTAMPTZ NULL` for recoverable data.
|
|
- Indexes on foreign keys and frequently queried columns.
|
|
- Use `EXPLAIN ANALYZE` before shipping new queries.
|
|
|
|
---
|
|
|
|
## 26. The Law of Docker
|
|
|
|
> "Build once, run anywhere — but build it right."
|
|
|
|
### Dockerfile Standards
|
|
|
|
- Multi-stage builds: builder stage + distroless/alpine runtime stage.
|
|
- `CGO_ENABLED=0` for Go builds.
|
|
- Distroless base image for production (`gcr.io/distroless/static-debian12` or `alpine:3.23`).
|
|
- Non-root user in runtime stage.
|
|
- No unnecessary packages in final image.
|
|
- Label images: `org.opencontainers.image.source`, `org.opencontainers.image.version`.
|
|
- Pin base image version digests in CI.
|
|
|
|
### Docker Compose Standards
|
|
|
|
- One `docker-compose.yml` per service for local development.
|
|
- Root `docker-compose.yml` at the monorepo root (if any) or a shared `infra/` repo for orchestrating all services locally.
|
|
- Service names match the repository name.
|
|
- Volume mounts for live reload in development.
|
|
- Default networks: `internal` (service-to-service), `external` (API gateway).
|
|
|
|
---
|
|
|
|
## 27. The Law of Observability
|
|
|
|
> "Thou shalt not fly blind."
|
|
|
|
### Logging
|
|
|
|
- Structured JSON logs via `log/slog` (Go) or `pino`/`consola` (Nuxt).
|
|
- Every log line includes: `level`, `msg`, `service`, `request_id`, `duration`, `error`.
|
|
- Log levels: `debug` (development), `info` (normal ops), `warn` (expected issues), `error` (unexpected failures).
|
|
- No sensitive data in logs (passwords, tokens, PII).
|
|
|
|
### Metrics
|
|
|
|
- Every service exposes Prometheus metrics at `GET /metrics`.
|
|
- Standard metrics: request count, request duration (histogram), error count, active connections.
|
|
- Business metrics: registered users, videos uploaded, orders placed (per service).
|
|
|
|
### Tracing
|
|
|
|
- OpenTelemetry with trace ID propagated via `x-trace-id` header.
|
|
- Trace ID generated at the API gateway if not present.
|
|
- Traces across service boundaries via HTTP headers or RabbitMQ message headers.
|
|
|
|
### Health
|
|
|
|
- `GET /health` — Returns 200 with `{"status": "ok"}`. Indicates the process is alive.
|
|
- `GET /ready` — Returns 200 when DB, Redis, and critical dependencies are reachable. 503 otherwise.
|
|
|
|
---
|
|
|
|
## 28. The Law of Resilience
|
|
|
|
> "Expect failure. Design for it."
|
|
|
|
- Every outbound HTTP call has a timeout. No infinite waits.
|
|
- Use circuit breakers for calls to other services. Return cached or degraded response on failure.
|
|
- Retry transient failures with exponential backoff + jitter. Max 3 retries.
|
|
- Idempotency keys for mutation endpoints (POST/PATCH) — reject duplicate requests.
|
|
- Graceful shutdown: Listen for SIGTERM/SIGINT, drain active connections, finish in-flight requests, then exit.
|
|
- Rate limit all public endpoints. Return 429 with `Retry-After` header.
|
|
- Queue-based load leveling: incoming work goes to RabbitMQ, workers consume at their own pace.
|
|
|
|
---
|
|
|
|
## 29. The Law of Events (RabbitMQ)
|
|
|
|
> "An event is a promise — deliver it or die trying."
|
|
|
|
- Events for cross-service communication (profile updates, video processed, order placed).
|
|
- One exchange per event type, one queue per consumer per service.
|
|
- Event payload includes: `event_type`, `event_id` (UUID v7), `timestamp`, `data`, `version`.
|
|
- Events are durable (persistent delivery). Queues are durable.
|
|
- Configure **dead letter exchanges** for every queue. Messages that fail after 3 retries land in the DLQ.
|
|
- Monitor DLQ size. Periodically inspect and manually replay corrected messages.
|
|
- Consumers are idempotent — processing the same event twice produces the same result.
|
|
- For job priority, use separate queues per priority level. Workers drain high-priority queues first.
|
|
- Document every event contract: `docs/events/` with schema examples.
|
|
|
|
---
|
|
|
|
## 30. The Law of Caching (Redis)
|
|
|
|
> "Cache with purpose, expire with discipline."
|
|
|
|
- Redis is a cache, not a primary data store. Data loss must be acceptable.
|
|
- Every cache key has a TTL. No infinite caches.
|
|
- Key naming: `service:entity:id` (e.g., `auth:user:550e8400`).
|
|
- Use **cache-aside** pattern: check Redis → miss → query DB → store in Redis with TTL → return.
|
|
- **Cache invalidation strategies** (choose by data type):
|
|
- **TTL**: Let data expire naturally. Good for slightly-staleable data.
|
|
- **Explicit invalidation**: Delete/update cache keys when source data changes. Requires discipline.
|
|
- **Tag-based**: Assign tags to related cache entries and delete by tag on updates. Complex but powerful.
|
|
- Use Redis for: session data, rate limiting counters, job queues (via RabbitMQ if async needed), hot data with low TTL.
|
|
- Do NOT store large objects in Redis. Use S3 for blobs, Redis for metadata.
|
|
- Connection pooling with timeout. Handle Redis failures gracefully — fall back to DB.
|
|
|
|
---
|
|
|
|
## 31. The Law of Continuous Integration
|
|
|
|
> "Let machines judge first, lest chaos reach production."
|
|
|
|
### CI Pipeline (`.gitea/workflows/build.yml`)
|
|
|
|
1. Lint (`golangci-lint` / `eslint`)
|
|
2. Type check (`go vet` / `tsc --noEmit`)
|
|
3. Unit tests + integration tests (with `-race` flag for Go)
|
|
4. Vulnerability scanning: `govulncheck` (Go), `npm audit` (TS), **Trivy** (Docker image CVEs)
|
|
5. Build Docker image (multi-stage, distroless, non-root)
|
|
6. Push to registry (tagged `:latest`, `:{{sha}}`, and semver tag)
|
|
7. Deploy to dev environment
|
|
8. Health check after deploy
|
|
|
|
### Requirements
|
|
|
|
- CI runs on every push to any branch.
|
|
- No merge allowed on failed pipelines.
|
|
- Staging mirrors production environment.
|
|
- Production deploys only from `main` via CI/CD.
|
|
- Tag releases semantically: `v1.2.0`, `v1.2.1`.
|
|
|
|
---
|
|
|
|
## 32. The Law of Deployment
|
|
|
|
> "Release with reverence, rollback with readiness."
|
|
|
|
- Blue-green deployments via Docker Compose (different project names: `-p auth-blue`, `-p auth-green`).
|
|
- Health check passes before traffic switches.
|
|
- Rollback by switching back to the previous stack.
|
|
- Database migrations run before the new version starts.
|
|
- Migrations must be backward-compatible (add columns/drop columns in separate releases).
|
|
- Never deploy on Friday unless you enjoy weekend debugging.
|
|
|
|
---
|
|
|
|
## 33. The Law of Version Control Discipline
|
|
|
|
> "Branches are parallel worlds — merge only when peace is achieved."
|
|
|
|
- `main` is stable and deployable at all times.
|
|
- Feature branches from `main`. Squash merge with conventional commit message.
|
|
- PR description includes: what changed, why, how to test, screenshots (if UI).
|
|
- No direct pushes to `main`. Only merge requests.
|
|
- Keep a `CHANGELOG.md` per service for every release.
|
|
|
|
---
|
|
|
|
## 34. The Law of Cross-Cutting Concerns
|
|
|
|
> "Some truths apply everywhere — code them once."
|
|
|
|
| Concern | Standard |
|
|
| ----------------- | ------------------------------------------------------------------------- |
|
|
| Health checks | `GET /health` (alive), `GET /ready` (dependencies) |
|
|
| Metrics | `GET /metrics` (Prometheus format) |
|
|
| Tracing | `x-trace-id` header propagated. OpenTelemetry spans. |
|
|
| Graceful shutdown | SIGTERM → drain → shutdown (max 30s) |
|
|
| Rate limiting | Per-IP, per-route configurable. 429 with `Retry-After`. |
|
|
| CORS | Whitelist `*.db123.ir` origins. No wildcard in production. |
|
|
| Auth | JWT bearer token. Validated in middleware. Forward user info via context. |
|
|
|
|
---
|
|
|
|
## 35. The Law of the API Gateway
|
|
|
|
> "The gateway is the gatekeeper — let nothing pass untested."
|
|
|
|
### Tool
|
|
|
|
- **Start with Nginx**. Migrate to **Traefik** when you need dynamic service discovery (Traefik reads container labels and auto-configures routes).
|
|
|
|
### Standards
|
|
|
|
- Route by path prefix: `api.db123.ir/v1/auth/*` → auth service, `api.db123.ir/v1/video/*` → video service.
|
|
- SSL termination with Let's Encrypt auto-renewal (Certbot or Traefik automatic).
|
|
- Rate limiting per IP at the gateway level. Return `429` with `Retry-After` header.
|
|
- Inject `X-Request-Id` if missing. Forward to upstream services.
|
|
- Enable **HTTP/2** for better client performance.
|
|
- Set security headers: `X-Content-Type-Options: nosniff`, `X-Frame-Options: DENY`, `Content-Security-Policy`.
|
|
- Strip the version prefix when proxying to services (configurable per service).
|
|
- CORS headers for allowed origins. Whitelist `*.db123.ir`.
|
|
- Serve static assets (CDN) with appropriate cache headers and far-future expiry.
|
|
|
|
---
|
|
|
|
## 36. The Law of the Frontend (Nuxt)
|
|
|
|
> "The frontend is the face of thy platform — make it consistent and swift."
|
|
|
|
- Shared Tailwind theme in `tailwind.config.ts` with brand colors, spacing, typography.
|
|
- No inline arbitrary Tailwind values. All values in the theme config.
|
|
- Centralized API client in `~/composables/useApi.ts` with auth header injection, error normalization, request/response logging.
|
|
- Pinia stores per domain: `useAuthStore`, `useVideoStore`, `useBlogStore`.
|
|
- Every domain page uses its own layout in `~/layouts/`.
|
|
- Shared UI components (Button, Input, Modal, Card) in `~/components/shared/`.
|
|
- Import the video player and blog editor as npm packages from the local Gitea registry.
|
|
- TypeScript types for every API response in `~/types/`.
|
|
- Use `useAsyncData` or `useFetch` with proper keys for cache deduplication.
|
|
- No inline styles. No `style` tag without `scoped` and only when Tailwind can't express it.
|
|
|
|
---
|
|
|
|
## 37. The Law of Tailwind
|
|
|
|
> "Utility classes are the atoms of your design — combine them with discipline."
|
|
|
|
- Customize the default theme via `tailwind.config.ts`. Never override base styles with custom CSS unless absolutely necessary.
|
|
- Use `@apply` in component classes only for patterns that appear in 5+ places.
|
|
- Responsive design: use Tailwind breakpoints (`sm:`, `md:`, `lg:`, `xl:`, `2xl:`). Mobile-first.
|
|
- Dark mode via `class` strategy. Toggle with a JS class on `<html>`.
|
|
- No inline `style` attributes. No `<style>` tags without `scoped`.
|
|
- Colors, fonts, spacing all come from `theme.extend` in config.
|
|
- Use `group` and `peer` variants for state-based styling.
|
|
- Animations: use Tailwind's built-in `animate-` or define custom keyframes in `theme.extend.animation`.
|
|
|
|
---
|
|
|
|
---
|
|
|
|
## 38. The Law of File Storage (Minio / S3)
|
|
|
|
> "Blobs belong in buckets, not in databases."
|
|
|
|
- Use Minio (S3-compatible) for all file storage: user uploads, images, documents, video assets.
|
|
- **Pre-signed URLs**: Instead of routing file uploads through your backend, generate pre-signed URLs. Clients upload directly to Minio, then notify the backend to process the file.
|
|
- Buckets are private by default. Use pre-signed URLs for controlled access.
|
|
- For public assets, put a CDN (e.g. Cloudflare) in front of Minio.
|
|
- Configure **lifecycle policies** to automatically expire old or temporary objects.
|
|
- Enable **versioning and object locking** for protection against accidental deletion.
|
|
- Mirror buckets to remote storage for disaster recovery.
|
|
|
|
---
|
|
|
|
## 39. The Law of Reliable Messaging (Outbox Pattern)
|
|
|
|
> "Publish not without protection, lest thy message be lost to the void."
|
|
|
|
- In the same database transaction as your business data, insert a record into an `outbox` table containing the serialised message.
|
|
- A separate relay process (outbox relay) polls the outbox table and publishes messages to RabbitMQ.
|
|
- After successful publication, mark the outbox record as sent or delete it.
|
|
- This guarantees **at-least-once delivery** — the message survives service crashes.
|
|
- Consumers must be idempotent: include a unique `message_id` and skip already-processed messages.
|
|
|
|
---
|
|
|
|
## 40. The Law of Sagas (Distributed Transactions)
|
|
|
|
> "A transaction spanning services is a journey — prepare for detours."
|
|
|
|
When a business process spans multiple services (e.g. Place Order → Reserve Inventory → Process Payment → Ship):
|
|
|
|
- **Choreography**: Each service listens to events, performs its local transaction, then emits the next event. Simple flows only.
|
|
- **Orchestration**: A central saga coordinator sends commands to each service and handles compensation (rollback) if a step fails. Use for complex flows.
|
|
- Every saga step must be idempotent.
|
|
- Design compensating transactions for every mutating step.
|
|
|
|
---
|
|
|
|
## 41. The Law of Schema Management
|
|
|
|
> "Contracts between services are sacred — version them or break them."
|
|
|
|
- Define event and API schemas in a shared repository or registry.
|
|
- Use **JSON Schema** for simple JSON payloads, **Protobuf** or **Avro** for compact binary formats.
|
|
- Version every schema: `order.v1.Event`, `order.v2.Event`.
|
|
- Never change a schema without versioning — old consumers must not break.
|
|
- Run contract tests in CI to verify producers and consumers agree on the schema.
|
|
|
|
---
|
|
|
|
## 42. The Law of the BFF (Backend For Frontend)
|
|
|
|
> "The frontend shall not chase a dozen services — one gate, one answer."
|
|
|
|
- Introduce a thin BFF service specifically for the Nuxt.js app.
|
|
- The BFF aggregates data from multiple microservices into a single response.
|
|
- It handles authentication, forwards user context, and hides internal service layout.
|
|
- Implement as a dedicated Go service or use the API gateway for basic aggregation.
|
|
- Direct file uploads: client requests a **pre-signed URL**, uploads directly to Minio, then notifies the BFF via API to process the file.
|
|
|
|
---
|
|
|
|
## 43. The Law of Alerting
|
|
|
|
> "A silent failure is a failure twice over."
|
|
|
|
- Configure **Prometheus Alertmanager** with rules for:
|
|
- High HTTP error rate (5xx > 1% of traffic)
|
|
- RabbitMQ queue depth exceeding threshold
|
|
- Disk space running low (< 20%)
|
|
- Service unavailable (health probe fails)
|
|
- Route alerts to a notification channel (Telegram, Slack, email).
|
|
- Set up on-call rotation for production incidents.
|
|
- Every alert must be actionable — no alert fatigue.
|
|
|
|
---
|
|
|
|
## 44. The Law of Feature Flags
|
|
|
|
> "Deploy is not release — separate the act from the unveiling."
|
|
|
|
- Decouple deploying code from releasing features using configuration-driven flags.
|
|
- Use environment variables or a lightweight feature-flag service.
|
|
- Enable features for a percentage of users for gradual rollout.
|
|
- Instantly disable broken features without redeploying.
|
|
- Perform A/B testing by routing different user cohorts to different flag values.
|
|
- Remove flags once the feature is fully rolled out and stable.
|
|
|
|
---
|
|
|
|
## 45. The Law of Backup and Disaster Recovery
|
|
|
|
> "A backup untested is a prayer unanswered."
|
|
|
|
- **PostgreSQL**: Schedule automated `pg_dump` or use WAL archiving for Point-In-Time Recovery. Store off-site.
|
|
- **Redis**: Configure RDB snapshots (if used for persistence) and backup those files.
|
|
- **Minio**: Mirror buckets to remote storage using `mc mirror` or bucket replication.
|
|
- **Elasticsearch**: Take snapshots to a shared repository (S3/Minio).
|
|
- **Test restores regularly** — if you haven't tested it, it's not a backup.
|
|
- Document the disaster recovery runbook and practice it.
|
|
|
|
---
|
|
|
|
## 46. The Law of Infrastructure as Code
|
|
|
|
> "Servers built by hand are cattle without brands — untracked, unversioned, unrepeatable."
|
|
|
|
- Define all infrastructure (servers, networks, databases, DNS) using **Terraform** or **Pulumi**.
|
|
- Store IaC configurations in Git. Review changes via merge requests.
|
|
- Replicate environments (dev, staging, production) from the same codebase.
|
|
- Never SSH into servers to make manual changes — if it's not in code, it doesn't exist.
|
|
|
|
---
|
|
|
|
## 47. The Law of Cost Management
|
|
|
|
> "Thou shalt not waste compute on idle thoughts."
|
|
|
|
- Set Kubernetes resource requests and limits on every container.
|
|
- Right-size database instances — monitor and adjust.
|
|
- Define retention policies on logs, metrics, and cached data.
|
|
- Monitor cloud/electricity bills and set budget alerts.
|
|
- Use spot/preemptible instances for non-critical workloads.
|
|
- Remove unused resources: orphaned volumes, stale load balancers, idle instances.
|
|
|
|
---
|
|
|
|
> "Code shall read like scripture: strict, clean, and unambiguous.
|
|
> Cleverness is temptation; clarity is salvation."
|
|
> — db123
|
|
|
|
**Thus ends the Holy Code Bible.**
|
|
**Follow it, or face undefined behavior.**
|