Add Parameter: One More Detail on the Tiffin Order Slip
Add Parameter explained simply — how to give a method a new piece of information it now needs, why an explicit parameter beats hidden global state, how to do it safely with overloads, and when to stop before the parameter list grows too long.
🥘 One More Detail on the Tiffin Order Slip
Asha didi runs a tiffin service in our colony — forty dabbas a day, cooked in one busy kitchen, delivered by cycle. Every morning her helper Raju goes door to door with a small pad of order slips. Each slip has four columns: name, number of rotis, sabzi of the day, delivery address. Raju fills the slips, the kitchen cooks from the slips, the cycles deliver. The system has worked beautifully for two years.
Then last month, new customers joined from the PG hostel near the engineering college. On day one, two of them — Priya and her roommate — returned their tiffins half-eaten. The complaint? "Bhaiya, too spicy! We are from the hills, we cannot eat this." Meanwhile, the very same evening, Verma uncle from B-block called to complain that the same dal was "completely bland, beta, like hospital food."
Asha didi stood in her kitchen, genuinely confused, holding two complaints about the same dal. Then she realised the real problem, and it was not the cooking. Her kitchen was producing one standard spice level for everyone, because the order slip had no place to write the customer's preference. The kitchen is not psychic. It can only cook from what the slip says. Priya knew what she wanted. Verma uncle knew what he wanted. Raju even heard them say it! But the slip had no column for it, so the information died at the doorstep and never reached the stove.
The fix was one small change. Asha didi took the slip pad and added a new column: "Spicy / Mild". Now Raju asks each customer one extra question, writes the answer down, and the kitchen reads the slip and cooks accordingly. Same kitchen, same recipes, same cook — but now the slip carries the one extra detail the kitchen needs. Complaints stopped within a week, and Priya started recommending the tiffin to her whole hostel floor.
A method in code is the kitchen. Its parameter list is the order slip. When the method needs a piece of information to do its job properly — and the caller is the one who knows that information — the honest solution is to add a field to the slip: a new parameter. That is the whole refactoring called Add Parameter.
What is Add Parameter? 🧐
Add Parameter is a refactoring from Martin Fowler's Refactoring book:
Give a method a new parameter so it can receive information it previously lacked.
The situation is always the same shape. A method must change its behaviour based on some piece of data — but the data lives with the caller, not inside the method. Maybe the method currently hard-codes a value ("en-US" for every customer), or sneaks the value from a global variable, or simply cannot do the new job at all. The cure: open the door honestly. Add a parameter, and let each caller pass the value from its own context.
It sounds almost too simple to be a "refactoring", but the discipline matters, because there are several tempting wrong ways to give a method extra data:
- Read a global or static variable. Hidden dependency. The signature lies — it claims the method needs only its listed parameters, but secretly it also reads world state. Tests become painful (you must set the global first), and two callers can fight over the same global. In tiffin terms: the kitchen guessing spice level from gossip instead of reading the slip.
- Hard-code the value. Works until the second use case appears — like Asha didi's one-spice-fits-all kitchen.
- Stuff the value into a field just before calling. This creates "temporal coupling": the method only works if you remembered to set the field first, in the right order. A time bomb.
The parameter is the honest path. The need becomes visible in the signature, the compiler enforces that every caller supplies the value, and a unit test can pass any value directly without setting up the whole world first.
A naming note from the canon: in the 2nd edition of Refactoring, Fowler folded Add Parameter (together with Remove Parameter and Rename Method) into one umbrella refactoring named Change Function Declaration — the catalog explicitly lists "Add Parameter" as one of its aliases. The insight: a method's name and its parameter list are one single "public face", and you change any part of that face using the same careful mechanics. So on refactoring.com you will find Add Parameter living inside Change Function Declaration; older sources (and refactoring.guru) still teach it under its own name. Both names point to the same move.
One important framing: Add Parameter and Remove Parameter are exact inverses — a seesaw. You add when the method genuinely needs more context; you remove when a parameter has become dead weight. Healthy code moves both ways over its lifetime: this month the slip gains a spice column; next year, if home delivery stops, the address column comes off.
The whole topic, sketched on one page:
College corner: Add Parameter is the everyday face of API evolution under backward compatibility. When a published function gains a new required input, every existing consumer is technically broken — unless the publisher provides a compatibility bridge: an overload, a default value, or a versioned endpoint. This is why mature HTTP APIs add new fields as optional (old clients keep working, new clients send more), and why protocol formats like Protocol Buffers were designed so new fields can be added without breaking old readers. The default-value trick you will learn below is the same idea in miniature: the new capability arrives, and nobody's existing code notices until they opt in.
When Do We Need It? 🚨
Reach for Add Parameter when you spot these situations:
- A hard-coded value must become flexible.
sendReceipt(order)always renders in English. Now French customers exist. The locale must come from outside — from whoever knows the customer. - The method secretly reads a global.
calculateTax()quietly readsglobalRegion. Make the hidden dependency explicit:calculateTax(region). Now the signature tells the truth and tests become one-liners. - New behaviour depends on caller-known data. You are extending a method, and the new logic branches on something only the caller knows — the spice preference, the user's role, the requested currency.
- Testing is awkward. If you cannot test a method without first mutating static state or environment variables, the missing parameter is usually the cause. Passing the value in makes the method pure-er and the test trivial.
- Two near-duplicate methods differ only by one value.
applyTenPercentDiscount()andapplyFifteenPercentDiscount()collapse intoapplyDiscount(rate). (When the duplication is the main point, Fowler calls this Parameterize Method / Parameterize Function — Add Parameter is the tool inside it.)
And the counter-signals — moments when adding a parameter is the wrong move:
- The method can compute the value itself from data it already receives. Then use Replace Parameter with Method Call instead; do not make callers do work the method can do.
- You are adding a boolean flag like
send(order, true)that flips behaviour. A flag argument is a smell — readers cannot guess whattruemeans. Split into two named methods (sendNow,queueForLater) instead. - This would be parameter number five. Stop. Repeated additions create the Long Parameter List smell. Bundle related values with Introduce Parameter Object, or pass the whole object with Preserve Whole Object.
Look at the week of complaints that finally pushed Asha didi to change the slip. The missing column was not annoying some customers — it was missing the mark for most of them, in both directions at once:
That "happy by accident" slice is the scariest one in code: callers for whom the hard-coded value happens to be right today. Nothing protects them tomorrow.
And here is the judgement call every developer faces when a signature must grow — add one parameter, or step back and bundle?
The spice level sits comfortably in the bottom-left: one value, stable need, simple parameter. A grading scheme with five boundary numbers that change per campus per year belongs top-right: bundle it.
Before and After at a Glance 📋
Here is the spiceless kitchen in TypeScript:
// BEFORE — the order slip has no spice column
type Order = { customer: string; rotis: number; sabzi: string };
function cookTiffin(order: Order): Tiffin {
// spice level is hard-coded — every customer gets "medium"
return kitchen.prepare(order.sabzi, order.rotis, "medium");
}
// Callers cannot express what they know:
cookTiffin(pgStudentOrder); // wanted mild — gets medium
cookTiffin(uncleOrder); // wanted spicy — gets mediumAnd after adding the column to the slip:
// AFTER — the slip carries the new detail
type SpiceLevel = "mild" | "medium" | "spicy";
function cookTiffin(order: Order, spice: SpiceLevel): Tiffin {
return kitchen.prepare(order.sabzi, order.rotis, spice);
}
// Each caller passes what it knows:
cookTiffin(pgStudentOrder, "mild");
cookTiffin(uncleOrder, "spicy");The kitchen logic barely changed. What changed is the flow of information: knowledge that used to be trapped at the caller now travels to the method that needs it — openly, through the front door of the signature.
As a structure, the upgraded order slip looks like this:
Step-by-Step, the Safe Way 🪜
Changing a signature breaks every caller at once — unless you stage it. Here is the careful sequence, shown on a receipt-emailing service that must learn about languages.
Step 0 — Starting point:
function sendReceipt(order: Order): void {
const body = renderReceipt(order, "en-US"); // hard-coded locale
email.send(order.customerEmail, body);
}Step 1 — Choose the type and a role-stating name. Not s: string but locale: Locale. The name should say what the value means to this method, not just its type. Raju's column does not say "text field"; it says "Spicy / Mild".
Step 2 — Add the parameter with a temporary safety net. For a private method with three callers, just change the signature and let the compiler guide you. For a public or widely-used method, keep the old shape working while you migrate. In TypeScript a default value is the lightest net:
// INTERMEDIATE — both old and new callers work
function sendReceipt(order: Order, locale: Locale = "en-US"): void {
const body = renderReceipt(order, locale);
email.send(order.customerEmail, body);
}In languages with overloads (C#, Java), the equivalent net is an old-signature overload that forwards:
// old shape forwards to new shape with a sensible default
public void SendReceipt(Order order)
=> SendReceipt(order, CultureInfo.GetCultureInfo("en-US"));Step 3 — Use the parameter in the body. Replace the hard-coded value or global read with the parameter. This is the moment the method actually gains its new ability.
Step 4 — Compile and test. With the default in place, all existing callers still behave exactly as before (they implicitly get "en-US"). Behaviour unchanged — that is the definition of a safe intermediate state.
Step 5 — Migrate callers one by one. Visit each call site and pass the real value from its own context:
// checkout flow knows the customer's language preference:
sendReceipt(order, customer.preferredLocale);
// admin resend tool defaults to the original order locale:
sendReceipt(order, order.locale);Run the tests after each migration (or each small batch).
Step 6 — Remove the safety net (if you want). Once every caller passes the value explicitly, decide: keep the default (if "en-US" is a genuinely sensible default) or delete it so the compiler forces every future caller to think about locale. For overload-style nets, delete the forwarding overload when its callers reach zero.
During the bridge period, an un-migrated caller's request flows like this — old shape in, default filled, new shape served:
And from a distance, the codebase moves through three states — exactly mirroring the gradual rename's three states, because both are Change Function Declaration underneath:
Two warnings worth tattooing on your wrist. First: the default-value safety net hides mistakes — a caller that should pass the locale but forgets will silently get English forever, and no compiler will complain. If the parameter is truly required for correctness, remove the default at the end so forgetting becomes a compile error. Second: run the test suite after every step, especially after step 3 (using the parameter in the body). That step changes real data flow, and an accidental swap — passing order.locale where you meant customer.preferredLocale — is exactly the kind of bug that types will not catch but tests will.
College corner: the danger in that first warning has a formal name — silent default drift. In C#, optional-parameter defaults are baked into the caller's compiled code, not the method's. If library v2 changes the default from "en-US" to "auto", every consumer compiled against v1 keeps passing "en-US" until they recompile. This is why public library authors prefer explicit overloads over optional parameters: the default lives in one place (the forwarding overload), so changing it later changes behaviour for everyone consistently. Binary compatibility and source compatibility are different promises — a distinction that only bites at scale, which is exactly when it hurts most.
A Bigger Real-Life Example 🏫
A school's report-card generator was built when the school had one campus and one grading style. Now a second campus opened, and it uses a different grade boundary table. Watch how the missing information is threaded through honestly.
// BEFORE — one campus baked into the logic
class ReportCardService {
generate(studentId: string): ReportCard {
const marks = this.marksRepo.findByStudent(studentId);
const grades = marks.map((m) => ({
subject: m.subject,
// grade boundaries HARD-CODED for the old campus
grade: m.score >= 91 ? "A1" : m.score >= 81 ? "A2" : m.score >= 71 ? "B1" : "B2",
}));
return new ReportCard(studentId, grades);
}
}The new campus wants A1 from 90, not 91, and adds a C band. The lazy fixes are all traps: a global currentCampus variable (hidden state), an if (schoolName === ...) inside (the method now hard-codes two campuses), or copy-pasting the whole class (duplication forever).
The honest fix: the caller knows which campus the student belongs to. Put it on the order slip.
// AFTER — the grading scheme arrives as a parameter
interface GradingScheme {
gradeFor(score: number): string;
}
class ReportCardService {
generate(studentId: string, scheme: GradingScheme): ReportCard {
const marks = this.marksRepo.findByStudent(studentId);
const grades = marks.map((m) => ({
subject: m.subject,
grade: scheme.gradeFor(m.score),
}));
return new ReportCard(studentId, grades);
}
}
// Each campus's code passes its own scheme:
const cityCampusScheme: GradingScheme = {
gradeFor: (s) => (s >= 91 ? "A1" : s >= 81 ? "A2" : s >= 71 ? "B1" : "B2"),
};
const newCampusScheme: GradingScheme = {
gradeFor: (s) => (s >= 90 ? "A1" : s >= 80 ? "A2" : s >= 65 ? "B1" : s >= 50 ? "B2" : "C"),
};
reportService.generate(studentId, cityCampusScheme);
reportService.generate(studentId, newCampusScheme);Notice three wins:
- The service knows nothing about campuses. It just applies whatever scheme it is handed. A third campus tomorrow costs zero changes here.
- Tests became trivial. Pass a tiny fake scheme —
{ gradeFor: () => "A1" }— and test the report assembly logic in isolation. No globals, no setup. - The parameter is one object, not five numbers. We could have added
boundary1, boundary2, boundary3, ...— five new parameters marching toward a Long Parameter List. Bundling them as oneGradingSchemekeeps the slip short. This is Add Parameter and Introduce Parameter Object working as a team — exactly the top-right quadrant of Figure 4.
The Same Refactoring in C# 🎯
C# gives us the cleanest staged migration, thanks to overloads and [Obsolete]. Here is a courier-charge calculator that must learn about delivery speed.
// BEFORE — every parcel priced as "standard" delivery
public class CourierService
{
public decimal CalculateCharge(Parcel parcel)
{
var baseCharge = parcel.WeightKg * 40m;
return baseCharge; // speed? what speed? everyone waits 5 days
}
}Step 1 — add the richer signature, and keep the old one as a forwarding overload:
public enum DeliverySpeed { Standard, Express, SameDay }
public class CourierService
{
// NEW — the real method
public decimal CalculateCharge(Parcel parcel, DeliverySpeed speed)
{
var baseCharge = parcel.WeightKg * 40m;
return speed switch
{
DeliverySpeed.Standard => baseCharge,
DeliverySpeed.Express => baseCharge * 1.5m,
DeliverySpeed.SameDay => baseCharge * 2.5m,
_ => baseCharge
};
}
// OLD — forwards with the historical default; warns callers to migrate
[Obsolete("Pass a DeliverySpeed explicitly. Will be removed in v4.")]
public decimal CalculateCharge(Parcel parcel)
=> CalculateCharge(parcel, DeliverySpeed.Standard);
}Every existing caller compiles and behaves exactly as before — standard delivery, same price. New code calls the two-argument form. The [Obsolete] warning nudges old callers over, team by team. When build logs show zero warnings, delete the old overload.
// AFTER — final state, single honest signature
public class CourierService
{
public decimal CalculateCharge(Parcel parcel, DeliverySpeed speed)
{
var baseCharge = parcel.WeightKg * 40m;
return speed switch
{
DeliverySpeed.Standard => baseCharge,
DeliverySpeed.Express => baseCharge * 1.5m,
DeliverySpeed.SameDay => baseCharge * 2.5m,
_ => baseCharge
};
}
}C#-specific care points:
- Optional parameters vs overloads:
DeliverySpeed speed = DeliverySpeed.Standardlooks tidier than an overload, but the default is baked into callers at compile time — changing the default later does not affect already-compiled assemblies. For public libraries, overloads are safer (see the College corner above). - Interfaces: if
CalculateChargelives on an interface, the interface and all implementations must change together. The forwarding-overload trick still works: add the new member to the interface, give old implementations a default behaviour, migrate, then remove. - An enum beats a bool: notice we added
DeliverySpeed, notbool isExpress.CalculateCharge(parcel, true)would be unreadable at every call site;CalculateCharge(parcel, DeliverySpeed.Express)reads like the order slip it is.
A Quick Python Taste 🐍
Python's keyword arguments make the staged addition particularly graceful — and keyword-only parameters (everything after the *) prevent the classic positional-argument mix-up:
from enum import Enum
class Spice(Enum):
MILD = "mild"
MEDIUM = "medium"
SPICY = "spicy"
def cook_tiffin(order, *, spice: Spice = Spice.MEDIUM):
"""Old callers keep working; new callers must name the argument."""
return kitchen.prepare(order.sabzi, order.rotis, spice.value)
# old caller — unchanged, gets the historical default:
cook_tiffin(priya_order)
# new caller — must write the name, so the call documents itself:
cook_tiffin(priya_order, spice=Spice.MILD)
cook_tiffin(verma_order, spice=Spice.SPICY)The spice=Spice.MILD at the call site is the readable order slip: nobody ever has to guess what a bare third argument means.
Did the Slip Change Pay Off? 📈
Asha didi kept her complaint notebook, so we can plot it. The week before the new column, complaints peaked; the week the column appeared, they collapsed — and the few that remain are genuine cooking issues, not information issues:
The same plot appears in software teams as "wrong-output bug reports" around any method with a hard-coded assumption. The fix is rarely cleverer logic; it is usually one more honest column on the slip.
IDE Support 🛠️
You rarely need to edit every caller by hand. The "change a signature" operation is fully automated in the major IDEs:
| Tool | Shortcut | What it does |
|---|---|---|
| JetBrains IDEs (IntelliJ IDEA, Rider, WebStorm, PyCharm) | Ctrl+F6 (Cmd+F6 on macOS) — Change Signature | One dialog to add, remove, reorder, and rename parameters. For a new parameter you supply a default value, and the IDE rewrites every call site to pass it. |
| Visual Studio | Ctrl+R, Ctrl+V — Change Signature (or the Quick Actions bulb) | Add/remove/reorder parameters with preview across the whole solution. |
| VS Code | No single built-in "add parameter" command — but F2 renames, and language extensions (C#, Java, TypeScript) offer "Change Signature" code actions; otherwise change the declaration and let the compiler errors list every caller as your to-do list. |
That last technique deserves a name: compiler-driven refactoring. In a statically-typed language, simply add the parameter and build. Every red error is a caller that needs the new value — a precise, complete checklist generated for free. Work through it, run tests, done. (This is one of the quiet superpowers of typed languages; in Python or plain JavaScript you must rely on search and tests instead.)
Benefits and Risks ⚖️
| Benefits | Risks / Costs |
|---|---|
| The dependency becomes explicit — the signature honestly lists everything the method needs. | Each addition lengthens the signature; repeated additions breed a Long Parameter List. Budget: by the time you reach 4+, bundle with Introduce Parameter Object. |
| Method becomes easier to test — pass values directly, no global setup. | Changing a public signature breaks external callers; needs the overload/default migration dance. |
| One method serves many cases instead of being duplicated per case. | A careless default value can silently hide callers that should have passed a real value. |
| Removes hidden reads of globals/statics — fewer "spooky" behaviour changes. | Adding a boolean flag parameter trades one smell for another; prefer two named methods or an enum. |
| Exact inverse of Remove Parameter — together they keep signatures matching reality in both directions. | If the method can derive the value itself, the new parameter is pure noise — use Replace Parameter with Method Call. |
Which Smells Does It Cure? 🧹
| Smell | How Add Parameter helps |
|---|---|
| Hidden / global dependencies (a flavour of Insider Trading and global-state coupling) | The secret read of a global becomes a visible, testable parameter. |
| Duplicated Code | Two methods that differ only by one embedded value merge into one parameterized method. |
| Divergent Change (hard-coded values forcing edits for every new case) | New cases are handled by passing different arguments, not by editing the method. |
| Shotgun Surgery (in reverse — beware!) | Done carelessly on a public API, adding parameters causes edits everywhere; the overload/default net prevents this. |
Quick Revision Box 📦
+----------------------------------------------------------------+
| ADD PARAMETER — CHEAT SHEET |
+----------------------------------------------------------------+
| Story : Tiffin slip gets a new column: "Spicy / Mild" |
| Goal : Method receives caller-known info through signature |
| 2nd ed. : Part of "Change Function Declaration" (Fowler) |
| Inverse : Remove Parameter (the same seesaw, other direction) |
| |
| SAFE STEPS: |
| 1. Pick type + role-stating name (locale, not s) |
| 2. Add param with default / forwarding overload |
| 3. Use it in the body (replace hard-code / global) |
| 4. Test — old callers must behave identically |
| 5. Migrate callers one by one, testing each |
| 6. Remove the default/overload if value is required |
| |
| DON'T when: method can compute it itself | it's a bool flag | |
| it would be the 4th-5th param (bundle instead) |
| IDE : JetBrains Ctrl+F6 | VS Ctrl+R,Ctrl+V | compiler-led |
+----------------------------------------------------------------+Practice Exercise ✍️
A neighbourhood pharmacy app has this billing function:
class PharmacyBilling {
// Today: every customer pays full price, bill prints in English,
// and home delivery is assumed to be FREE for everyone.
createBill(items: MedicineItem[]): Bill {
const total = items.reduce((sum, i) => sum + i.price * i.qty, 0);
const labelText = "TOTAL AMOUNT"; // English only
const deliveryCharge = 0; // free for all — owner is losing money!
return new Bill(items, total + deliveryCharge, labelText);
}
}The owner now wants: (a) senior citizens get a 10% discount, (b) bills in Hindi or English per customer choice, (c) delivery charge of ₹30 beyond 3 km, free within.
Tasks:
- Decide which new parameters
createBillneeds. For each, write the type and a role-stating name. (Careful: isisSenior: booleanthe best shape, or would adiscountRate: number— or a smallCustomerProfileobject — serve better as the app grows? Plot each candidate on Figure 4's quadrant before deciding.) - Perform the refactoring in stages: show the intermediate version where new parameters have defaults so existing callers keep working, then the final version after all callers migrate. Mark which state of Figure 8 each version represents.
- One teammate suggests: "Just read
window.currentCustomerinside the function — no signature change needed!" Write two sentences explaining to them why the parameter is safer, using the words hidden dependency and testability. - You have now added three parameters. The owner mentions two more upcoming features (loyalty points, GST category). At what point do you stop adding and reach for Introduce Parameter Object? Sketch what that bundled object would look like.
- Write one unit test for the senior-citizen discount that would have been impossible to write cleanly with the global-variable approach.
If your final createBill signature reads like Asha didi's improved order slip — every column meaningful, nothing hidden in the kitchen, and Raju never has to guess — you have understood Add Parameter completely.
Frequently asked questions
- Why add a parameter instead of just reading a global variable inside the method?
- Because a parameter is visible and a global is hidden. With a parameter, the method's signature honestly declares everything it needs, callers stay in control, and tests can pass any value directly. With a global, the dependency is invisible from the call site, the method silently changes behaviour when someone else changes the global, and tests must set up world state first. Explicit beats hidden.
- Will adding a parameter break all my callers?
- If you change the signature directly, yes — and for private methods that is fine, because the compiler immediately lists every caller for you to fix. For public or widely-used methods, use the safe path: add an overload (or a default value) that carries the new parameter, have the old signature forward to it with a sensible default, migrate callers gradually, then remove the old form.
- When should I NOT add a parameter?
- Three common cases. First, if the method can compute the value from data it already has, use Replace Parameter with Method Call instead. Second, if you are about to add a boolean flag that switches the method between two behaviours, split into two clearly-named methods instead. Third, if this would be the fourth or fifth parameter, stop and bundle related ones with Introduce Parameter Object or pass the whole object with Preserve Whole Object.
- Is Add Parameter really the inverse of Remove Parameter?
- Yes, exactly. Add Parameter gives the method new context it genuinely needs; Remove Parameter deletes context that has become dead weight. Fowler's 2nd edition merges both (plus Rename Method) into one refactoring called Change Function Declaration, because all three are just edits to the method's public signature done with the same safe mechanics.
- Should the new parameter be a primitive or an object?
- Prefer the smallest thing that states the role clearly. A well-named primitive (spiceLevel: SpiceLevel) is fine for one value. But if you find yourself passing two or three values that always travel together — street, city, pincode — they are a data clump, and you should pass one Address object instead. Fowler also advises thinking about future needs: passing a slightly larger object can save repeated signature changes later.
Further reading
Related Lessons
Remove Parameter: Delete the 'Telegram Address' Field from the School Form
Remove Parameter explained simply — how to safely delete a parameter the method no longer uses, why dead parameters mislead readers and burden every caller, and the checks (interfaces, overrides, reflection) you must do before deleting.
Rename Method: Fix the Shop Board So It Tells the Truth
Rename Method explained simply — why a method's name must say what the method really does, how to rename safely with a delegating old method, and how IDEs like VS Code (F2) and JetBrains (Shift+F6) make it a one-key job.
Introduce Parameter Object: Hand Over One Address Card, Not Five Answers
Introduce Parameter Object explained simply — why the same group of parameters travelling together through many methods is a hidden concept, and how bundling them into one named object shortens signatures, stops order mistakes, and attracts behaviour.
Preserve Whole Object: Show the Whole ID Card
Learn the Preserve Whole Object refactoring with a school ID card story, TypeScript and C# examples, safe step-by-step mechanics, and an honest look at the coupling cost of passing whole objects.