CodingNic

Testing, Verification & Next Steps

Next Steps with an AI Assistant

Testing, Verification & Next Steps 20 min read

Next Steps with an AI Assistant

Objectives

By the end of this chapter, you should be able to:

  • Direct an AI assistant through the aircraft/route enrichment pattern this course left out, reusing the caching shape you already reviewed once in Module 5
  • Explain the specific scaling limit Module 2 flagged, and what to tell an AI assistant if that limit is ever actually hit
  • Direct an AI assistant from npm run dev toward something deployable, and know which parts of its output need the closest read

💡 Why this matters: A course has to stop somewhere, and where it stops shouldn’t feel arbitrary. Every feature left out below was left out for a specific, stated reason, not because it was hard, but because it wasn’t load-bearing for anything this course needed to teach. Each one reuses a pattern you’ve already prompted, reviewed, and steered an AI assistant through at least once.

Aircraft, Type, and Route Enrichment

The real SKYWATCH app enriches the detail panel with registration, manufacturer, and model, a type photo, and origin/destination for the current flight, none of which this course built. That’s not an oversight. It’s the same “Postgres cache in front of a flaky third-party API” shape Module 5’s METAR weather badge already taught, applied to a couple more upstream sources instead of one.

If you want an AI assistant to build this, the prompt is closer than it looks to the METAR one: a route handler that checks a Postgres cache table first, calls the upstream API on a miss, stores whatever comes back, and returns it; a frontend hook that does the same two-tier caching on the client, an in-memory Map for the lifetime of the tab, falling back to the server route on a miss. The one thing worth naming explicitly in the prompt, because it’s the one part an AI assistant is likely to skip if you don’t: the cache table needs a notFound: boolean column and a fetchedAt timestamp, not just the enrichment data itself. Without that column, “never looked this hex up” and “looked it up, upstream has nothing” collapse into the same falsy value, and the second case gets retried on every single request forever instead of being remembered as a confirmed miss. That’s the exact same three-state design mistake worth checking for that Module 5’s home field taught you to watch for, just showing up in a new table. When you review what the AI produces, look for that column by name before anything else.

The Scaling Limit Module 2 Already Named

Module 2’s realtime-layer lesson said this outright, and it’s worth returning to now that the whole app is built: positions travel over a plain in-process EventEmitter because it’s the simplest thing that works at this app’s actual scale, one Node process running both the poller and the WebSocket server. That stops working the moment the poller and the API/WS server become two separate processes, for instance scaling the WebSocket layer horizontally behind a load balancer, or running the poller as a separate worker so a WS server restart doesn’t interrupt polling. At that point, eventBus.emit("positions", ...) in the poller’s process is invisible to a WebSocket connection sitting in a different process.

If that need actually arrives, the prompt to an AI assistant is narrow and specific: swap the in-process emitter for Redis, emitPositions() becomes a PUBLISH, onPositions() becomes a SUBSCRIBE callback, and every call site in poller/loop.ts and ws/register.ts stays exactly where it is. Alerts already made this exact tradeoff, on real Postgres LISTEN/NOTIFY, specifically because they needed to survive a process boundary an in-process emitter can’t cross. What’s worth watching for when an AI assistant does this migration is the opposite failure from the enrichment cache: not a missing detail, but overreach. It’s a very plausible AI move to reach for Redis pub/sub the moment you even mention “scaling” in a prompt, before you’ve actually split the poller and the WS server into two processes. Push back on that. The single-process EventEmitter is correct today, and adding Redis before the process split actually happens is a real cost, a new service to run and operate, paid for a scenario that hasn’t occurred yet.

From npm run dev to Deployed

Nothing about this course’s Postgres setup, a docker-compose.yml with one postgres service, is production-shaped. It’s a local dev convenience. If you’re directing an AI assistant toward an actual deploy, here’s roughly the order you’d hit these, and what’s worth reading closely in each:

  • Containerize both apps, not just Postgres. Ask for a Dockerfile for apps/server and one for apps/web, each a multi-stage build, install, npm run build, then copy only the production output into a slim final image. Check the final stage doesn’t COPY the whole repo, including dev dependencies and the other app’s source, into the deployed image. That’s an easy corner for an AI assistant to cut, since a single-stage COPY . . also technically works.
  • Environment variables move from .env to whatever your host provides. DATABASE_URL pointing at a managed Postgres instance instead of the local Compose container, WEB_ORIGIN set to the real deployed frontend URL (Module 2’s CORS setup already reads this correctly, it just needs the right value), NEXT_PUBLIC_SERVER_URL set to the real deployed backend URL. Ask an AI assistant to grep the codebase for every process.env read before you deploy, and confirm each one has a real value set on the host, not just a local default that silently degrades in production.
  • Migrations become a deploy step, not a local command. npm run db:migrate needs to run against the production database as part of shipping a release, before the new server code that expects the new schema starts receiving traffic. If you ask an AI assistant to wire this into a deploy pipeline, check the ordering explicitly: migrate first, then roll out the new server code, never the reverse.
  • A process manager or platform-level restart policy for the poller. It’s a long-running process with real consequences, missed poll cycles, a stale “last update” indicator, if it silently dies and nothing brings it back. Whatever an AI assistant sets up here, confirm it actually restarts the poller on crash, not just on a full redeploy.

What This Course Didn’t Build, On Purpose

A few things were left out entirely, worth stating plainly rather than leaving implicit:

  • No authentication, no multi-user support. SKYWATCH is a single-operator tool by design, one set of watch regions, one home location, one set of saved locations. Adding real multi-tenancy means threading a userId through nearly every table and route this course built, a genuinely different app, not an incremental prompt.
  • No pagination on any list endpoint. Everything caps out at a limit query param with a hard maximum, correct at the data volumes this app actually produces, and the first thing that would need to change if it tracked airspace busy enough to produce thousands of rows a day.
  • No rate limiting on the REST API. Everything here assumes a small number of trusted clients. A public-facing deployment needs it before going live.

None of these are hard to prompt an AI assistant to build. They were left out because they’d change what kind of app this is, and a course has to stop somewhere that isn’t arbitrary.

What You Actually Walked Away With

An AI assistant wrote nearly every line of code in this app. If that’s all this course taught, it wouldn’t have been worth six modules. What you actually practiced, over and over, from the buildless packages/shared check in Module 1 to the WebSocket reconnect check two lessons ago, is something an AI assistant can’t do for you: knowing what correct looks like before you ask for it, reading generated output against that standard instead of against “does it look plausible,” and catching the specific, realistic ways a confident-sounding result can still be wrong. A missing partial index. A route that quietly skips the repo layer. A test suite that’s green for the wrong reason. A verification report describing a check that was reasoned about, not run. None of those show up as a crash. All of them show up if you know to look, and none of them would have been caught by trusting the AI’s own account of its work.

That’s the skill this course was actually teaching, and it’s the one that doesn’t go stale the next time the underlying model gets better. You’ve now directed an AI assistant through SKYWATCH from an empty repository to a full-featured, real-time flight tracker: a Postgres schema with database-level invariants, a poller and detection engine, a WebSocket realtime layer, a four-theme frontend, watch regions with history playback, and a full feature set on top, all verified end to end, by you, not by the AI’s word for it. That’s the course.