logoalt Hacker News

mkw5053yesterday at 3:49 PM0 repliesview on HN

I use these exact principles (which change):

  1. No overengineering. Minuscule complexity. Always pick the smallest
     implementation that works. No speculative features, no defensive code
     for impossible cases, no premature abstraction.

  2. Lean on tools, libraries, vendors, and existing internal patterns so
     we maintain the least complexity ourselves. Before any real
     implementation decision, default to discovery (official docs, recent
     trusted expert sources, etc).

  3. Shift left in the SDLC. For every check, pick the cheapest
     deterministic mechanism upstream. Hierarchy: types, then lint, then
     DB constraints, then build-time checks, then deterministic CI, then
     tests we maintain.

  4. Tests are not exempt from minimum-complexity discipline. Don't write
     tests for the sake of coverage. Tests must be few, complementary, and
     valuable. If a type, lint rule, DB constraint, or build-time check
     already proves something, a test that re-asserts the same guarantee
     is duplication: extra source to maintain, drifts from reality, adds
     CI latency.

  5. Compound engineering. When you teach me a rule, build the prevention
     into artifacts (AGENTS.md, lint, hooks, reviewer prompts) so it
     applies automatically going forward. Don't rely on memory.

  6. Prefer functional, pure, immutable. Mutation is a smell unless inside
     a contained scope with a clear reason. Arrow functions plus
     reduce/map/filter over for-loops with let.

  7. Parse, don't validate. Boundary inputs become typed values via Zod
     (or similar); downstream code carries the type. No scattered
     re-validation.

  8. Functional core, imperative shell. I/O at the edges; domain logic
     pure.

  9. Keep going. Don't stall on ceremony. Make forward progress.