From Feature Branch to Production: My Shipping Checklist

The checklist I like before shipping: branch discipline, scoped changes, tests, lint, typecheck, build, smoke testing, and honest release notes.

Shipping is where software becomes real. Until then, code is only an idea in a branch.

I like moving fast, but I do not like random shipping. A small checklist catches many mistakes before users see them.

Start with a clean task

Before coding, I want the task to be clear enough that I can explain the finish line.

Not perfect. Just clear.

Good task:

Add unsubscribe endpoint for blog subscribers.
It should be idempotent for already unsubscribed emails.
Unknown emails should return 404.

Bad task:

Fix subscription.

The first task can be tested. The second one invites guessing.

Use a branch that says what changed

Branch names do not need to be clever.

git switch -c feature/blog-subscription-api

That is enough. When multiple tasks are happening, clear branch names reduce confusion.

Read before editing

This is where many bugs start. A developer opens one file, sees one pattern, and edits too quickly.

I try to read:

  • Nearby files
  • Existing tests
  • Package scripts
  • Shared constants
  • Previous task context if it exists

Most codebases already have an answer for how to do the next change. You just need to find it.

Write the risky test first

I do not write tests for every character I type. But if the behavior can break users, I want a failing test before the implementation.

For example:

it("keeps unsubscribe idempotent", async () => {
  await subscribe("hello@example.com");
  await unsubscribe("hello@example.com");

  const second = await unsubscribe("hello@example.com");

  expect(second.status).toBe("unsubscribed");
});

This test becomes a contract. It tells future me what must not break.

Keep the diff small

A good diff is easy to review. It changes the files needed for the task and leaves unrelated improvements for later.

I watch for these signs:

  • Did I rename things not related to the task?
  • Did I format files I did not touch?
  • Did I move code just because I felt like it?
  • Did I add a dependency for something small?

Small diffs ship faster because they create less doubt.

Run the boring commands

Before production, I want the boring checks:

bun run lint
bun run typecheck
bun run test:all

For frontend work, I also want a build. For UI-heavy changes, I want a browser smoke test. If I cannot run something, I say that clearly in the final note.

Smoke test the user path

Tests are not a replacement for using the product once.

For a blog site, I would check:

  • Home page renders
  • Blog listing renders
  • Article route works
  • My Reads route works
  • Search index returns content
  • RSS feed builds
  • Mobile layout does not break

For a payments app, the smoke test is much stricter. The checklist should match the risk.

Write honest release notes

I do not like vague release notes.

Bad:

Improved app.

Better:

Added unsubscribe endpoint, validation errors, and idempotent unsubscribe behavior.
Verified with integration tests and lint/typecheck.

The second version helps the next person understand what actually changed.

Final thought

Shipping well is not about moving slowly. It is about removing avoidable risk.

A clear task, focused branch, small diff, tests, build, smoke test, and honest notes are enough for most work. The process is simple because simple processes are easier to repeat.