- Plain HTML
- Bun
- Bun's shell scripting mode
- TypeScript
- Property Based Testing
- Monoliths
- SQLite
- Bazel / Buck2
- Observable Plot
- Tailwind
- Shell scripts
- Python
- Pass, don't validate (from https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/)
- Log, or rethrow an exception. Never both.
- Fakes belong next to the real implementation (not in the test directory!) because they often contain a lot of implementation details. This allows the fake to be tested, dependency injected with less config, and exported to consumers of the API to use in their own tests
- If you can get away with it, put tests alongside the implementation, like Rust, or Python doctests. Yes, fakes need to be tested too.
- All test data must be generated using a Property Based Testing framework. All data types
Tshould have an acompanyingArbitrary<T>. - Never call an implementation
...Impl. Given an interfaceCache, don't call itCacheImpl! If it uses an in-memory hashmap, call itInMemoryCache. Someone can come along and create aRedisCachewithout renaming everything! - Interfaces should be reasonably small. If it gets too big (more than 5 methods?), split it. This will make it easier to write fakes.
- Don't store booleans, store datetimes (from https://changelog.com/posts/you-might-as-well-timestamp-it)
from https://htmx.org/essays/codin-dirty/ and https://htmx.org/essays/locality-of-behaviour/
- Strive for Locality of Behaviour. Don't spread logic across multiple files if it can reasonably live together. "Spooky action at a distance" is a maintenance liability. When LoB conflicts with DRY or Separation of Concerns, use judgment — a violation a few lines away is far less severe than one in a separate file.
- Big functions are fine. A few large "crux" functions (up to 200–400 LOC) are acceptable and often preferable — they keep important logic visible and together rather than smeared across dozens of tiny abstractions. Support functions: ~10–20 LOC. Utilities: ~5–10 LOC. Don't split functions just to keep them small; only split for genuine reuse or clarity.
- Prefer integration tests over unit tests. Avoid heavy unit testing early on before abstractions have settled — it locks you into implementation details. Once the core API has crystallised, test it exhaustively at the integration level. Integration tests survive refactors; unit tests often don't. TDD is fine but apply it at the API level, not the unit level.
- Keep class/concept count low. Resist the urge to decompose everything into single-responsibility micro-classes. Fewer concepts = less cognitive overhead. It's fine for a class to serve multiple layers (e.g. an ORM model that also supports view logic). Prefer a longer if/else over unnecessary polymorphism and new abstractions.
- Emergent over prescribed aesthetics.
- Expose state and inner workings.
- Dense, not sparse.
- Explicit is better than implicit.
- Engineered for human vision and perception.
- Regiment functionalism.
- Performance is design.
- Verbosity over opacity.
- Ignore design trends. Timeless and unfashionable.
- Flat, not hierarchical.
- Diametrically opposite of minimalism, as complex as it needs to be.
- Driven by objective reasoning and common sense.
- Don't infantilize users.