The discount that charged too much
Riya is adding a festival discount to the college bookshop's billing program. She writes discount(price, percent) and tests it: 10% off Rs 500 gives Rs 450. Then her friend calls it as discount(10, 500), and a Rs 500 book now costs minus Rs 40.
Sir, Java is so strict about types. Why didn't it stop him from swapping the numbers?
Because both are numbers, and 10 and 500 both fit. Java matches arguments to parameters by position and checks only that the types fit. It doesn't know which number you meant as the price.
So I should check bad inputs inside the method too?
Yes. Put a guard clause at the top: if the percent is outside 0 to 100, return early. Today you'll learn exactly how values go in and come out of a method, and what javac checks on both sides.
What are parameters and return values?
A parameter (formal parameter) is a variable declared in a method's header that receives a value when the method is called. An argument (actual parameter) is the value supplied in the call. Arguments are evaluated left to right and assigned to parameters by position; their number must match and each must be assignable to the parameter's type. A method's return type declares the type of the single value it sends back with a return statement, or void if it returns nothing.
Parameters are the empty slots in an order form. Arguments are what you write in the slots when you place an order. The return value is the parcel that comes back. The slots are filled in order, so writing the quantity in the price box gives a wrong order.
Words you'll meet in this lesson
price in discount(double price, …).500 in discount(500, 10).The rules javac checks
| Count | Same number of arguments as parameters (for now; varargs in Lesson 5.6 relax this) |
|---|---|
| Order and type | Matched by position. Each argument must fit its parameter's type: int → double is fine (widening), double → int is not |
| Names | Don't matter. The caller's variable names have nothing to do with the parameter names |
| Return type | Every return must give a value that fits the declared type |
| Every path | A non-void method must return on every possible path, or it's a compile error |
| One value | Only one value comes back; to return several, return an array or an object |
Exam tip: know the three compile errors by their exact words: missing return statement (some path doesn't return), unreachable statement (code after a return), and incompatible types (wrong type returned or passed, e.g. possible lossy conversion from double to int).
What can go in, what can come out
Many parameters
emi(double p, double rate, int months)
Matched by position, each with its own type.
One returned value
static boolean isAdult(int age)
Any type: int, double, boolean, String…
Several values in an array
static int[] minMax(int[] a)
Pack the answers into one array and return its reference.
Guard clauses: handle the bad cases first
public class Main {
static double discount(double price, int percent) {
if (percent <= 0) {
return price; // guard: nothing to discount
}
return price - price * percent / 100;
}
public static void main(String[] args) {
double a = discount(500, 10);
double b = discount(800, 0);
System.out.println(a + " " + b);
}
}The three compile errors around return
| Code | javac says | Fix |
|---|---|---|
if (m >= 75) return "A"; else if (m >= 40) return "B"; and nothing after | missing return statement | Add a final return for the remaining case |
return n * 2;System.out.println("done"); | unreachable statement | Move the print before the return, or delete it |
static int avg(int a, int b) { return (a + b) / 2.0; } | incompatible types: possible lossy conversion from double to int | Return type double, or cast deliberately |
void check(…) { … return 1; } | incompatible types: unexpected return value | A void method can only use return; |
int x = printLine(); (a void method) | incompatible types: void cannot be converted to int | A void call has no value to store |
Type-safe is not mistake-proof: if two parameters have compatible types, like (double price, int percent), swapped arguments still compile. Use clear parameter names, keep parameter lists short, and validate inputs with guard clauses.
From call to returned value
The caller evaluates the arguments
Each argument expression is calculated first, left to right:double a = discount(400 + 100, 10);400 + 100becomes 500. Only the resulting values are passed.Values are copied into the parameters, by position
A new frame is pushed. The 1st value goes to the 1st parameter, the 2nd to the 2nd. The int 500 is widened to 500.0 to fitstatic double discount(double price, int percent)double price. javac already checked at compile time that every argument fits.Guard clauses reject bad input
Aif (percent <= 0) { return price; }returnends the method immediately, wherever it appears. The lines below it don't run for this call.The main path computes and returns
The expression is evaluated, converted to the return type if needed (only widening is automatic), and handed to the caller. The method's frame is popped.return price - price * percent / 100;The caller uses the value
In the caller, the call expression is replaced by the returned value. It can be stored, printed, or passed straight into another call. If you ignore it, it is simply thrown away.double a = discount(500, 10); // a = 450.0
Values in, value out: both views
Left: the line running and its effect. Right: arguments landing in parameter slots, and the returned value going back to main.
discount 6 steps- Call:
discount(500, 10). The arguments are worked out first;mainwaits for an answer. - Parameters filled: first argument → first parameter, second → second. The int 500 becomes 500.0.
- Guard, then formula: the percent is positive, so the guard doesn't fire, and the method computes 450.0.
- Returned:
agets 450.0. The method's work is finished. - Early return: with a percent of 0 the guard fires and returns the price unchanged. The formula line never runs.
- Output: the real output of the program is
450.0 800.0.
- The call has two argument values. If an argument were an expression like
400 + 100, it would be calculated first. - A new frame is pushed. Values are copied into parameter slots strictly by position; the int 500 is widened for the
doubleslot. - Inside the method,
priceandpercentbehave like normal local variables of this frame. returnsends exactly one value back and the frame is popped.mainstores it ina.- The second call gets its own fresh frame. The guard clause returns the price at once.
- Both values now live in
main's frame, and the method's frames are long gone.
Parameters and returns in real systems
In real code, a method's header is a contract: what it needs, and what it promises to give back. Teams read headers far more often than bodies.
Watch it with an example: an EMI calculator
emi(60000, 12): the arguments are copied into the parameters, the method works with its own copies, and one value travels back with return.
- The call:
emi(60000, 12)passes two arguments. Their order matters: the first goes to the first parameter. - Copying: a new frame is created. Parameters
principalandmonthsreceive copies of 60000 and 12. - Guard clause: the method first rejects bad input (months must be positive), then computes 60000 / 12 = 5000.
- return: sends exactly one value back to the place it was called from. The method ends immediately.
- Result: the frame is popped and
monthly= 5000. EMI, tax and insurance-premium calculators are all methods like this. (Real EMI formulas include interest; this is a no-cost EMI.)
1. Guard clauses in payment and banking code
Transfer and withdrawal methods start with checks: amount positive, account active, balance sufficient, daily limit not crossed. Each failed check returns or rejects immediately, so the money-moving line runs only when everything is valid.
2. Calculators: EMI, tax, premium
Loan EMI, income tax and insurance premium calculators are methods with several parameters and one returned number. The same method serves the website, the mobile app and the bank officer's screen.
3. Returning several results together
A report method may need to give back a count, a total and a maximum. Beginners return an array; production code returns an object or a record with named fields (Modules 6 and 14), so callers can't mix up the positions.
4. The compiler as a safety net
Because parameter and return types are checked at compile time, a whole class of bugs never reaches production: passing text where a number is needed, or forgetting to return a value on some path.
Examples are simplified. Real financial code uses BigDecimal for money and named result objects instead of arrays.
A weekly sales summary
public class Main {
static int[] minMax(int[] values) {
int min = values[0];
int max = values[0];
for (int v : values) {
if (v < min) min = v;
if (v > max) max = v;
}
return new int[]{min, max};
}
static double average(int[] values) {
if (values.length == 0) {
return 0;
}
int sum = 0;
for (int v : values) {
sum += v;
}
return (double) sum / values.length;
}
public static void main(String[] args) {
int[] sales = {4200, 3900, 5100, 4700, 3600};
int[] range = minMax(sales);
System.out.println("Lowest day: Rs " + range[0]);
System.out.println("Best day: Rs " + range[1]);
System.out.println("Average: Rs " + average(sales));
System.out.println("No sales yet: Rs " + average(new int[0]));
}
}static int[] minMax(int[] values)Takes an array, returns a new two-element array: index 0 = min, index 1 = max.if (values.length == 0) return 0;A guard clause: avoids dividing by zero. The int 0 widens to 0.0 for the double return type.(double) sum / values.lengthCast first so the average keeps its decimals (Lesson 2.4).average(new int[0])An argument can be any expression of the right type, even a brand-new empty array.
Splitting a bill: two values in one returned array
Watch the arguments land in split's frame by position, the guard clause pass, and an array come back to main.
Worked examples: real problems, solved and traced in memory
Three problems where what goes into a method and what comes out of it matter. Run each solution, then step through it to see arguments copied into parameters, early returns, and returned values (even arrays) arriving back in main.
Withdraw with guard clauses
Problem statement
Write withdraw(balance, amount) that returns the new balance. If the amount is not positive, or more than the balance, print a message and return the balance unchanged. Try a valid and an invalid withdrawal from Rs 5,000.
balance = 5000 withdraw 2000, then withdraw 4000
Insufficient funds Balance: Rs 3000
Approach
- Check the bad cases first (guard clauses), each with its own early
return. - If both checks pass, the last line does the real work:
return balance - amount. - main stores the returned value back into its own
balance; the method can't change main's variable directly.
Solution: run it live
What happens in memory, step by step
Recorded from the real JVM: press Next to execute one line at a time. Watch method calls push and pop frames on the stack, and objects (numbered #1, #2 …) appear on the heap.
Returning three numbers in one array
Problem statement
A cart page shows the number of items, the total amount and the costliest item. Write cartStats(int[] prices) that returns all three in one int[].
prices = {499, 1299, 250}Items: 3 Total: Rs 2048 Costliest: Rs 1299
Approach
- A method has only one return value, so pack the three answers into an array:
{count, total, max}. - Loop once over the prices, updating total and max.
- Return a new array; main reads the values by index.
Solution: run it live
What happens in memory, step by step
Recorded from the real JVM: press Next to execute one line at a time. Watch method calls push and pop frames on the stack, and objects (numbered #1, #2 …) appear on the heap.
Maturity amount: parameters in the right order
Problem statement
A bank app shows the maturity amount of a fixed deposit with simple interest. Write interest(principal, ratePercent, years) and maturity(principal, ratePercent, years), which uses it. Show a 2-year deposit of Rs 40,000 at 7%.
principal = 40000, rate = 7%, years = 2
Maturity amount: Rs 45600.0
Approach
interesttakes three parameters; the caller must pass them in the same order.maturitypasses its own parameters straight on tointerestand adds the principal.- Watch the int argument 40000 arrive as 40000.0 in a double parameter (widening).
Solution: run it live
What happens in memory, step by step
Recorded from the real JVM: press Next to execute one line at a time. Watch method calls push and pop frames on the stack, and objects (numbered #1, #2 …) appear on the heap.
Check yourself
Quick revision
Lesson 5.2 in one screen
- Parameters are in the header; arguments are the values in the call.
- Arguments are evaluated left to right and matched by position; names don't matter.
- Count and types must fit: widening (int → double) is automatic, narrowing is a compile error.
- A non-void method must
returna value of its type on every path. returnends the method at once; guard clauses use this to reject bad input early.- Only one value comes back; return an array (or object) for several.
- Errors to recognise: missing return statement, unreachable statement, incompatible types.
Interview and viva questions
What is the difference between a parameter and an argument?
A parameter is the variable declared in the method header; it exists in the method's frame while the call runs. An argument is the actual value passed in a particular call. In square(5), 5 is the argument and n in square(int n) is the parameter.
Can a Java method return more than one value?
Not directly; a method has exactly one return value. To return several, return an array, a collection, or an object (a class or a record) whose fields hold the values. Objects are preferred because the fields have names.
What is a guard clause and why is it preferred over nested if-else?
A guard clause is an early check that handles an invalid or special case and returns immediately. It keeps the main logic at the left margin instead of buried in nested blocks, makes each rule easy to see, and ensures the main calculation only runs on valid input.
Why does javac report "missing return statement" when my if and else-if both return?
javac checks every possible path. If neither condition is true, execution reaches the end of the method without a return. Adding a final else or a last return covers that path. javac does not reason about which values are actually possible at run time.
Can a void method contain a return statement?
Yes, but only return; without a value, used to leave the method early. return 1; in a void method is a compile error: incompatible types: unexpected return value.
Practice problems
For each problem, first write only the method header: what goes in (parameters, in which order, of which types) and what comes out (the return type). Then fill in the body. The last three are real business tasks.
Simple interest
Write simpleInterest(double principal, double ratePercent, int years) that returns P × R × T / 100. Print the interest for two deposits.
(50000, 7.5, 3) and (12000, 6, 2)
Interest: Rs 11250.0 Interest: Rs 1440.0
Show solution and explanation
public class Main {
static double simpleInterest(double principal, double ratePercent, int years) {
return principal * ratePercent * years / 100;
}
public static void main(String[] args) {
System.out.println("Interest: Rs " + simpleInterest(50000, 7.5, 3));
System.out.println("Interest: Rs " + simpleInterest(12000, 6, 2));
}
}How it works: arguments are matched to parameters by position: the first goes to principal, the second to ratePercent, the third to years. The int arguments 50000 and 6 are widened to double automatically.
Power without Math.pow
Write power(int base, int exp) that returns baseexp as a long, using a loop.
(2, 10), (5, 3), (7, 0)
1024 125 1
Show solution and explanation
public class Main {
static long power(int base, int exp) {
long result = 1;
for (int i = 1; i <= exp; i++) {
result *= base;
}
return result;
}
public static void main(String[] args) {
System.out.println(power(2, 10));
System.out.println(power(5, 3));
System.out.println(power(7, 0));
}
}How it works: the result starts at 1 and is multiplied exp times. For exp = 0 the loop never runs and 1 is returned, which is correct. The return type is long because powers grow fast; an int would overflow sooner.
Is it a vowel?
Write isVowel(char ch) that returns true for a, e, i, o, u in either case. Test it on a few letters.
'a', 'E', 'k', 'U'
a -> true E -> true k -> false U -> true
Show solution and explanation
public class Main {
static boolean isVowel(char ch) {
char c = Character.toLowerCase(ch);
return c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u';
}
public static void main(String[] args) {
char[] letters = {'a', 'E', 'k', 'U'};
for (char ch : letters) {
System.out.println(ch + " -> " + isVowel(ch));
}
}
}How it works: converting to lowercase first halves the number of comparisons. The whole boolean expression is returned directly, so the method is one decision and one return.
Percentage with a guard
Write percentage(int obtained, int total) that returns the percentage as a double. If total is 0 or less, return 0 instead of dividing.
(432, 500), (0, 0)
86.4% 0.0%
Show solution and explanation
public class Main {
static double percentage(int obtained, int total) {
if (total <= 0) {
return 0;
}
return obtained * 100.0 / total;
}
public static void main(String[] args) {
System.out.println(percentage(432, 500) + "%");
System.out.println(percentage(0, 0) + "%");
}
}How it works: the first if is a guard clause: it handles the bad case up front and leaves early, so the main formula below never sees a zero total. return 0; is allowed in a double method because 0 widens to 0.0.
Clamp a value into a range
Write clamp(int value, int min, int max) that returns min if value is below it, max if above it, otherwise the value itself. Use it to keep a volume level between 0 and 100.
-20, 45, 130
0 45 100
Show solution and explanation
public class Main {
static int clamp(int value, int min, int max) {
if (value < min) {
return min;
}
if (value > max) {
return max;
}
return value;
}
public static void main(String[] args) {
System.out.println(clamp(-20, 0, 100));
System.out.println(clamp(45, 0, 100));
System.out.println(clamp(130, 0, 100));
}
}How it works: two guard clauses deal with the out-of-range cases; whatever reaches the last line is already valid. Note the parameter order (value, min, max): calling clamp(0, 100, 45) would compile but give a wrong answer, so order matters as much as type.
Return a reversed copy
Write reversed(int[] a) that returns a new array with the elements in reverse order, leaving the original unchanged.
a = {10, 20, 30, 40}Original: [10, 20, 30, 40] Reversed: [40, 30, 20, 10]
Show solution and explanation
import java.util.Arrays;
public class Main {
static int[] reversed(int[] a) {
int[] result = new int[a.length];
for (int i = 0; i < a.length; i++) {
result[i] = a[a.length - 1 - i];
}
return result;
}
public static void main(String[] args) {
int[] a = {10, 20, 30, 40};
int[] r = reversed(a);
System.out.println("Original: " + Arrays.toString(a));
System.out.println("Reversed: " + Arrays.toString(r));
}
}How it works: the method creates a new array on the heap and returns its reference. The local variable result disappears when the method returns, but the array lives on because r in main now refers to it.
Count vowels in a String
Write countVowels(String text) that returns how many vowels the text contains. Reuse the idea from the isVowel problem.
"Mumbai Indians", "Rhythm"
6 0
Show solution and explanation
public class Main {
static boolean isVowel(char ch) {
return "aeiouAEIOU".indexOf(ch) >= 0;
}
static int countVowels(String text) {
int count = 0;
for (int i = 0; i < text.length(); i++) {
if (isVowel(text.charAt(i))) {
count++;
}
}
return count;
}
public static void main(String[] args) {
System.out.println(countVowels("Mumbai Indians"));
System.out.println(countVowels("Rhythm"));
}
}How it works: countVowels calls isVowel once per character, passing a char argument to a char parameter. indexOf returns −1 when the character isn't in the list of vowels (Lesson 4.5).
Minimum and maximum in one call
A method can return only one value, but that value can be an array. Write minMax(int[] a) that returns {min, max}.
marks = {67, 92, 48, 75, 88}Lowest: 48, Highest: 92
Show solution and explanation
public class Main {
static int[] minMax(int[] a) {
int min = a[0];
int max = a[0];
for (int x : a) {
if (x < min) min = x;
if (x > max) max = x;
}
return new int[]{min, max};
}
public static void main(String[] args) {
int[] marks = {67, 92, 48, 75, 88};
int[] result = minMax(marks);
System.out.println("Lowest: " + result[0] + ", Highest: " + result[1]);
}
}How it works: the two answers are packed into a small array and returned together. The caller must know the agreed order (index 0 = min, index 1 = max). Later you'll use a class or a record (Lesson 14.5) to give the values names.
Greatest common divisor
Write gcd(int a, int b) using Euclid's method: replace (a, b) with (b, a % b) until b is 0. Use it to simplify a fraction.
fraction 84 / 126
gcd = 42 84/126 = 2/3
Show solution and explanation
public class Main {
static int gcd(int a, int b) {
while (b != 0) {
int r = a % b;
a = b;
b = r;
}
return a;
}
public static void main(String[] args) {
int num = 84, den = 126;
int g = gcd(num, den);
System.out.println("gcd = " + g);
System.out.println(num + "/" + den + " = " + (num / g) + "/" + (den / g));
}
}How it works: inside the method, a and b are changed freely; they are copies, so num and den in main keep their values. 84, 126 → 126, 84 → 84, 42 → 42, 0, so the gcd is 42.
Loan EMI calculator
Write emi(double principal, double annualRatePercent, int months) using EMI = P × r × (1 + r)n / ((1 + r)n − 1), where r is the monthly rate. Guard: if the rate is 0, the EMI is simply P / n. Print the EMI rounded to 2 decimals.
Rs 5,00,000 at 9% for 60 months Rs 60,000 at 0% for 12 months
EMI: Rs 10379.18 EMI: Rs 5000.00
Show solution and explanation
public class Main {
static double emi(double principal, double annualRatePercent, int months) {
if (annualRatePercent == 0) {
return principal / months;
}
double r = annualRatePercent / 12 / 100;
double factor = Math.pow(1 + r, months);
return principal * r * factor / (factor - 1);
}
public static void main(String[] args) {
System.out.printf("EMI: Rs %.2f%n", emi(500000, 9, 60));
System.out.printf("EMI: Rs %.2f%n", emi(60000, 0, 12));
}
}How it works:
- The guard clause handles the zero-interest offer separately; without it the formula would divide 0 by 0.
(1 + r)nis computed once and stored infactor, then used twice.- Three parameters of different meanings: passing months where the rate is expected would compile (int widens to double) but give nonsense, so read headers carefully.
GST breakup for an invoice line
Write gstBreakup(double taxable, double gstPercent) that returns an array {cgst, sgst, total}. For an intra-state sale, CGST and SGST are each half of the GST.
taxable = 2400, GST = 18%
CGST 9%: Rs 216.0 SGST 9%: Rs 216.0 Invoice total: Rs 2832.0
Show solution and explanation
public class Main {
static double[] gstBreakup(double taxable, double gstPercent) {
double gst = taxable * gstPercent / 100;
double cgst = gst / 2;
double sgst = gst / 2;
return new double[]{cgst, sgst, taxable + gst};
}
public static void main(String[] args) {
double[] b = gstBreakup(2400, 18);
System.out.println("CGST 9%: Rs " + b[0]);
System.out.println("SGST 9%: Rs " + b[1]);
System.out.println("Invoice total: Rs " + b[2]);
}
}How it works:
- The method returns three related numbers in one
double[]. - 18% of 2400 is 432, split equally into 216 + 216.
- Inter-state sales use IGST instead (the whole 18%); that would be a separate method or a third parameter.
Railway fare with concessions
Write fare(int baseFare, int age): children under 5 travel free, passengers aged 60 or more get 40% off, everyone else pays the base fare. Use guard clauses and print the fare for a family.
baseFare = 850, ages = {4, 34, 31, 63}Age 4: Rs 0 Age 34: Rs 850 Age 31: Rs 850 Age 63: Rs 510 Family total: Rs 2210
Show solution and explanation
public class Main {
static int fare(int baseFare, int age) {
if (age < 5) {
return 0;
}
if (age >= 60) {
return baseFare * 60 / 100;
}
return baseFare;
}
public static void main(String[] args) {
int[] ages = {4, 34, 31, 63};
int total = 0;
for (int age : ages) {
int f = fare(850, age);
System.out.println("Age " + age + ": Rs " + f);
total += f;
}
System.out.println("Family total: Rs " + total);
}
}How it works:
- Each special case returns immediately, so the code reads as a list of rules, without nested if-else.
- 40% off means paying 60%: 850 × 60 / 100 = 510. Multiplying before dividing keeps the int arithmetic exact here.
- The order of arguments matters: swapping them as
fare(34, 850)still compiles (both are int), but the method then treats 850 as the age and returns Rs 20.
Real-world scenario questions (asked in company interviews)
Most method bugs in real systems are about inputs that weren't checked and results that were misread. Decide your answer first, then open the model answer.
A product sold at a negative price
A method applyDiscount(double price, int percent) is called from a new campaign screen, which keeps both values as int, as applyDiscount(percent, price). The code compiles and a Rs 1,200 jacket is shown at a negative price. Why didn't Java catch this, and how do you prevent it?
The interviewer is checking: do you know that arguments are matched by position, and that type-checking can't catch every mix-up?
Show a strong answer
- Arguments are matched by position. The int percent widens to fit the double price parameter, and the int price fits the int percent parameter, so javac had nothing to complain about.
- Add guard clauses: reject a percent outside 0–100 and a price that is not positive, with a clear error.
- Make mix-ups harder: clear parameter names, fewer parameters, or a small object (e.g. a
Discounttype) instead of bare numbers. - Add a unit test with realistic values for the new screen.
Avoid: saying "the compiler should have caught it". Validation is the method's job.
Deeply nested transfer code
A transfer method has five levels of nested if-else checking the amount, account status, KYC, balance and daily limit, with the actual debit buried in the middle. A bug slipped in where a blocked account could still transfer. How would you restructure it?
The interviewer is checking: can you use guard clauses and early returns to make validation obvious?
Show a strong answer
- Rewrite each check as a guard clause at the top: if the condition fails, return a clear failure result (or reject) immediately.
- After all guards, the debit and credit run at the left margin, only on the "everything is valid" path.
- Each rule becomes one readable block that can be tested on its own, including the blocked-account case that was missed.
- Keep the order intentional: cheap checks (amount > 0) first, costly ones (database lookups) later.
Avoid: adding a sixth nested level to fix the bug. The structure is what hid it.
The dashboard that swapped min and max
A helper returns int[]{min, max}. A teammate assumed index 0 was the maximum, so the dashboard showed "Best day: Rs 3,600" for months. How would you make this kind of bug less likely?
The interviewer is checking: do you see the weakness of returning unnamed values in an array?
Show a strong answer
- Positions in an array carry no meaning; every caller must remember the order, and nothing checks it.
- Return a small object or record with named fields, e.g.
Range(int min, int max), so the caller writesrange.max(). - Or split into two clearly named methods,
min(values)andmax(values), if performance isn't an issue. - At minimum, document the order in the method comment and add a test.
Avoid: blaming the teammate. The method's design made the mistake easy.
"Just return 0 when something is wrong"
A method double shippingCharge(int pincode, double weightKg) returns 0 whenever the pincode is unknown or the weight is invalid. Finance later finds hundreds of orders shipped free by mistake. What would you change?
The interviewer is checking: do you understand that a return value must not hide errors?
Show a strong answer
- 0 is a valid-looking charge, so callers can't tell "free delivery" from "something went wrong". The error was silently turned into a business decision.
- For invalid input, fail clearly: throw an exception (Module 9) so the order can't be placed with a wrong charge.
- For a legitimately "unknown" result, use a value the caller is forced to check, such as
Optional(Lesson 14.4), rather than a magic number. - Log the bad pincodes so the rate card can be fixed.
Avoid: special return values like 0 or −1 for errors in money calculations.