DEV Community

Wael Rabadi for Nuewframe

Posted on

Your Local Environment Tests a Different System Than Production

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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, not localhost: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 .env files

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
Enter fullscreen mode Exit fullscreen mode

Then interact by service name:

ndl apply -f manifest.yaml

# Call by DNS name (not localhost)
curl http://api.default.ndl.test:18400/users
Enter fullscreen mode Exit fullscreen mode

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`);
Enter fullscreen mode Exit fullscreen mode

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.

🎥 Watch on YouTube

Demo 2: .NET Multi-Service App (5 min)

Real distributed system: API + frontend + database + centralized config.

🎥 Watch on YouTube

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)