One Claude Code Tip a Day: Write the File Contract Before Editing

Before Claude Code edits a busy file, make it state the file contract: responsibilities, callers, invariants, side effects, and proof commands. Then review the patch against that contract.

Editorial illustration of an AI coding assistant mapping a file contract, invariants, tests, and git diff before editing code.

This post is part of the “One Claude Code Tip a Day” series — a daily guide to using Claude Code more effectively.

The failure: Claude edits the right file for the wrong contract

A common Claude Code failure is not choosing the wrong file. It is changing a real file while misunderstanding what that file promises to the rest of the system. You ask for a small fix in `pricing.ts`, Claude finds the conditional that looks relevant, patches it, and the focused test passes. Then staging breaks because another checkout path expected the old rounding behavior, or an analytics event disappears because the helper also carried side effects.

Today's habit is to make Claude write the file contract before editing. A file contract is a short, evidence-based description of what the file owns, who calls it, what invariants must not change, which side effects matter, and what commands can prove the contract still holds. It is not documentation theatre. It is a guardrail for high-traffic files where a correct-looking patch can violate an invisible promise.

Start with a read-only contract prompt

Use this when the file is central, old, or suspiciously convenient. For example, imagine a SaaS billing app where a coupon display bug appears in the React checkout page. The obvious target is `src/billing/applyDiscount.ts`, but that helper is also used by subscription previews, receipts, and admin refunds. Before asking Claude to patch it, run a read-only pass:

Prompt: `Read src/billing/applyDiscount.ts, its direct callers, and the nearest tests. Do not edit. Write a file contract with five bullets: responsibility, inputs and outputs, invariants, side effects, and proof commands. Quote the file paths that support each bullet. End with the smallest safe edit strategy for the coupon display bug.`

This forces Claude Code to do useful inspection before it spends your diff budget. A strong answer names callers such as `CheckoutSummary.tsx`, `receiptBuilder.ts`, and `adminRefundPreview.ts`; it says amounts remain integer cents; it notices that display labels are separate from charged totals; and it proposes changing presentation data without touching calculation semantics.

Turn the contract into edit boundaries

The next prompt should use the contract as a constraint, not as a preface you immediately forget. Good sessions sound almost bureaucratic here:

Prompt: `Using the contract above, make the smallest patch for the coupon label bug. Do not change money calculation, rounding, receipt serialization, or analytics payloads. If you need to touch a contract boundary, stop and explain why before editing.`

That line prevents a classic AI-coding drift: Claude notices nearby cleanup opportunities and starts improving names, moving helpers, or normalizing data structures. Those changes may be defensible in isolation, but they are not part of this bug. The contract gives you a shared reason to reject them without arguing from vibes.

Inspect the diff against the contract

After the patch, do not merely ask whether tests pass. Ask Claude to audit its own diff against the contract:

Prompt: `Review the git diff against the file contract. For each changed hunk, state which contract bullet it touches, whether the change is allowed, and which proof command covers it. If a hunk is not covered, recommend reverting or adding proof before continuing.`

In a healthy session, Claude might report that one hunk only changes the label passed into `CheckoutSummary`, a second hunk adds a regression test for coupon display, and no hunk changes integer-cent calculations. In a bad session, it might reveal that it renamed a return field used by receipts. That is the moment to stop before the accidental refactor becomes tomorrow's production bug.

Use a second pass when the contract was incomplete

File contracts are useful precisely because they can be wrong on the first pass. Suppose the focused checkout test passes, but a receipt snapshot fails. Do not say, `fix the snapshot`. Use the failure to repair the contract:

Prompt: `The receipt snapshot failed after the patch. Update the file contract with the missed caller or invariant, explain why the first contract missed it, then propose the narrowest correction. Do not edit until the corrected contract separates display label behavior from receipt serialization.`

This is the TonBisa-style loop that makes the habit valuable: prompt, inspect, find the gap, correct, verify. The second contract may add that receipt serialization consumes the same discount label field. The better fix might be a new display-only field instead of changing the shared one. You did not just get a patch; you taught the session why the first patch was unsafe.

Where this habit pays off

Use file contracts for payment helpers, auth middleware, shared components, migration scripts, API clients, caching layers, and anything with many callers. It also helps frontend work. Before changing a shared `Button` component to fix one mobile drawer, make Claude name keyboard behavior, disabled states, focus styles, analytics hooks, and visual regression checks. Before changing deployment code, make it name environment variables, startup order, rollback path, and log lines that prove success.

Do not use this for every typo or isolated test fixture. If the file has one caller and a narrow assertion, the contract may be more ceremony than safety. The signal is risk: many callers, hidden side effects, financial or auth behavior, or a diff that looks too easy for the blast radius.

Rule of thumb

Before Claude Code edits a file that other parts of the system depend on, make it write the contract in plain English and cite the evidence. Then make every patch, test, and diff review point back to that contract. If Claude cannot state what must not change, it is not ready to edit the file. It is only ready to read.