Building complex conditional forms + using LLM's for type analysis
Context
Our platform needed to publish a Defense Supplier Checklist as a marketplace offering. The challenge? Building a complex form with 26 interdependent questions, each requiring specific conditional logic based on previous answers. The kicker: it needed to be done under significant time pressure.
Note: While specific implementation details have been anonymized, this case study focuses on the technical approach to solving a common challenge in form builders: managing complex conditional logic while maintaining type safety and user experience.
What I Built
I developed an automated form generation system that could:
- Create hierarchical form structures with complex conditional logic
- Handle recursive dependencies between form fields
- Maintain type safety across the entire form structure
- Generate proper GraphQL mutations for form creation
The end result was a fully functional, conditional form workflow that guides users through a complex compliance checklist.
Technical Breakdown
Stack & Tools
- TypeScript/Node.js: Core implementation
- GraphQL: Platform API integration
- LLM: Type analysis and condition generation
- Form Builder API: Custom platform form creation
Key Architecture Decisions
- Field ID Management System
interface FieldIds {
[key: string]: {
id: string;
slug: string;
};
}
// Store and track field IDs for conditions
const fieldIds: FieldIds = {};
// Create fields and store references
const choiceField = await createSingleChoiceField(
tenantId,
section.id,
`q${questionNumber}`,
label,
conditions,
);
fieldIds[`q${questionNumber}`] = {
id: choiceField.id,
slug: choiceField.slug,
};
This system:
- Tracks all created field IDs and slugs
- Enables reference to previous fields in conditions
- Maintains type safety for field references
- Complex Conditional Logic
// Example of nested conditional logic
const conditions = {
query: {
expression: {
condition: "AND",
rules: Array.from({ length: 25 }, (_, idx) => ({
id: fieldIds[`q${(idx + 1).toString().padStart(2, "0")}`].id,
operator: "EQUALS_TO_OPTION",
value: "1",
slug: fieldIds[`q${(idx + 1).toString().padStart(2, "0")}`].slug,
})),
},
},
};
// Field creation with conditions
await updateFormField(tenantId, field.id, {
richTitle: label,
metadata: {
type: "SELECT",
options: [
{ label: "Ja/Yes", value: "1" },
{ label: "Onderhanden/WIP", value: "2" },
{ label: "Nee/No", value: "0" },
],
visible: conditions,
},
});
The conditional system handles:
- Multi-level dependencies
- Complex AND/OR logic
- Dynamic visibility rules
- Type-safe condition generation
- LLM-Assisted Type Analysis
// Example of type structure fed to LLM
interface FormFieldMetadata {
type: "SELECT" | "TEXTAREA" | "FILE_INPUT";
visible?: {
query: {
expression: {
condition: "AND" | "OR" | "NOT";
rules: Array<{
fieldId: string;
operator: "EQUALS_TO_OPTION" | "NOT_EQUALS_TO_OPTION";
value: string;
slug: string;
}>;
};
};
};
// ... other type definitions
}
Used LLM to:
- Analyze complex type definitions
- Generate correct condition structures
- Validate conditional logic
- Suggest proper field configurations
What I Learned
- Time-Constrained Development
When faced with tight deadlines, I learned to:
- Prioritize working solutions over perfect implementations
- Leverage AI tools for rapid development
- Focus on core functionality first
- Use existing patterns where possible
- Type System Analysis
The approach of feeding type definitions to LLMs proved valuable:
- Quick understanding of complex type systems
- Accurate condition generation
- Reduced trial and error
- Faster development cycle
- Conditional Form Architecture
Key insights about building conditional forms:
- Store field references immediately after creation
- Build conditions incrementally
- Validate condition types early
- Test edge cases in dependencies
What's Next?
-
Robustness Improvements
- Build type validation system
- Add condition testing framework
- Implement condition visualization
- Create condition templates
-
Developer Experience
- Build condition builder UI
- Add type generation tools
- Improve error messages
- Create debugging tools
-
Performance Optimization
- Cache field references
- Batch condition updates
- Optimize condition evaluation
- Implement change tracking
Key Takeaways
In this project what I took away is the importance of balancing technical debt with delivery speed. By leveraging LLMs for type analysis and focusing on core functionality, I was able to deliever a complex form build under tight time constraints (quicker than our CSM team could have done it manually).
Hey, sometimes the "perfect" solution isn't the right solution. In this case, using AI to understand and generate complex type structures was more efficient than building a comprehensive type system from scratch.
I think it demonstrated how modern development tools (like LLMs) can be creatively applied to solve traditional development challenges, especially when time is a critical factor.
Note: This case study focuses on the technical implementation while respecting confidentiality around specific requirements and implementations.