惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

L
LangChain Blog
阮一峰的网络日志
阮一峰的网络日志
Y
Y Combinator Blog
博客园 - 聂微东
GbyAI
GbyAI
H
Hackread – Cybersecurity News, Data Breaches, AI and More
月光博客
月光博客
T
The Blog of Author Tim Ferriss
爱范儿
爱范儿
宝玉的分享
宝玉的分享
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
雷峰网
雷峰网
MyScale Blog
MyScale Blog
Cloudbric
Cloudbric
T
Threatpost
Scott Helme
Scott Helme
N
News | PayPal Newsroom
Google DeepMind News
Google DeepMind News
Martin Fowler
Martin Fowler
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Engineering at Meta
Engineering at Meta
美团技术团队
Apple Machine Learning Research
Apple Machine Learning Research
V2EX - 技术
V2EX - 技术
罗磊的独立博客
Attack and Defense Labs
Attack and Defense Labs
IT之家
IT之家
S
Secure Thoughts
C
Cyber Attacks, Cyber Crime and Cyber Security
D
Docker
V
V2EX
T
Troy Hunt's Blog
Forbes - Security
Forbes - Security
aimingoo的专栏
aimingoo的专栏
J
Java Code Geeks
Last Week in AI
Last Week in AI
C
CERT Recently Published Vulnerability Notes
T
Tor Project blog
P
Proofpoint News Feed
Recorded Future
Recorded Future
B
Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Simon Willison's Weblog
Simon Willison's Weblog
A
About on SuperTechFans
The GitHub Blog
The GitHub Blog
MongoDB | Blog
MongoDB | Blog
O
OpenAI News
V
Vulnerabilities – Threatpost
P
Privacy International News Feed
云风的 BLOG
云风的 BLOG

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
What We Lose When Everything Is a Wrapper
Viktor Lázár · 2026-05-18 · via DEV Community

What a 14-year-old HTML5 Wolfenstein 3D port taught me about owning the stack

A recent Primeagen video sent me back into an old feeling: the strange discomfort of realizing how much of modern software is now built around things we do not really own.

Not in the legal sense. In the practical sense.

We install a package, which installs other packages, which wrap lower-level packages, which hide runtime behavior behind conventions, build steps, plugins, adapters, loaders, and generated code. The result works. Often it works beautifully. But there is a quiet moment, usually during debugging, where the shape of the thing becomes hard to hold in your head. You are no longer asking, "What does my program do?" You are asking, "Which layer is currently doing this on my behalf?"

That is not always a problem. Abstraction is one of the reasons software can grow at all. Nobody should rewrite a font renderer to build a settings page. Nobody should reimplement TLS because they want an API endpoint. Most dependency-free manifestos eventually collapse into a kind of performance art where the programmer is very proud, very tired, and still missing Unicode.

This is not that argument.

This is not an argument against dependencies. It is an argument for knowing when they are helping you build, and when they are quietly becoming the thing you are building around.

The Game With No Dependencies

Fourteen years ago, I ported Wolfenstein 3D to HTML5.

No engine. No framework. No package manager. No third-party runtime dependencies.

Just JavaScript, canvas, and a very specific obsession.

There was a production build step, because the web has always had its rituals. The release build went through Google's old Java-based Closure Compiler. But that was packaging, not architecture. It made the output smaller. It did not define the shape of the program.

That distinction matters. It was not a small raycasting demo inspired by Wolfenstein. It was an actual browser port of the game, built directly on the platform the browser gave me at the time. This is the same idea I was circling around in The Browser Was the Engine: for that project, the browser was not just a deployment target. It was the engine.

I am open-sourcing it now, and the thing that feels most surprising in 2026 is not that it still exists. It is that there is almost nothing underneath it that can have gone stale. There is no abandoned package to replace. No transitive dependency tree to audit. The old minifier is a historical detail, not a living dependency graph. No ecosystem era is trapped in amber around it.

It is just code.

That sounds smaller than it is. A complete game is not a toy problem. Even a simple raycasting engine needs a loop, input, collision, map parsing, rendering, textures, sprites, timing, state, audio, UI, and all the little bits of glue that turn a technical demo into something that feels like a game. You do not get to skip complexity by avoiding dependencies. You only choose where the complexity lives.

In that project, the complexity lived in the project.

That made the code less general. It was not a reusable game engine. It did not have an extension system, a plugin API, a renderer abstraction, or a roadmap. It was not trying to become a platform. It was trying to be one thing.

That narrowness was the feature.

The renderer did not need to serve every possible game. The input layer did not need to become an input library. The map representation did not need to model the universe. Every piece of the program could be shaped around the exact problem in front of it. The code had no ambition beyond the game, and because of that, the game could be completely itself.

Fourteen years later, that still matters.

This used to be much more normal. On the C64 and the Amiga, on MS-DOS machines, and especially on old consoles from Nintendo, Sega, and the rest of that era, software was often written as a direct answer to a specific machine. A game was not usually a thin layer over a general engine over a general runtime over a general abstraction of hardware. It was a peculiar little organism fitted to the memory layout, graphics modes, timing behavior, controller shape, and limits of the target. The constraints were brutal, but they made the program concrete. You could feel the machine in the code.

I do not want to pretend that era was better in every way. It was harder, narrower, less portable, and full of tricks nobody should be forced to rediscover for ordinary application work. But it had one quality that is easy to miss now: the program and the platform had an intimate relationship. The software did not float above the machine. It negotiated with it directly.

The Shelf Life of Small Surfaces

Software ages in strange ways.

Sometimes code ages because the problem changes. Sometimes it ages because the platform changes. But a huge amount of modern code ages because the stack around it moves.

The package manager changes. The bundler changes. The framework changes. The framework's compiler changes. The plugin ecosystem changes. The lockfile format changes. The version of Node you need becomes a small archaeological site. You do not just maintain the application. You maintain the ability to assemble the application.

There is a real cost hidden there.

Dependencies do not only add code. They add motion. They add release notes, security advisories, migration guides, incompatible peer ranges, deprecated APIs, and small future obligations that are easy to accept because they arrive wrapped in convenience.

That convenience is often worth it. But it is not free.

The dependency-free Wolfenstein source has a different aging profile. It depends on the browser, of course. Every web program does. The production build once depended on Closure Compiler. But the game itself does not depend on a cultural moment in the JavaScript ecosystem. The old build step can be replaced, skipped, or recreated because it was only an output step. The source does not need a 2012 toolchain to remember how to be itself. Its surface area is small enough that the future had fewer places to break it.

This is one of the underrated virtues of writing something directly: you reduce the number of external clocks your project has to keep time with.

Every dependency is a clock. Some tick slowly and responsibly. Some tick chaotically. Some stop. Some are taken over by someone else. Some are still correct but no longer fashionable. Some become infrastructure so deeply embedded that nobody remembers choosing them.

The fewer clocks you attach to a project, the longer it can sit quietly without turning into an incident.

Generality Has a Price

A dependency is usually a package of generality.

That is its value. The author solved a problem broadly enough that you can reuse the solution in a context they did not know about. A good library is a small miracle of compression: years of thought hidden behind a function call.

But generality has a shape, and that shape enters your program.

If you use a game engine, your game starts to inherit the engine's idea of a game. If you use a framework, your application inherits the framework's idea of an application. If you use a router, a state manager, an ORM, a component library, a testing framework, and a build system, your program becomes partly an expression of all their assumptions.

Again, this can be good. Shared assumptions are how teams move. Conventions are how projects become legible. A framework can save you from wasting your life on plumbing that has nothing to do with the thing you are trying to build.

But every general-purpose layer also asks you to pay for cases that are not yours.

Sometimes you pay in bytes. Sometimes in indirection. Sometimes in concepts. Sometimes in the strange helplessness of reading code that looks simple while knowing that the real behavior lives somewhere else.

This is the wrapper feeling.

Not abstraction itself. Abstraction is a tool. The wrapper feeling is what happens when the abstraction no longer disappears. It stays visible. It stands between you and the material. You can feel yourself programming the wrapper's model of the problem instead of the problem.

At that point, the dependency may still be doing useful work, but it has also become a design constraint. You are building with it, but you are also building around it.

The Good Kind of Dependency

I should be careful here, because I build dependencies too.

I work on react-server. At Level 0x40 Labs, I build open-source tools and primitives, including things like virtual-frame. Some of my work is explicitly about making reusable layers for other programs. I care about composable runtimes, shared primitives, and the boring miracle of code that lets other code exist more easily.

So no, I do not believe the purest program is the one that imports nothing.

The good dependency does something more interesting than save keystrokes. It gives you a stable primitive. It makes a hard boundary understandable. It removes accidental complexity without stealing the shape of the thing you are building.

The best dependencies feel less like wrappers and more like materials.

Canvas is a material. SQLite is a material. A focused parser can be a material. A well-designed runtime primitive can be a material. You can build with them while still feeling that the program is yours.

The dangerous dependency is different. It does not merely provide capability. It brings a worldview. It decides the architecture, the vocabulary, the lifecycle, the extension points, the error model, the deployment model, and the kinds of solutions that feel natural. You can still build something good inside it, but you are no longer only building your thing. You are also participating in its theory of software.

That may be exactly what you want.

If I am building a product with a team, I want shared machinery. I want boring defaults. I want the dependency graph to absorb problems that are not central to the product. I want the project to move because not every layer deserves to be handmade.

But if I am building the core mechanic of a game, or the central abstraction of a runtime, or the part of a system that defines what the system is, I want a different relationship to the code.

I want ownership.

Ownership Is a Design Constraint

Ownership is not the same as authorship.

You can write every line of a program and still not own it, because the design is copied from a framework you are no longer using. You can also depend on a library and still own your system, because the boundary is clear and the library is just a tool in your hand.

Ownership means you understand the important consequences of the system's shape.

You know where time is spent. You know where state lives. You know what happens when input arrives. You know which layer can fail and how failure moves. You know the constraints because you chose them, or at least because you have looked at them directly enough to accept them.

This is what dependency-heavy development can erode. Not skill in the macho sense. Not the ability to write a linked list on a whiteboard. Something more practical: the habit of tracing behavior all the way down until the system becomes concrete again.

When everything is a wrapper, the bottom keeps moving away.

You learn the public API. Then the plugin API. Then the framework convention. Then the generated output. Then the bundler behavior. Then the runtime behavior. Every layer may be reasonable on its own, but the stack as a whole can become a place where nobody quite knows what is happening. The program works because the ecosystem works. Until it does not.

The cost is not only reliability. It is imagination.

If you only build inside other people's abstractions, you start to think in the shapes they make convenient. Your ideas arrive pre-filtered by the tools. You ask, "How do I do this in this framework?" before you ask, "What is this thing, actually?"

That question matters.

The New Game

The game I am working on now also uses no third-party code.

Not because I have become allergic to packages. Not because I think every line must be sacredly handcrafted. It is simpler than that: the game has a specific shape, and a purpose-built implementation fits that shape better than a general engine would.

A game engine would give me a thousand solved problems. Some of those solutions would be useful. Some would be irrelevant. Some would slowly bend the game toward the engine's strengths. I would move faster at the beginning, and then spend part of that saved time negotiating with decisions made for other games.

For this project, that trade does not feel right.

I want the renderer to know exactly what this game is. I want the update loop to carry exactly the concepts the game needs. I want the asset pipeline, the state model, and the interaction rules to be small enough that they can stay in my head. I want the implementation to be shaped by the game, not by the possibility space of all games.

That is not a universal recommendation. It is a local one.

The mistake is treating dependency decisions as identity. Dependency-free does not mean serious. Dependency-heavy does not mean sloppy. The useful question is always more specific:

What part of this system needs to be mine?

Choosing the Layer

There are layers where depending on other people is a sign of maturity.

Cryptography. Databases. Protocol implementations. Image codecs. Font shaping. Accessibility primitives. Hard-won platform knowledge that would be arrogant or irresponsible to casually recreate.

There are layers where depending on other people is mostly a business decision.

Admin UI, routing, deployment glue, test runners, analytics, documentation tooling, forms, dashboards, the parts of a product whose correctness matters but whose originality does not.

And there are layers where depending too early can cost you the thing you are trying to discover.

The core interaction of a game. The execution model of a runtime. The data model of a creative tool. The editor behavior of a text surface. The small strange thing that made the project worth building in the first place.

Those layers deserve suspicion. Not because dependencies are bad, but because premature generality can erase the local shape before you have understood it.

Sometimes the right move is to build the first version directly. Learn the material. Discover the constraints. Let the project teach you what abstractions it actually wants. Then, if a dependency fits, you will know where it fits. You will be choosing it from knowledge rather than reaching for it from reflex.

That difference is everything.

The Muscle

The reason I still care about from-scratch projects is not nostalgia.

It is maintenance of a muscle.

The muscle is the ability to look at a problem without immediately asking which package owns it. The ability to draw the smaller machine inside the larger machine. The ability to build a narrow implementation without apologizing for its lack of generality. The ability to recognize when a dependency is a material, when it is a tool, and when it is slowly becoming the architecture.

Modern software needs dependencies. It also needs people who remember what the layers are made of.

That is what the old Wolfenstein project gives me now. It is not impressive because it avoided packages. It is not morally superior because the dependency list is empty. It is useful because it is a reminder that a complete thing can still be built as a complete thing.

No engine. No framework. No package graph.

A game.

That possibility is worth keeping alive.

Not for every project. Not for every layer. Not as a purity test.

Because sometimes the only way to know what you are building is to build enough of it yourself that it can answer back.