# 13.5 Functions: the signature is the contract (/building-ai-workers/programming-fundamentals/functions)

---
type: Document
title: "13.5 Functions: the signature is the contract"
description: "How to read a function's signature as its promise, test it with one assertion you can check by hand, and why every number comes from a tested function."
status: stable
order: 313.05
ksor:
  owner: team:panaversity
  audience: [ public ]
  approval:
    by: process:panaversity
    at: 2026-11-02T13:55:25Z
chapter: "13"
part: III
expert_status: builder-only
concepts: [ "13.5" ]
last_verified: 2026-10-30
sources:
  - id: nature-cornwell
    title: "In the AI era, coding means thinking like a systems architect (Nature, Career column, October 13, 2026) (Jacques Cornwell, accessed 2026-10-30)"
    resource: https://www.nature.com/articles/d41586-026-02715-x
generated:
  at: 2026-11-02T13:55:25Z
  by: process:panaversity
trust_tier: unverified
build_id: sha256:e1fbcb7d9a8f8b9b7cb96875022325c02c9ba4be94cef13b301cbd39a9edad85
dirty: true
ksor_version: 0.0.60
---

**In everyday life.** A vending machine has a sign: put in $2.00, press B4, get a bottle of water. You can check its promise from the outside. The sign tells you what goes in, what comes out, and what it promises. If you put in $2.00, press B4 and get orange juice, the machine broke its promise, and you know it without opening it. Code is the same: try it with an answer you already know.

**What you will learn.** How to read the first line of a named piece of code as a promise, test the promise with one line you can check by hand, and see what strict mode does when that first line has no labels.

**Why it matters.** A test shows a broken promise before you read the body, the code that does the work.

A **function** is a named piece of code that takes inputs and gives back a result. The inputs are its **parameters**. The values you pass in when you use it are its **arguments**. What it gives back is its **return value**. A function with no `return` line gives back nothing: `undefined` in TypeScript, `None` in Python. The first line of a function, with its name, its parameters and their types, and its return type, is its **signature**. The signature is the sign on the machine. It promises the kind of answer, not the right answer: a return type of `string` accepts any text. So only a test shows that the answer is right.

### What does a signature promise?

What you must give the function, and what you get back. Here is one from Brightline: `dueDate(invoiceDate: string, terms: string): string`.

| Part of the signature | What it promises | In `dueDate` |
| --- | --- | --- |
| Name | What the function is for | Works out a due date |
| Parameters with types | What you must give it | An invoice date as text, and the terms as text |
| Return type | What you get back | A due date as text, the last `: string` |
| What it does not say | Anything else must be in a test | That unknown terms are an error |

### Why one job each?

A function that does one job can be named in a few words and tested on its own. It is like a fuse box with one fuse per room: when a light goes out, you know which fuse, and the rest of the house stays lit. A function that reads a file, computes due dates and prints a report is really three functions in one. When an agent writes a long function, ask it to split it, and then test each piece. The writer of the *Nature* column explains why from his own experience. With small, single-purpose functions, "I can isolate, interrogate and fix the faulty function without jeopardizing the rest of the pipeline"[^nature-cornwell]. In other words, he can find and fix one bad function without breaking the rest.

**The easiest functions to test.** A function is **pure** when its result depends only on its inputs, and it changes nothing outside itself. Pure functions are easy to test. A pure function is like a multiplication table: 7 times 8 is 56 today, tomorrow, and on any computer. `dueDate("2026-10-07", "Net 30")` gives the same answer every time, on any computer, so a test can check it. A function that reads today's date from the computer's clock gives a different answer tomorrow. So Brightline's scripts take the cutoff date as an input, never from the clock.

### Why must the math be done by a function, not a reply?

A pure, tested function is a small **calculating engine**: code whose only job is to compute an answer from its inputs, the same way every time. It is like an adding machine that prints a tape. A model's reply is more like a colleague adding in their head: often right, with no tape to check. If you ask it in a chat to add up thirteen invoice amounts, it may give a total that looks right but is wrong. It may also give a different total the next time.

**The rule for every number.** AI can reason about complex problems, but **deterministic** engines should enforce accounting rules and mathematical consistency. A deterministic engine is one that gives the same output for the same input, every time, on every computer. So when you need a number, ask the agent to write the function and its tests, then run the function. Never accept a total, a date or a tax amount that a model worked out in its reply.

Being deterministic makes a result repeatable, but not right. A wrong rule gives the same wrong answer every time, like a spreadsheet formula with the wrong tax rate in it. Confidence comes from rules you agreed, expected results you checked by hand, tests and review.

### What is an assertion?

An **assertion** is a line that states what must be true, and stops the program with an error if it is not. It is the smallest possible test. You will decide many of them.

**At work.** Before Ana reads the body of a new function, she reads only its signature and says what it promises: "Given an invoice date and terms, it returns the due date." Then she picks one case she can check on a calendar, and the agent writes it as an assertion. An invoice on October 7 with Net 30 terms is due November 6. If the assertion passes, she reads the body. If it fails, she has found the bug without reading the body at all.

**In TypeScript.** The two `import` lines bring in ready-made code: `assert`, and `dueDate` from its file. Predict: which line prints, and what stops the program?

```ts
import assert from "node:assert/strict";
import { dueDate } from "./src/due-date.ts";

assert.equal(dueDate("2026-10-07", "Net 30"), "2026-11-06");
console.log("first assert passed");
assert.equal(dueDate("2026-10-07", "Net 30"), "2026-11-07");
console.log("second assert passed");
```

```text
first assert passed
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
+ actual - expected

+ '2026-11-06'
- '2026-11-07'
```

The second assertion expected the wrong day, and the program stopped with the actual and expected values side by side.

**In Python.** The signature is `def due_date(invoice_date: str, terms: str) -> str:`. The `-> str` is the return type.

```python
from brightline_ap.due_date import due_date

assert due_date("2026-10-07", "Net 30") == "2026-11-06"
print("first assert passed")
assert due_date("2026-10-07", "Net 30") == "2026-11-07", "Net 30 from Oct 7"
print("second assert passed")
```

```text
first assert passed
Traceback (most recent call last):
  ...
AssertionError: Net 30 from Oct 7
```

**Where the languages differ.** In TypeScript, `assert` comes from Node's built-in `node:assert/strict` **module**. A module is a file of ready-made code that a program imports, and [Chapter 14](../structuring-programs/modules.md) covers modules. `assert.equal` shows both values when it fails. In Python, `assert` is part of the language, and a plain `assert` shows only your message. pytest is the tool that runs Python tests. It makes a plain `assert` show both values, the way Node does.

### What if a function has no labels?

Then its first line promises nothing, and an agent can write a function like that. [Strict mode](the-workbench.md#why-strict-mode) treats it as an error. You turn it on with `strict` in `tsconfig.json`, the file of TypeScript settings, and with `typeCheckingMode = "strict"` for Pyright. This function adds up a list of amounts, with a loop like the ones in [13.4](decisions-and-repetition.md), and it has no type labels. It runs, and only strict mode reports a problem.

**In TypeScript.**

```ts
function totalDue(amounts) {
  let total = 0;
  for (const amount of amounts) total += amount;
  return total;
}

console.log(totalDue([482500, 61875]));
```

```text
$ node total-due.ts
544375
$ npx tsc --noEmit --strict --ignoreConfig total-due.ts
total-due.ts(1,19): error TS7006: Parameter 'amounts' implicitly has an 'any' type.
```

`npx tsc` runs the TypeScript type checker on one file, here in strict mode and without the project's settings. `any` means any kind of value at all, so the checker has nothing to check `amounts` against.

**In Python.**

```python
def total_due(amounts):
    total = 0
    for amount in amounts:
        total += amount
    return total

print(total_due([482500, 61875]))
```

```text
$ uv run python total_due.py
544375
$ uv run pyright
total_due.py:1:15 - error: Type annotation is missing for parameter "amounts" (reportMissingParameterType)
```

Pyright reports more than one error here, and the output shows the one that names the cause. The fix is the label, a list of numbers: `amounts: number[]` and `amounts: list[int]`. Without strict mode, Pyright reports no errors here.

**The operating rule:** read a function's signature first and say its promise in one sentence, then test the promise with an assertion you can check by hand, before you read the body.

![Figure 13.6. The signature of dueDate drawn as a box, with two string inputs, invoiceDate and terms, and a string result. "2026-10-07" and "Net 30" go in, and "2026-11-06" comes out. Below it are the expected result for that call and a rule the signature cannot show: unknown terms must raise an error.](img/function-contract.png)

*Figure 13.6. The signature is the contract. What goes in, what comes out, and the case you can check by hand.*

**Practice it.** [Practice: functions](both-ai-vendors.md#practice-functions), in your own tool, fifteen minutes.

[^nature-cornwell]: In the AI era, coding means thinking like a systems architect (Nature, Career column, October 13, 2026), Jacques Cornwell.
