// 01 · the-context
The context
A multi-site accounting firm with 260 employees. Daily reality: business tools scattered everywhere (Pennylane for client management, 360Learning for training, a shared drive, emails), redundant manual data entry, constant copy-paste.
The mission goal: build a single internal platform that centralizes everything, automates flows between tools, and integrates AI where it brings real business value.
A parallel business priority: reduce dependency on costly third-party tools. On the training side specifically, internalize content to progressively cut the 360Learning subscription.
// 02 · the-solution
The solution
I joined a project already kicked off by another developer, built on a Domain-Driven Design / Hexagonal architecture. I took the time to understand the existing structure before extending — domain, application, infrastructure clearly separated — to stay consistent with what was in place.
Modules I contributed to:
- →Client management: Pennylane API integration, enriched client records, mission and fiscal deadline tracking
- →Training module: import from 360Learning (50 existing trainings consolidated in-house, to progressively reduce subscription dependency)
- →AI training generation module (the most interesting piece — detailed below)
- →Employee onboarding: HR documents workflow, access provisioning
- →Internal knowledge base: full-text search for all employees
- →Pilot dashboards: missions, deadlines, workload per partner
AI integration: generating training from raw documents
Before, creating internal training would take hours from a trainer: take a PDF or slides, structure an outline, write learning objectives, split into modules.
I built a training generation module that takes a raw document as input (PDF, slides, internal doc) and outputs a structured, ready-to-deploy training: title, objectives, modules, key points.
5 trainings have already been AI-generated and deployed internally, alongside the 50 imported from 360Learning. The logic: import to consolidate the existing, generate to stop depending on an external tool.
// Simplified pipeline
async function generateTrainingFromDocument(
documentUrl: string,
options: TrainingOptions,
): Promise<Training> {
// 1. Content extraction (PDF, slides, .docx)
const content = await extractContent(documentUrl);
// 2. Structured generation via Claude API
const training = await claude.generate({
schema: TrainingSchema, // Strict Zod validation
prompt: buildTrainingPrompt(content, options),
});
// 3. Business validation + persistence
await validateTrainingRules(training);
return await trainingRepository.save(training);
}
The module enforces a Zod-validated output structure: Claude never returns free text, only a typed object that can be directly persisted. If the LLM deviates from the schema (rare), we retry with a prompt enriched by the validation error.
Cross-tool automations
I also built automations to eliminate redundant entries between the firm's tools — notably via the Pennylane API for client management and the 360Learning import for trainings.
// 03 · technical-challenges
Technical challenges
Fitting into an existing architecture. The project was already running in DDD/Hexagonal when I joined. Rather than impose my habits, I took time to understand the existing patterns and extend in the same style. It's rarely the default reflex for a new dev on a project, but it's what keeps the codebase coherent months down the line.
Guaranteeing AI-generated content quality. A poorly generated training is worse than no training at all — it discredits the tool and nobody uses it. Strict Zod output validation, mandatory human review before publishing, free editing after generation.
Integrating a critical third-party API (Pennylane). Pennylane manages the firm's client data. A bad integration could corrupt the source of truth. Built a clean abstraction layer with error handling and retry, so a third-party outage never blocks the CRM user.
// 04 · business-results
Business results
- →260 employees use the platform daily
- →50 trainings imported from 360Learning consolidated in the internal CRM
- →5 AI-generated trainings already in production
- →Approximately €60k estimated savings over 2 years on the client side, mainly tied to reduced 360Learning dependency and tool consolidation
// 05 · tech-stack
Tech stack
| Layer | Technologies |
|---|---|
| Frontend | Next.js, TypeScript, Tailwind |
| Backend | NestJS, TypeScript, DDD / Hexagonal architecture |
| Database | PostgreSQL, TypeORM, Redis (cache + queues) |
| AI | Claude API (Anthropic), structured prompting, Zod validation |
| Auth & Storage | Supabase Auth, AWS S3 |
| Integrations | Pennylane API, 360Learning API (import) |
| Infra | Docker, CI/CD |
// 06 · what-im-taking-away
What I'm taking away
This mission confirmed that AI is only useful when integrated into existing workflows. A standalone AI assistant, nobody uses. An AI module that takes a document you already have on hand and delivers a structured, ready-to-deploy training — that gets used every day.
And the real skill on this kind of mission isn't LLM prowess. It's clean integration into an existing architecture, with strict validations, fallbacks, and a user experience that makes the AI invisible.