Single Functions: Code Generation Guide
Generating a single function is the most common code generation task. A function is small enough to specify clearly, yet large enough to solve a meaningful problem. This article teaches you the function-level prompt pattern: signature, requirements, examples, and acceptance tests.
The Function-Level Prompt Pattern
A complete function prompt has five parts:
1. Language and Context
Language: [Python / JavaScript / Rust / etc.]
Purpose: [Brief description]
Existing imports or patterns: [If relevant]
2. Function Signature
def function_name(param1: Type, param2: Type) -> ReturnType:
3. Detailed Requirements
- Requirement 1 (what the function does)
- Requirement 2 (inputs validation)
- Requirement 3 (error handling)
- Requirement 4 (return value behavior)
4. Examples (happy path and errors)
Example 1: inputs → expected output
Example 2: edge case inputs → expected behavior
Example 3: error case → expected exception
5. Acceptance Criteria
- Code must pass tests [list]
- Must follow style [guide name]
- Performance target: [if applicable]
Real Example: Parsing a URL
Let's generate a function that parses URLs and validates them. Here's the prompt:
Language: Python 3.11
Purpose: Parse a URL string and extract its components
Function Signature:
def parse_url(url: str) -> dict[str, str | None]:
Requirements:
1. Accept a URL string (http://, https://, ftp://, etc.)
2. Extract components: scheme, host, port, path, query, fragment
3. Validate URL format using RFC 3986
4. If URL is malformed, raise ValueError with a descriptive message
5. Normalize the host to lowercase
6. Return a dict with keys: scheme, host, port, path, query, fragment
7. If a component is missing, set its value to None (not empty string)
8. Include a module-level docstring and function docstring (Google style)
Examples:
Example 1 (valid HTTPS URL):
Input: "https://example.com:8080/path/to/page?key=value#section"
Output: {
'scheme': 'https',
'host': 'example.com',
'port': '8080',
'path': '/path/to/page',
'query': 'key=value',
'fragment': 'section'
}
Example 2 (URL without port):
Input: "http://EXAMPLE.COM/test"
Output: {
'scheme': 'http',
'host': 'example.com', # note: lowercased
'port': None,
'path': '/test',
'query': None,
'fragment': None
}
Example 3 (malformed URL):
Input: "not a url"
Expected: raises ValueError("Invalid URL format: missing scheme")
Example 4 (URL with query but no path):
Input: "https://api.example.com?action=list"
Output: {
'scheme': 'https',
'host': 'api.example.com',
'port': None,
'path': None,
'query': 'action=list',
'fragment': None
}
Constraints:
- Use only the standard library (urllib.parse)
- No regex (use urllib's built-in parser)
- Type hints required
- Docstring: Google style with Args, Returns, Raises sections
- Code style: Black (line length 88, use f-strings)
This prompt is specific enough that the AI model can generate correct code without iteration. It includes:
- Clear function signature (inputs and return type)
- Detailed requirements (7 numbered items)
- Edge cases (missing port, case normalization)
- Error scenarios (malformed URL)
- Style constraints (PEP 8, Black, Google docstrings)
Common Function Generation Scenarios
Scenario 1: Data Transformation
Transform input data to output format (parsing, formatting, conversion).
Function: format_phone_number(phone: str) -> str
Requirements:
1. Accept phone strings in various formats: "1234567890", "(123) 456-7890", "+1-123-456-7890"
2. Normalize to format: "(123) 456-7890" (US numbers)
3. Validate exactly 10 digits (US standard)
4. If invalid, raise ValueError("Invalid US phone number")
5. Preserve leading + for international numbers, but don't process them
6. Docstring with examples
Examples:
- "1234567890" → "(123) 456-7890"
- "+1-234-567-8901" → "+1 (234) 567-8901"
- "abc" → ValueError
Scenario 2: Validation
Check that input meets criteria and return true/false or raise errors.
Function: is_valid_password(password: str) -> bool
Requirements:
1. At least 12 characters
2. At least one uppercase letter
3. At least one lowercase letter
4. At least one digit
5. At least one special character (!@#$%^&*)
6. No spaces
7. Return True if all criteria met, False otherwise
8. Docstring with examples
Examples:
- "MyPass123!" → True
- "weakpassword" → False (no uppercase, no digit, no special char)
- "Password123 " → False (contains space)
Scenario 3: Integration with External Systems
Call an API, database, or service and handle responses.
Function: fetch_github_user(username: str) -> dict
Requirements:
1. Use requests library to call GitHub API: GET /users/{username}
2. Return a dict with fields: login, name, public_repos, followers
3. If user not found (404), return None (not raise)
4. If network error or API error (5xx), raise ConnectionError
5. Parse JSON response
6. Timeout: 5 seconds
7. Docstring with example
Examples:
- fetch_github_user("torvalds") → {'login': 'torvalds', 'name': 'Linus Torvalds', 'public_repos': 5, 'followers': 123456}
- fetch_github_user("nonexistent_user_xyz") → None
- fetch_github_user("...") when API down → raises ConnectionError
Scenario 4: Business Logic
Calculations, decision-making, and complex rules.
Function: calculate_order_total(items: list[dict], tax_rate: float, discount_percent: float) -> float
Requirements:
1. items is a list of dicts with keys: 'price' (float), 'quantity' (int)
2. Calculate subtotal: sum of (price * quantity)
3. Apply discount: subtotal * (1 - discount_percent / 100)
4. Apply tax: (subtotal after discount) * (1 + tax_rate / 100)
5. Validate inputs:
- If any price < 0, raise ValueError("Price cannot be negative")
- If tax_rate < 0, raise ValueError("Tax rate cannot be negative")
- If discount_percent > 100, raise ValueError("Discount cannot exceed 100%")
6. Round final total to 2 decimal places
7. Docstring with example
Examples:
- items=[{'price': 10.0, 'quantity': 2}], tax_rate=8.0, discount_percent=10 → 19.44
(subtotal=20, after 10% discount=18, after 8% tax=19.44)
Handling Functions with Complex State
If your function manipulates objects or class state, include context:
Class Context:
class UserManager:
def __init__(self):
self.users = {} # dict[int, User]
def get_user(self, user_id: int) -> User | None:
return self.users.get(user_id)
Function to Generate:
def add_user(self, user_id: int, name: str, email: str) -> User:
Requirements:
1. Create a new User object (id, name, email, created_at timestamp)
2. Validate email format (basic check: must contain @)
3. If user_id already exists, raise ValueError("User already exists")
4. Store in self.users dict
5. Return the created User
6. Docstring with example
Example:
- manager = UserManager()
user = manager.add_user(1, "Alice", "[email protected]")
# returns User(id=1, name="Alice", email="[email protected]", created_at=<timestamp>)
Tips for Better Function Prompts
Be specific about return values. Don't say "return the result"; say "return a list of tuples where each tuple is (user_id, username)".
Specify error types. Don't say "handle invalid input"; say "raise ValueError if input is a string".
Include docstring format. Specify "Google style" or "NumPy style" for Python, "JSDoc" for JavaScript.
Test mentally first. Before writing the prompt, imagine calling the function with various inputs. If you can't picture the expected output, your prompt is too vague.
Use realistic examples. Examples with actual values (not placeholders) help the AI model understand your intent better.
# Vague:
Example: input → output
# Better:
Example: calculate_discount("SPRING20", 100.0) → 80.0 (20% discount)
Key Takeaways
- Use the five-part function prompt: signature, requirements, examples, constraints, acceptance criteria.
- Include at least three examples: happy path, edge case, error case.
- Be specific about error handling and return value formats.
- Include docstring requirements and code-style constraints.
- Test prompts mentally before generating code: can you explain what the function should do?
Frequently Asked Questions
Should I include type hints in the function signature?
Yes, always. Type hints help the AI model understand parameter and return types. They're also required for Python type checking and improve code clarity.
What if the function needs to do too much for one function?
Break it into smaller functions. If your requirement description exceeds 10 bullet points, split into two functions. For example, instead of "parse and validate a CSV," create parse_csv() and validate_csv() separately.
How do I handle optional parameters?
Include them in the signature with default values. Example: def fetch_user(user_id: int, include_repos: bool = False) -> dict. In requirements, specify what each optional parameter does.
Can I generate a function that calls other generated functions?
Yes, include the called functions in your context. Show the signature and docstring of any dependencies. The AI model will integrate them correctly.
What if the function is language-specific and uses idioms I don't know?
That's fine. Describe the behavior and let the AI model apply idiomatic patterns. For example, in Rust, just say "should return Result<T, Error>" and the model will use proper error handling.