A Reproducible Methodology for Generating Real, Clean, and Scalable Software Using LLM Agents Orchestrated Through Formal FRD Documents
Introduction
Over the past few weeks, Iโve been applying a development approach based on FRDs (Functional Requirements Documents) orchestrated with the support of Artificial Intelligence to build modern, clean, and scalable backends. This method allowed me to generate a complete backendโfrom CRUD, database layer, JWT authentication, migrations, and unit testingโwithout chaos, without improvisation, and with clean code from the very first commit.
Although FRDs are a traditional standard widely used in software engineering, whatโs truly interesting here is not the FRD concept itself, but how FRDs are combined with LLM agents to create a deterministic build process.
This approach aligns with modern techniques such as LLM Orchestration, Task Graphs, and Agentic Pipelines, which are becoming mainstream in tools like Windsurf, Cursor, Claude Sonnet, and VSCode Agents.
One key insight I found early on was that I initially started with a massive FRDโa single document describing all phases (boilerplate, database, authentication, testing, etc.). Even though the document was detailed, two real problems quickly emerged:
- LLMs had to process too much information in a single context window, increasing reasoning load and causing the agent to drift, forget steps, or ask redundant questions.
- The process became slow because the model entered loops of โconfirmation,โ uncertainty, ambiguity, and frequent re-validation.
This led me to investigate a more scalable approach: splitting the project into multiple small FRDs, each responsible for a specific phase, and introducing a Master FRD-00 responsible for orchestrating them in the correct order.
With this structure:
- The LLM no longer needs to parse a giant document
- Each stage has a short, precise FRD
- The Master FRD controls the entire execution pipeline
- The agent stops asking โwhatโs nextโ and simply executes
- Adding new features becomes safer and easier because each one lives in its own FRD without interfering with others
What Is FRD-Based Orchestration?
FRDs define:
- What needs to be built
- In what order
- What criteria must be satisfied
- Which steps are mandatory
- What behaviors the AI must validate
- What acceptance conditions each phase must pass
The key insight is that youโre not only describing features, youโre also defining the construction process itself, ensuring accuracy, repeatability, and zero improvisation.
๐ Connection to Existing Practices
FRD-based orchestration does not try to reinvent methodologiesโit integrates formal, well-known practices with the new wave of AI-assisted development.
This method incorporates ideas from:
- LLM-driven Development Workflows
- Prompt Chaining
- Task Graph Execution
- Reproducible Pipelines
- Specification-Driven Engineering
The contribution here lies in how the process is structured, not in the FRD concept itself.
FRD-00: The Orchestration Controller
This master document defines:
- Required phases
- Clear dependencies
- Agent behavior
- Validation rules per stage
- Strict conditions to advance or stop
- Success criteria for each phase
It acts as the logical pipeline that ensures the project can be executed by AI without deviations.
๐ How Orchestration Actually Begins
Before starting any phase, I execute this initial instruction so the AI loads all FRDs and understands the execution order:
@FRD-00-master-orchestration.md
@FRD-01-boilerplate-core-products.md
@FRD-02-products-database.md
@FRD-03-auth-security.md
@FRD-04-unit-testing.md
folder name: api-products
FRD-00-master-orchestration.md
FRD-01-boilerplate-core-products.md
FRD-02-products-persistence-typeorm.md
FRD-03-unit-testing.md
Start the orchestration.
Why Is This Critical?
- Ensures the agent follows the correct order
- Enforces FRD-00 as the master controller
- Reduces context overload
- Prevents unnecessary loops or clarifying questions
- Makes the process reproducible across any AI editor: Windsurf, Antigravity, Cursor, Sonnet, VSCode Agents, etc.
General Project Architecture
The final result was a NestJS backend with:
๐งฉ Included Modules
- Auth (JWT + bcrypt + Passport)
- Users (registration, login, hashing)
- Products (CRUD with validation)
- Centralized config
- SQLite + TypeORM + migrations
- Unit testing (33 tests)
๐ Final Structure
api-products/
โโโ src/
โ โโโ auth/
โ โโโ products/
โ โโโ users/
โ โโโ config/
โ โโโ main.ts
โโโ migrations/
โโโ README.md
โโโ package.json
๐ฅ Phase 1 โ Boilerplate + In-Memory CRUD
- Project created using
nest new - Swagger enabled
- Global validation pipes
- In-memory CRUD
- DTOs with class-validator
- Mandatory JSDoc
export class CreateProductDto {
@IsString()
name: string;
@IsBoolean()
isPremium: boolean;
@IsNumber()
price: number;
}
๐๏ธ Phase 2 โ Database + TypeORM
- TypeORM integration
- Migrations
- ProductRepository
- Real persistence using SQLite
@Entity()
export class Product {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column()
name: string;
@Column()
isPremium: boolean;
@Column('float')
price: number;
}
๐ Phase 3 โ JWT Authentication
- UsersService + repository
- bcrypt hashing
- LocalStrategy + JwtStrategy
- JwtAuthGuard
- Swagger Bearer Auth
async login(loginDto: LoginDto): Promise<{ accessToken: string }> {
const user = await this.usersService.validateCredentials(loginDto.email, loginDto.password);
if (!user) {
throw new UnauthorizedException('Invalid credentials');
}
const payload = { email: user.email, sub: user.id };
const accessToken = this.jwtService.sign(payload);
return { accessToken };
}
๐งช Phase 4 โ Unit Testing
- 6 test suites
- 33 tests
- Clean mocks
- No real database
describe('login', () => {
it('should login successfully and return access token', async () => {
const loginDto: LoginDto = {
email: 'test@example.com',
password: 'password123',
};
const accessToken = 'jwt-token';
mockUsersService.validateCredentials.mockResolvedValue(mockUser);
mockJwtService.sign.mockReturnValue(accessToken);
const result = await service.login(loginDto);
expect(result).toEqual({ accessToken });
expect(usersService.validateCredentials).toHaveBeenCalledWith(loginDto.email, loginDto.password);
expect(jwtService.sign).toHaveBeenCalledWith({ email: mockUser.email, sub: mockUser.id });
});
it('should throw UnauthorizedException for invalid credentials', async () => {
const loginDto: LoginDto = {
email: 'test@example.com',
password: 'wrongpassword',
};
mockUsersService.validateCredentials.mockResolvedValue(null);
await expect(service.login(loginDto)).rejects.toThrow(UnauthorizedException);
expect(usersService.validateCredentials).toHaveBeenCalledWith(loginDto.email, loginDto.password);
});
});
๐ Final Results
- All 33 tests passing
- Migrations working
- JWT fully functional
- CRUD operational
- Consistent architecture
๐ผ๏ธ Visual Evidence
(Gallery with screenshots from Windsurf, Antigravity, and Claude Sonnet)
๐ง Why This Matters in the AI Era
- AI needs structure
- Enables end-to-end project execution
- Reinforces best practices in LLM-assisted development workflows.
- Reproducible, scalable, modular
๐งญ Conclusion
FRD Orchestration is not merely an operational techniqueโit is a way of structuring collaboration between humans and AI models to produce reproducible, stable, and scalable software.
This method enhancesโnot replacesโthe developer.
๐ Official Repository with FRDs + Full Backend
`



Top comments (0)