Why "works on my machine" keeps happening—and what we're building to fix it.
You've seen this before:
Your multi-service app works perfectly locally. All services talk to each other, tests pass, everything's green. You push to CI.
CI fails.
Or staging breaks. Or production returns 404s on routes that worked fine on your laptop.
The Problem: localhost Isn't Production
Here's what you're actually testing locally:
// Your local code
const API_URL = 'http://localhost:3001';
fetch(`${API_URL}/users`); // Direct call, root path
Here's what runs in production:
// Same code, different environment
const API_URL = 'https://api.example.com';
fetch(`${API_URL}/users`); // Through gateway, with /api prefix, auth headers
Different calling patterns. Different routing. Different configuration.
You didn't test the wrong code. You tested code running in the wrong environment.
What's Missing: Production Primitives
Production has:
-
DNS-based service names - Services call
api.internal, notlocalhost:3001 - Gateway routing - Single entrypoint, path-based routing, header injection
- Service discovery - Services find each other by name, not hardcoded ports
-
Centralized config - One source of truth, not scattered
.envfiles
Local development has:
- Port hunting (
localhost:3000,3001,3002...) - Direct service calls (no gateway)
- Hardcoded URLs everywhere
- Configuration drift across machines
This gap is why integration bugs show up late.
Introducing NDL
We're building NDL (Nuewframe Development Library) to bring production primitives to local development:
# Declare your service
apiVersion: apps/v1alpha1
kind: Deployment
metadata:
name: api
namespace: default
spec:
source:
workingDirectory: "./api"
command: "npm start"
environment:
PORT: "{{.Service.Port}}"
service:
port: 3001
Then interact by service name:
ndl apply -f manifest.yaml
# Call by DNS name (not localhost)
curl http://api.default.ndl.test:18400/users
Your frontend code:
// Same pattern locally AND in production
const API_URL = process.env.API_URL; // http://api.default.ndl.test:18400
fetch(`${API_URL}/users`);
Key Ideas
1. DNS-based service names
Stop calling localhost:3001. Start using api.myapp.ndl.test.
2. Gateway routing
Test path prefixes, header injection, and routing rules locally—before they break in staging.
3. Service discovery
Services find each other by name. No more hardcoded ports.
4. Polyglot support
Same workflow for Node.js, .NET, Python, Go, Java. Just change the command line.
5. No Kubernetes required
Uses Kubernetes architectural patterns (declarative manifests, reconciliation, operators) without needing a cluster. 50MB vs 4GB, 2s startup vs 90s.
See It in Action
We've published two demos showing NDL with real apps:
Demo 1: Simple Service (2 min)
Basic "hello world" showing DNS routing and gateway access.
Demo 2: .NET Multi-Service App (5 min)
Real distributed system: API + frontend + database + centralized config.
Read the Full Story
I've written a comprehensive introduction covering:
- Why local dev loses validation
- The primitives production has (that local dev doesn't)
- How NDL works (without becoming "Kubernetes on your laptop")
- Who it's for (and when it's not the right fit)
- The architectural approach (declarative, event-driven, extensible)
📖 Read: Introducing NDL - Simplifying Local Development for Distributed Systems
Early Access
NDL is in early access (public GitHub release coming March 2026). We're gathering feedback from developers and teams building distributed systems locally.
What we're looking for:
- Teams feeling the pain of config drift and late integration failures
- Polyglot environments (Node + .NET + Python + Go)
- Platform engineers standardizing local dev across teams
- Anyone tired of "works on my machine" onboarding
What you'll get:
- Early access to NDL before public release
- Direct input on roadmap and features
- Support for getting set up with your specific stack
Join the early access waitlist →
When you sign up, let us know what problem you're hoping NDL will help you solve. That feedback directly shapes what we build.
Questions I'm Expecting
"How is this different from Docker Compose?"
Docker Compose orchestrates containers. NDL brings production primitives (DNS, gateway, discovery) to local dev—whether you use containers or not. Many teams use both: Compose for databases/infra, NDL for app services. I'll be posting a detailed comparison next week.
"Do I need Kubernetes knowledge?"
No. NDL uses Kubernetes architectural patterns (declarative manifests, reconciliation) but doesn't require a cluster. If you can write YAML, you can use NDL.
"What languages/runtimes are supported?"
Node.js, Deno, .NET, Python, Go, Java—anything that runs on your laptop. The manifest just changes the command line. I'll be posting a polyglot guide showing Node + .NET + Python + Go in one system.
"Can I use this with existing projects?"
Yes. NDL works with new and existing projects. Start by adding a manifest to your main service, then expand to dependencies incrementally.
Coming next on Dev.to:
- NDL vs Docker Compose (detailed comparison)
- .NET Microservices Tutorial
- Polyglot Guide (Node, .NET, Python, Go—one manifest)
Follow me here or on Twitter/X for updates.
Top comments (0)