2 min read
Dependency-free by default
Why karf.dev treats every npm install as a trade-off, and how building cron parsers, OG generators, and password tools without external libraries keeps the project honest.
The default answer to “should we add a dependency?” on this project is no.
Not out of stubbornness — out of economics. Every package you pull in is a maintenance surface: upgrade churn, bundle weight, supply-chain risk, and one more thing to audit when something breaks at 2 AM. For a portfolio site that ships 190+ client-side tools, that surface area adds up fast.
The rule
Before reaching for npm, ask: can I build this in under 200 lines with platform APIs? If yes, build it. If no, justify the dependency against a concrete need — not convenience.
What that looks like in practice
Cron parser. Standard 5-field cron syntax, next-run calculation, human-readable
summaries. The Date API and some modular arithmetic handle it. No cron-parser, no
later.js. The result is ~120 lines in src/lib/cron.ts with 30 unit tests.
OG image generation. Most projects reach for satori (or Vercel’s OG library) to
render Open Graph images. This site generates them as SVG templates with a seeded
identity motif — string interpolation, a FNV-1a hash for deterministic colors, done.
Zero runtime dependencies, sub-millisecond generation at build time.
Reading time. Instead of pulling reading-time plus mdast-util-to-string, a
12-line remark plugin counts words from the AST and divides by 200. It’s been accurate
enough for 200+ pages.
Password generation. crypto.getRandomValues() provides the entropy.
Math.log2(poolSize) * length gives the entropy estimate. Strength grading is four
threshold comparisons. No generate-password, no zxcvbn.
When dependencies earn their place
Three runtime packages made the cut — each against a specific technical argument:
marked— Markdown-to-HTML parsing for the Markdown Preview tool. Writing a spec-compliant CommonMark parser is a multi-thousand-line undertaking. The trade-off is clear.dompurify— XSS sanitization for rendered HTML output. Security-critical code that benefits from community auditing. Rolling your own sanitizer is how you get bypassed.@astrojs/rss— RSS feed generation against the Atom/RSS spec. A 4 KB package that handles XML edge cases correctly.
Each addition was a deliberate decision, documented in the phase retrospective that introduced it.
Why it matters
After 79 phases of development, the project has 9 runtime dependencies total. The
node_modules folder exists, but it’s mostly dev tooling — TypeScript, Vitest, Astro
itself. The code that ships to users is almost entirely first-party.
This isn’t purity for its own sake. It’s a design constraint that forces you to understand the problem before solving it. When you write your own cron parser, you learn cron. When you template your own OG images, you learn SVG. The dependencies you avoid become knowledge you gain.
The portfolio demonstrates craft partly through what it chose not to install.