BusinessMath Development Journey
8 min read
Development Journey Series
We were implementing IRR (Internal Rate of Return) calculation for BusinessMath. IRR is conceptually simple—find the discount rate where NPV equals zero—but the implementation requires iterative solving with Newton-Raphson method.
I had a working implementation. The tests passed. The calculations were correct.
But I couldn’t document it.
When I tried to write the DocC documentation, I struggled to explain what the parameters meant, when the function would throw errors, and what users should expect. The act of documentation revealed design flaws in the API itself.
That’s when we discovered: If you can’t document it clearly, the API design is wrong.
The traditional workflow puts documentation last:
The problem: By step 4, you’ve invested heavily in the implementation. Changing the API now feels expensive. So you write convoluted documentation to explain a poorly designed API instead of fixing the root cause.
With AI generating code quickly, this problem accelerates. AI happily implements whatever you ask for, but it doesn’t push back on bad API design. You get working code with terrible interfaces.
We needed to front-load the design validation.
Write complete DocC documentation BEFORE implementing anything.
1. Write the DocC Tutorial First
Before writing any implementation code, write the complete DocC article including:
2. If Documentation Is Hard to Write, Redesign the API
Struggling to document? That’s a signal. The API is confusing. Fix it now while it’s cheap.
3. Use Documentation as AI Specification
Once the documentation reads clearly, give it to AI as the implementation spec. The clearer your docs, the better AI’s implementation.
Here’s what AI generated on the first attempt:
// BEFORE: Hard to document
public func calc(_ a: [Double], _ b: Double, _ c: Int) -> Double?
Trying to document this:
/// Calculates... something?
///
/// - Parameter a: An array of... values? Cash flows?
/// - Parameter b: A rate? Or is it a guess?
/// - Parameter c: Maximum... iterations? Or is it periods?
/// - Returns: The result, or nil if... it fails?
Even writing this, I had to guess what the parameters meant. That’s a sign of bad API design.
After redesigning the API with documentation in mind:
// AFTER: Easy to document
/// Calculates the internal rate of return for a series of cash flows.
///
/// The IRR is the discount rate that makes NPV equal to zero.
/// Uses Newton-Raphson method for iterative solving.
///
/// ## Usage Example
///
/// let cashFlows = [-1000, 300, 400, 500]
/// let irr = try calculateIRR(cashFlows: cashFlows)
/// print(irr.percent(1)) // "12.5%"
///
/// - Parameter cashFlows: Array of cash flows, starting with initial investment
/// - Returns: IRR as Double (0.125 = 12.5%)
/// - Throws: `FinancialError.convergenceFailure` if doesn't converge
public func calculateIRR(cashFlows: [Double]) throws -> Double
Notice the difference:
calculateIRR (not calc)cashFlows (not a)Double (not Double?)throws (not returning nil)The first attempt returned Double? (optional). But when I tried to document this:
/// - Returns: The IRR, or nil if...
I couldn’t finish the sentence. What does nil mean?
The documentation revealed the design flaw: we needed typed errors, not ambiguous nil.
Fix:
enum FinancialError: Error {
case convergenceFailure
case invalidCashFlows
}
public func calculateIRR(cashFlows: [Double]) throws -> Double
Now the documentation writes itself:
/// - Throws: `FinancialError.convergenceFailure` if doesn't converge after 100 iterations
/// `FinancialError.invalidCashFlows` if all cash flows are positive
When writing the usage example, I wrote:
let irr = try calculateIRR(cashFlows: cashFlows)
print(irr) // Prints: 0.12456789
Looking at that output, I realized: Users will want percentages, not decimals.
This led to adding format guidance in the documentation:
print(irr.percent(1)) // "12.5%"
Without writing the example first, I wouldn’t have caught this usability issue.
Once the documentation was clear, I gave it to AI with this prompt:
“Implement
calculateIRRto match this documentation exactly. Use Newton-Raphson method. The function signature must match what’s documented.”
AI’s implementation:
No back-and-forth. No debugging. The documentation was the specification, and AI executed it perfectly.
My initial documentation attempt:
/// Calculates IRR for cash flows.
///
/// - Parameter cashFlows: The cash flows
/// - Returns: The IRR
This tells you nothing. What’s the format? What are the units? What can go wrong?
AI implemented it, but not the way I wanted. It made assumptions about default values, convergence tolerance, and error handling that didn’t match my intent.
Fix: Be specific. Include units, formats, edge cases, and examples.
I wrote the example before implementing the function (good!), but I made a mistake:
// Wrong:
let irr = calculateIRR([-1000, 300, 400, 500]) // Missing label!
When I tried to build the documentation, it failed.
This is actually good! I caught the error in documentation, not in user code. Fixed it immediately:
// Correct:
let irr = try calculateIRR(cashFlows: [-1000, 300, 400, 500])
Lesson: Documentation examples should compile. If they don’t, fix the API before implementing.
If you can’t document it clearly, the API design is wrong. Fix it while it’s cheap.
Documentation-first development creates a forcing function:
By writing documentation first, you catch these issues before investing in implementation. Redesigning the API takes 5 minutes. Redesigning after implementation, tests, and integration takes hours.
Key Takeaway: Write DocC before implementation. If the docs are hard to write, the API is wrong. Fix it now, while it’s cheap.
For your next feature:
1. Write Complete DocC First
2. Check for Red Flags
3. Redesign if Needed
4. Give Documentation to AI
5. Verify Example Compiles
This practice is demonstrated throughout the BusinessMath library:
Technical Examples:
Related Practices:
Problem: “I’ll fill in details later” → Never happensSolution: Write complete docs now. It takes 10 minutes and saves hours.
Problem: You’ll rationalize the existing API instead of improving itSolution: Docs first, always. Don’t compromise.
Problem: Users copy broken examples and get frustratedSolution: Build documentation in Xcode, fix compile errors immediately
Questions to consider:
Series Progress:
Tagged with: ai-collaboration, documentation, api-design, docc, development journey