Rebuilding Invoice System with AI: A 48-Hour Journey
Table Of Contents
You know that moment in Star Wars when the Rebels have to quickly adapt their battle plans because the Empire throws a curveball? That’s exactly how I felt when my client announced they were moving to a new payment system. Sure, most things stayed the same, but the invoice generation process? That needed a complete overhaul.
The Breaking Point
Our original setup was like a game of bureaucratic ping-pong: compile screenshots for two weeks, process images, generate USD invoices for the client, then create separate INR versions for our accountants and tax folks. It worked, but it was about as elegant as a Stormtrooper’s aim.
The new system promised something different - a streamlined process where you input key details and poof! - weekly PDFs appear automatically. Great for the client side, but we still needed those INR invoices for our accounting system. This wasn’t just a migration; it was a chance to rebuild smarter.
The 90/10 Approach
Like many developers keeping up with the AI revolution, I’ve been fascinated by how LLMs are reshaping our development workflow. The community’s been buzzing about developers becoming more like orchestrators than traditional coders - gathering requirements, crafting prompts, and steering AI assistants toward solutions.
Think of it like being Master Yoda - you’re not doing the heavy lifting yourself, but you’re guiding the Force (in this case, the AI) to achieve what needs to be done. You’re the conductor of an AI orchestra, ensuring all the pieces come together harmoniously.
I decided to put this theory to the test: could I rebuild our invoice system using 90% AI-generated code, acting primarily as a prompt engineer and system architect? With GitHub Copilot and its agent beta as my padawans, I embarked on this experiment.
Prompt Engineering as System Design
Thanks to IndyDevDan’s Principled AI Coding course, I approached this with two key concepts as discussed in the course:
-
Information Dense Keywords (IDKs) - these are like the Force-sensitive pressure points of prompt engineering. Words like ADD, UPDATE, APPEND, and MIRROR that clearly communicate intent to the AI.
-
Spec prompts - the architectural blueprints that outline everything from high-level requirements to specific file changes, without getting lost in the details.
Here is the updated version of the spec prompt, I modified IndyDevDan’s template, which worked with aider, to suite how Github Copilot works:
# Specification Template
> Ingest the information from this file, implement the Low-Level Tasks, and generate the code that will satisfy the High and Mid-Level Objectives.
## High-Level Objective
- [High level goal of the work goes here in 20 words or less]
## Mid-Level Objective
- [List of mid-level objectives - what are the steps to achieve the high-level objective?]
- [Each objective should be concrete and measurable]
- [But not too detailed - save details for implementation notes]
## Implementation Notes
- [Important technical details - what are the important technical details?]
- [Dependencies and requirements - what are the dependencies and requirements?]
- [Coding standards to follow - what are the coding standards to follow?]
- [Other technical guidance - what are other technical guidance?]
## Context
### Beginning context
- [List of files that exist at start, only file names with indication as (new) if the file needs to be created - what files do we need to start the work?]
- file.js
- file2.js
- file3.js (new)
### Ending context
- [List the expected output files and any new files created during the process]
- file.js
- file2.js
- file3.js (new)
## Low-Level Tasks
> Ordered from start to finish
<!-- All the low level tasks that will guide implementation. Here think about endgoal and work backwards from it.
First task should be the basis, and the other task can be built upon the earlier tasks.
FORMAT: Only include formatted output and nothing else
1. [Task - what is the task?]
\`\`\`prompt
CREATE/UPDATE fileName:
- CREATE/UPDATE/DELETE function:
- What exactly needs to be done in the function?
- If there are any code examples, add them here verbatim.
\`\`\`
-->
Now map this to a Prompt file, which is a new functionality to create and use reusable prompt file for Github Copilot. You can add one of those reusable prompts in chat, add your additional requirement and it will run as if it’s part of our tasks. Here is one of prompt which uses above spec prompt.md file.
Generate task breakdown for the tasks based provided in <Task> tag on the following requirements.
For each task, include:
1. Files/components to be modified or created
2. Clear implementation details that align with the existing codebase
3. Dependencies and relationships with existing components
4. Configuration changes needed
Implementation guidelines:
- Follow existing patterns and coding conventions mentioned in [conventions.md](./convention.md)
- Reuse existing services/utilities where applicable
- Consider impacts on existing functionality
- Include performance considerations based on established patterns
Each task should be self-contained and implementable without requiring additional context beyond what's provided in the task description and the referenced files
When implementing changes, refer to and consider the file content mentioned in <AdditionalFiles> tag if provided.
Make sure the output follows the format of [specPrompt.md](./specPrompt.md)
Usually I have conventions file and I attach this prompt to copilot. Explain what I needed in <Task>
tag, attach @workspace
in the prompt to help the model understand the context of the codebase. And bam! It generated a spec prompt. Once I am done, I send it to Copilot edits, and that worked like a charm. I only send some additional prompts to do the course correction. That too with IDKs when possible.
The result of the workflow mentioned above is like watching a well-choreographed dance. Here’s how it handled one of our trickier requirements - the currency conversion service:
import { ExchangeRate } from '../types/currency';
export class CurrencyService {
private static readonly PRECISION = 2;
async convertUSDtoINR(
amountUSD: number,
rate: ExchangeRate
): Promise<number> {
if (amountUSD < 0) {
throw new Error('Amount cannot be negative');
}
const amountINR = amountUSD * rate.value;
return Number(amountINR.toFixed(this.PRECISION));
}
formatINR(amount: number): string {
return new Intl.NumberFormat('en-IN', {
style: 'currency',
currency: 'INR'
}).format(amount);
}
}
The Human Element
While AI handled 90% of the heavy lifting, that crucial 10% human touch made all the difference. Like a Jedi sensing disturbances in the Force, I needed to:
- Debug edge cases, especially around the transition from a two-hop system to a single-hop process
- Fine-tune PDF formatting to match original templates
- Write and verify prompts (yes, ironically, the tool that writes code needs human-written instructions)
It’s similar to how FCB’s tiki-taka style needs human creativity despite its systematic nature - the system provides the framework, but human insight guides its application.
Lessons from the Trenches
After five prompt revisions and 48 hours of development, here’s what I learned:
-
Clarity is king: The more precise your spec prompt, the better the AI’s output. It’s like giving good requirements to a junior developer.
-
Trust but verify: AI can write impressive code, but always review critical sections, especially around currency calculations and file processing.
-
Iterate quickly: Don’t spend hours perfecting a prompt. Try something, see how the AI responds, and refine based on results.
The Future of Development
This experiment showed me a glimpse of development’s future. We’re moving from being pure coders to becoming AI orchestrators. It’s like the transition from assembly to high-level languages - we’re abstracting away implementation details to focus on system design and business logic.
The implications are fascinating:
- Developers will spend more time on architecture and less on routine coding
- Documentation and clear requirements become even more crucial
- The ability to “speak AI” becomes as important as knowing a programming language
This shift reminds me of how Pep Guardiola revolutionized football tactics - it’s not about doing away with fundamentals, but about elevating them to a new level of sophistication.
Conclusion
Rebuilding my invoice system wasn’t just about solving a technical problem - it was about exploring a new way of development. Like the Rebels adapting to new Imperial threats, we developers need to embrace and master these new tools.
The future belongs to developers who can blend traditional coding skills with AI orchestration. We’re not being replaced; we’re being upgraded to development version 2.0.
May the Force (and the LLMs) be with you…