Skip to content

Dry Dock Drills

The core lesson is complete.

Now comes the part where we make sure the ideas actually stick.

These drills are optional, but they are excellent final reps. They focus less on “can we deploy?” and more on:

  • can we make a live app easier to understand?
  • can we spot bad operational patterns quickly?
  • can we add small support-minded improvements without overengineering the whole thing?

Think of these as final dry runs for the habits we want to leave with.

Keep the Scope Small

None of these drills are meant to become giant side projects. The point is to strengthen production-awareness instincts with small, deliberate changes.


Drill 1 — Maintenance: Add a Tiny Beacon

Section titled “Drill 1 — Maintenance: Add a Tiny Beacon”

Objective: Add the smallest possible operational health signal.

You have an Express application that starts and serves routes, but there is no clean way to ask:

“Are you alive right now?”

Add a dedicated route at:

/ping

It should return a small JSON response such as:

{ "status": "alive" }

Keep it fast and simple.

You should end up with a small app.get() route that:

  • responds immediately
  • does not depend on heavy logic
  • gives operators a basic liveness signal
Student-to-AI Prompt

I want to add the smallest possible health-style route to an Express app. Show me the minimal code for a /ping endpoint that returns a simple JSON response, and explain why this kind of route is useful in a deployed application.


Drill 2 — Custom Job: Fail Loudly, Not Rudely

Section titled “Drill 2 — Custom Job: Fail Loudly, Not Rudely”

Objective: Improve operational visibility around a failing route.

A route catches an error and returns a generic 500 message to the user, but logs nothing useful for the developer or operator.

app.post("/checkout", async (req, res) => {
try {
await processPayment(req.body);
res.status(200).send("Success.");
} catch (error) {
res.status(500).send("Something went wrong with the payment.");
}
});

Update the catch block so it:

  • logs a clear error message
  • includes the underlying error.message
  • still returns a safe, generic message to the user

You should end up with a route that separates:

  • operator-facing detail in the logs
  • user-facing messaging in the response

That is exactly the habit we want.

Student-to-AI Prompt

I have an Express route with a try/catch block. I want the user to see a safe generic error message, but I want the server logs to include the actual failure details. Show me a clean pattern for doing both at once.


Drill 3 — Solo Special: The Hanging Middleware Trap

Section titled “Drill 3 — Solo Special: The Hanging Middleware Trap”

Objective: Diagnose one of the most common Express middleware mistakes.

You add global request logging middleware. The logs look great, but suddenly every request hangs forever.

Here is the code:

app.use((req, res) => {
const time = new Date().toISOString();
console.log(`[${time}] Traffic spotted: ${req.method} ${req.url}`);
});

Identify what is missing and fix it.

You should recognize that the middleware intercepted the request but never passed control onward.

The fix is to:

  • include next in the middleware signature
  • call next() after logging
Student-to-AI Prompt

I wrote an Express app.use() middleware to log requests, and now every route hangs forever. The logging works, but the responses never complete. Help me spot what I forgot and explain why the whole app appears frozen.


Objective: Make a health-style endpoint a little more informative without making it heavy.

A route like /health is useful, but { "status": "healthy" } might be a bit too bare.

Expand your heartbeat route so it safely returns a little more context, such as:

  • uptime
  • current environment
  • timestamp

Keep it lightweight. Do not turn it into a dependency stress test.

You should end up with a route that is still fast, but more helpful for quick operational sanity checks.

Student-to-AI Prompt

I want my Express /health endpoint to be a little more useful than just { "status": "healthy" }, but I do not want to make it slow or overly complicated. What fields would be reasonable to include?


Objective: Reduce release confusion after multiple deploys.

You deploy several times in one afternoon. A teammate asks:

“Which version is actually live right now?”

Add a /version route that returns a small release identifier from an environment variable like:

APP_VERSION

You should end up with a route that helps answer:

  • what version is currently running?
  • did the expected release actually make it to the hosted app?
Student-to-AI Prompt

I want my deployed Express app to expose a /version endpoint using an environment variable like APP_VERSION. Show me a clean way to implement it and explain why this is useful during rapid deployment cycles.


Objective: Learn to interpret what different operational clues actually mean.

Imagine the following observations:

  • the startup log appears normally
  • /health returns 200 OK
  • a POST request shows up in the request logs
  • but the route still returns a 500

Explain what those signals tell you, and where you would investigate next.

You should be able to reason that:

  • the app started
  • the route is reachable
  • the request reached the server
  • the failure is likely happening deeper inside route handling or a dependency interaction

This is less about coding and more about thinking like an operator.

Student-to-AI Prompt

My startup logs look healthy, my /health route returns 200, and I can see the incoming request in the logs, but the user still gets a 500 error. Help me reason through what that narrows down and what layer I should investigate next.


If you want a good progression through the drills, try them in this order:

  1. Add a tiny beacon
  2. Fail loudly, not rudely
  3. Fix the hanging middleware trap
  4. Improve the heartbeat
  5. Add the version endpoint
  6. Practice interpreting the combined signals

That order moves from small implementation changes into stronger operational reasoning.


This final lesson was never about turning us into full-time SREs in a single afternoon.

It was about establishing one very practical idea:

once software is live, supportability matters.

These drills reinforce that idea by pushing us to make the application:

  • a little clearer
  • a little easier to inspect
  • a little easier to trust
  • and a lot less silent when something goes wrong.

Express: Error Handling

The drills are complete. All that remains is the quick-reference manifest: a compact operational codex for the road.