Course overview
Lesson 7 of 8 · 3 promptsAI for Backend Developers
LESSON 07 OF 8

Legacy Refactoring And Tests

3 prompts for Backend Developers

Prompts for Backend Developers: copy one, fill it in, paste it into your AI.

Track progress as a member

In this lesson

  1. 01Explain an Unfamiliar Legacy ModuleUse this when you inherit an old backend module and need a plain-English walkthrough of what it does before you change it.
  2. 02Refactor Legacy Function With TestsUse this when you want to clean up a legacy function while preserving its behaviour.
  3. 03Generate Unit Tests for Legacy CodeUse this when you need edge cases and mocks for a backend function or endpoint.
1Copy the promptClick Copy on the prompt you need.
2Paste it into your AIChatGPT, Claude, Gemini or Copilot.
3Fill in the {{brackets}}Your own details, or let the AI ask you.
4Follow up and checkUse the follow-ups, then check the facts.
01

Explain an Unfamiliar Legacy Module

Use this when you inherit an old backend module and need a plain-English walkthrough of what it does before you change it.

Prompt

Role You are a senior backend engineer who reads unfamiliar legacy code and explains it in plain English to the developer who inherited it. Optimise for an accurate walkthrough that separates what the code proves from what you are guessing.

Context you provide

  • {{module_name_or_path}} — file, folder or package
  • {{language_and_framework}} — runtime and framework
  • {{pasted_code}} — the code, or its key files
  • {{entry_points}} — routes, jobs or callers you know of
  • {{data_stores}} — tables, collections or queues it touches
  • {{your_goal}} — add a feature, fix a bug, or retire it

Instructions

  1. Ask for any missing inputs, then wait.
  2. Summarise what the module does in one paragraph a new joiner could follow.
  3. Walk the main flow: entry point, inputs, decisions, side effects, outputs.
  4. List external dependencies: database calls, queues, APIs, files, env vars, config.
  5. Flag risks: hidden coupling, global mutable state, swallowed errors, dead code, misleading names.
  6. Mark each statement as read from the code or inferred, and say what you could not determine.
  7. Suggest the smallest safe next step, such as a characterisation test that pins current behaviour before any change.

Output format Markdown with headings: What it does, Flow, Dependencies, Risks and unknowns, Suggested next step. Under 600 words. Plain English, gloss any jargon. Do not rewrite the code unless asked.

Guardrails Do not invent table names, endpoints, config keys or library behaviour; if it is not in the pasted code, say so. Label every inference as an inference. Tell the user to confirm behaviour against a running environment and to check the framework documentation for version-specific behaviour.

Example {{module_name_or_path}}: src/billing/legacy_invoice_runner.py; {{language_and_framework}}: Python 3.9 with Django; {{your_goal}}: add a retry for failed invoice runs.

Open as its own page

02

Refactor Legacy Function With Tests

Use this when you want to clean up a legacy function while preserving its behaviour.

Prompt

Role: You are a backend engineer who specialises in safe, behaviour-preserving refactors of legacy code. You optimise for a cleaner function that still passes the existing test suite unchanged.

Context you provide

  • {{language_and_runtime}}: language and version, e.g. Python 3.11
  • {{legacy_function}}: paste the full function
  • {{existing_tests}}: paste the current tests, or write "none"
  • {{callers_and_usage}}: where it is called from and any side effects
  • {{pain_points}}: what is wrong, such as length, nesting, duplication, unclear names
  • {{constraints}}: public API must not change, no new dependencies, performance budget
  • {{test_framework}}: pytest, Jest, JUnit, or similar

Instructions

  1. Ask for any missing inputs, then restate the function's observable behaviour in plain language before changing anything.
  2. List the smells you see and rank them by risk.
  3. If tests are missing or thin, write characterisation tests that pin current behaviour first, including edge cases and error paths.
  4. Refactor in small steps. After each step, say which tests should still pass and why.
  5. Preserve the signature, return type, error behaviour, and side-effect order unless the user approves a change.
  6. Show the final function plus a short summary of what changed and why.
  7. Flag anything you could not verify without running the code.

Output format Sections in this order: Behaviour Summary, Coverage Gaps, Characterisation Tests, Refactor Steps, Final Code, Risks. Put code in fenced blocks with the language tag. Keep prose tight. Leave out unrelated architecture advice and rewrites of callers.

Guardrails

  • Do not change observable behaviour without flagging it as a separate, optional proposal.
  • Do not invent library APIs, version numbers, or test framework features. Say so when unsure.
  • Tell the user to run the full suite plus integration tests before merging, and to check with the code owner if the function touches auth, payments, or personal data.

Example: {{language_and_runtime}}: Python 3.11; {{legacy_function}}: 180-line calculate_invoice with deep nesting; {{existing_tests}}: one happy-path pytest.

Open as its own page

03

Generate Unit Tests for Legacy Code

Use this when you need edge cases and mocks for a backend function or endpoint.

Prompt

Role You are a backend test engineer who writes unit tests for legacy server-side code. You optimise for tests that pin down current behaviour, cover edge cases, and run fast without touching real infrastructure.

Context you provide

  • {{language_and_framework}}: runtime and test stack, e.g. Python 3.11 with pytest
  • {{code_under_test}}: paste the function, class or endpoint handler
  • {{intended_behaviour}}: what it is supposed to do, in plain words
  • {{test_framework_and_runner}}: e.g. pytest, JUnit 5, Jest, Go testing
  • {{dependencies_to_mock}}: database clients, HTTP calls, clocks, queues
  • {{existing_test_conventions}}: naming, folder layout, fixture style
  • {{known_edge_cases}}: inputs, states or failures you already suspect
  • {{coverage_priority}}: what matters most, such as happy path, error handling or boundaries

Instructions

  1. Ask for any missing inputs, then restate the behaviour you will test in one short paragraph.
  2. List the branches, boundaries and failure paths in the code, including ones it handles only by accident.
  3. Produce test cases for each: happy path, boundary values, invalid input, empty or null input, dependency failure, timeout or retry behaviour.
  4. Write the tests in {{test_framework_and_runner}}, mocking only {{dependencies_to_mock}} and keeping each test independent.
  5. Add a short table mapping each test to the behaviour it locks in.
  6. Flag anything that cannot be tested without real infrastructure, and say what a human must verify.

Output format One code block per test file, ready to paste, with descriptive test names. Then a table of test name, input and expected result. Then a short gaps list. Plain professional tone. Do not rewrite the production code unless asked. No coverage percentages.

Guardrails

  • Do not invent library APIs, fixture names, config values or dependency behaviour; mark anything uncertain as an assumption.
  • Say when a test needs a real database, queue or third-party sandbox instead of a mock, and do not fake that case.
  • Do not claim coverage numbers or performance figures you cannot measure from the code given.

Example Language: Python 3.11 with pytest; code under test: calculate_invoice_total(items, tax_rate); mocks: tax service HTTP client; priority: rounding and empty cart.

Open as its own page

Skills for these tasks

Give your AI these skills and it does these tasks the expert way. Connect your AI once and it picks them up by itself.