A codebase you don't know is a risk, not a hurdle. The plan should burn down that risk in order: understand, run, trace, spike, then make the change small enough that "production-ready" is a property you can demonstrate, not a hope.
Here are the first five steps, with the evidence you collect at each.
Step 1 — Get it building and the baseline green (half day)
Read the README, the manifest(s), the CI config, and any architecture docs. Boot the app locally, run the full test suite, run the linter/typechecker.
Evidence: The exact commands that reproduce a clean build and a passing test suite from a fresh checkout (recorded output, not memory of it). A one-page map you wrote yourself: where the entry points are, the layers, the build/test toolchain, and the main risks to your feature (e.g. "this touches cron jobs and payments").
Step 2 — Trace the smallest end-to-end slice that resembles your feature (half day)
Don't start with your feature. Pick a neighboring one that already works and follow it from entry to persistence. Note the conventions along the way: how errors surface, how config is injected, how logging is done, how tests are written.
Evidence: An annotated trace — file → function → what it does — for that slice. A written list of "conventions I must follow" (not intuitions; things you observed in real code). A named shortlist of the 3–5 files you will actually touch. If you can't produce this trace, you're not ready to write code.
Step 3 — Spike the core path (day 2)
Build the ugly version: hardcoded values, no error handling, no tests. The purpose is to confirm the path you traced in step 2 is real and to surface what you didn't know you didn't know.
Evidence: A working spike demonstrating the feature's central data path, alongside a list of every assumption the spike broke and what you corrected. That correction list is the most valuable document in this whole plan.
Step 4 — Write the contract before the code (half of day 2 / day 3)
Once the spike proves the path, pin down what production needs: the inputs/outputs, the error cases, where it sits in the conventions from step 2. Then write the tests — they'll be red, but they're the specification.
Evidence: A one-to-two-page design doc, an agreed interface/API shape (with the team if there is one), and a red test suite that encodes intended behavior. If you can't write the contract without consulting the code, you haven't finished step 2.
Step 5 — Implement in small, verified increments (days 3–4)
Replace the spike with the real thing in small commits, each one keeping the suite green, leaning on the existing patterns. Run lint/tests/typecheck per commit — CI, not just locally — and exercise the actual path against a real instance (staging or a local environment that isn't stubbed).
Evidence: A branch with progressive commits, each green in CI; coverage on the new code; something that proves it works against reality (a test result, a log trace, a screenshot); and a review by at least one person who knows the codebase. The review counts as evidence — an unfamiliar codebase has tribal knowledge you cannot extract from the files alone.
Steps 6+ would be the things that actually make it "shipped": a migration plan and its rollback, feature flagging, observability, the release and post-release verification. But the first five get you to a reviewed, green, working slice in staging — which is the point at which you can say "this will work in production" with evidence behind it, instead of a guess.