Tools: Your Local Environment Tests a Different System Than Production

Tools: Your Local Environment Tests a Different System Than Production

Source: Dev.to

The Problem: localhost Isn't Production ## What's Missing: Production Primitives ## Introducing NDL ## Key Ideas ## See It in Action ## Read the Full Story ## Early Access ## Questions I'm Expecting 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. Or staging breaks. Or production returns 404s on routes that worked fine on your laptop. Here's what you're actually testing locally: Here's what runs in production: Different calling patterns. Different routing. Different configuration. You didn't test the wrong code. You tested code running in the wrong environment. Local development has: This gap is why integration bugs show up late. We're building NDL (Nuewframe Development Library) to bring production primitives to local development: Then interact by service name: 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. 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. I've written a comprehensive introduction covering: 📖 Read: Introducing NDL - Simplifying Local Development for Distributed Systems 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: 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. "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: Follow me here or on Twitter/X for updates. Templates let you quickly answer FAQs or store snippets for re-use. Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment's permalink. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse CODE_BLOCK: // Your local code const API_URL = 'http://localhost:3001'; fetch(`${API_URL}/users`); // Direct call, root path Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: // Your local code const API_URL = 'http://localhost:3001'; fetch(`${API_URL}/users`); // Direct call, root path CODE_BLOCK: // Your local code const API_URL = 'http://localhost:3001'; fetch(`${API_URL}/users`); // Direct call, root path CODE_BLOCK: // 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 CODE_BLOCK: // Same code, different environment const API_URL = 'https://api.example.com'; fetch(`${API_URL}/users`); // Through gateway, with /api prefix, auth headers CODE_BLOCK: // Same code, different environment const API_URL = 'https://api.example.com'; fetch(`${API_URL}/users`); // Through gateway, with /api prefix, auth headers COMMAND_BLOCK: # 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 COMMAND_BLOCK: # 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 COMMAND_BLOCK: # 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 COMMAND_BLOCK: 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 COMMAND_BLOCK: ndl apply -f manifest.yaml # Call by DNS name (not localhost) curl http://api.default.ndl.test:18400/users COMMAND_BLOCK: ndl apply -f manifest.yaml # Call by DNS name (not localhost) curl http://api.default.ndl.test:18400/users CODE_BLOCK: // 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 CODE_BLOCK: // Same pattern locally AND in production const API_URL = process.env.API_URL; // http://api.default.ndl.test:18400 fetch(`${API_URL}/users`); CODE_BLOCK: // Same pattern locally AND in production const API_URL = process.env.API_URL; // http://api.default.ndl.test:18400 fetch(`${API_URL}/users`); - 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 - Port hunting (localhost:3000, 3001, 3002...) - Direct service calls (no gateway) - Hardcoded URLs everywhere - Configuration drift across machines - 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) - 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 - Early access to NDL before public release - Direct input on roadmap and features - Support for getting set up with your specific stack - NDL vs Docker Compose (detailed comparison) - .NET Microservices Tutorial - Polyglot Guide (Node, .NET, Python, Go—one manifest)