diff --git a/content/_index.md b/content/_index.md index a6b96dd9..776ca694 100644 --- a/content/_index.md +++ b/content/_index.md @@ -123,16 +123,16 @@ images: ["images/og-image-fission.png"]
- PATTERN + NEW

- No. Series + Vibe Coding for AL

- The “Number Series” system is used extensively to provide numbers to master records, documents, and other transactions through Microsoft Dynamics 365 Business Central. + AI-optimized coding rules and guidelines designed to enhance the AL developer experience in modern AI-powered IDEs like VS Code and Cursor.

- - + +
diff --git a/content/docs/vibe-coding/README.md b/content/docs/vibe-coding/README.md new file mode 100644 index 00000000..48ce0477 --- /dev/null +++ b/content/docs/vibe-coding/README.md @@ -0,0 +1,102 @@ +# Vibe Coding Rules - Contribution Guide + +This directory contains AI-optimized coding rules for AL development. Each rule set is organized into markdown files that can be easily consumed by AI coding assistants. + +## Directory Structure + +``` +vibe-coding/ +├── _index.md # Main landing page +├── README.md # This file - contribution guide +├── al-guidelines-rules.md # Complete rules file with references to all categories +├── al-code-style.md # Code style, formatting, and documentation rules +├── al-naming-conventions.md # File naming, object naming, and variable naming +├── al-performance.md # Performance optimization and query guidelines +├── al-error-handling.md # Error handling patterns and troubleshooting +├── al-events.md # Event-driven development and extensibility +├── al-testing.md # AL-Go workspace structure and testing guidelines +``` + +## How to Add New Rules + +### 1. Choose the Right Category +Select the appropriate category for your rules, or create a new category if needed. + +### 2. Follow the Standard Format +Each rule file should follow this structure: + +```markdown +--- +title: "AL [Category Name] Rules" +description: > + Brief description of the rule category for AL development +globs: ["*.al", "*.json"] # File types this rule applies to +alwaysApply: true|false # Whether to always apply these rules +--- + +# AL [Category Name] Rules + +Brief introduction to the category and its importance in AL development. + +## Rule 1: [Descriptive Title] + +### Intent +What this rule aims to achieve, including implementation details and guidance for AI assistants. + +### Examples + +```al +// Good example +[code example] +``` + +You can also add a bad example to show what to avoid, but this is optional. +```al +// Bad example (avoid) +[code example] +``` + +## Rule 2: [Next Rule] +[Continue with same format...] +``` + +### 3. Update the Index +After adding new rule files: +- Update `al-guidelines-rules.md` to include `@your-new-file.md` reference +- Update `_index.md` to include links to new categories +- Ensure all cross-references are updated + +### 4. Test Your Rules +Before submitting, test your rules with AI assistants to ensure they: +- Are clearly understood by AI agents +- Produce the expected code patterns +- Don't conflict with existing rules + +## Contribution Workflow + +1. **Fork** the alguidelines repository +2. **Create** your rule files in the appropriate category +3. **Test** the rules with your preferred AI assistant +4. **Submit** a pull request with: + - Clear description of the rules added + - Examples of how the rules improve code quality + - Any testing results with AI assistants (optional) + +## Best Practices for Rule Writing + +### Make Rules AI-Friendly +- Use clear, unambiguous language specific to AL development +- Provide specific AL code examples with proper syntax +- Include both positive and negative examples, when applicable +- Structure content consistently + +## Questions? + +If you have questions about contributing rules, please: +- Open a discussion in the GitHub repository +- Join the Business Central community Discord +- Contact the initiative maintainers + +--- + +*This README is part of the Vibe Coding for AL initiative - enhancing AL development through AI-optimized guidelines.* \ No newline at end of file diff --git a/content/docs/vibe-coding/_index.md b/content/docs/vibe-coding/_index.md new file mode 100644 index 00000000..b002939d --- /dev/null +++ b/content/docs/vibe-coding/_index.md @@ -0,0 +1,100 @@ +--- +title: "Vibe Coding for AL" +tags: ["AL", "Vibe Coding"] +categories: ["Vibe Coding"] +description: > + AI-optimized coding rules and guidelines for AL development +--- + +_Created by the Business Central Community, Enhanced for AI-powered AL Development_ + +# Vibe Coding for AL + +Welcome to the **Vibe Coding for AL** initiative! This section contains AI-optimized coding rules and guidelines specifically designed to enhance the AL developer experience in modern AI-powered IDEs like VS Code and Cursor. + +## What is Vibe Coding? + +Vibe Coding represents a new approach to coding guidelines that are specifically formatted and structured to work seamlessly with AI coding assistants. These rules are designed to: + +- **Enhance AI Understanding**: Provide clear, structured guidelines that AI agents can easily parse and apply +- **Improve Code Quality**: Maintain high standards while leveraging AI assistance +- **Standardize Practices**: Create consistent coding patterns across the AL development community +- **Boost Productivity**: Help developers write better code faster with AI assistance + +## Key Features + +### 📋 **Structured Rule Format** +All rules are provided in markdown format with clear structure that AI agents can easily understand and apply during development. + +### 🔄 **Community-Driven** +Built and maintained by the AL community, including MVPs and the Microsoft product team. + +### 🛠️ **IDE Integration** +Future AL extension support will allow generating local rules files directly in VS Code and Cursor. + +### 🤖 **AI-Ready** +Designed as a foundation for AL-focused AI tools and Model Context Protocols (MCPs). + +## How to Use + +1. **Browse the Rules**: Explore the various rule categories below +2. **Copy for Your Project**: Use these rules as templates for your own coding standards +3. **Contribute**: Submit your own rule variations via pull requests +4. **Stay Updated**: Watch for AL extension integration coming soon + +## Rule Categories + +The Vibe Coding rules are organized into logical categories to make them easy to find and implement: + +- **[Complete AL Guidelines Rules](al-guidelines-rules/)** - Comprehensive rules file with references to other rules +- **[AL Code Style & Formatting](al-code-style/)** - Indentation, folder organization, and code documentation +- **[AL Naming Conventions](al-naming-conventions/)** - File naming, object naming, and variable naming patterns +- **[AL Performance Optimization](al-performance/)** - Query optimization, temporary tables, and performance analysis +- **[AL Error Handling & Troubleshooting](al-error-handling/)** - Try/catch patterns, debugging, and telemetry integration +- **[AL Event-Driven Development](al-events/)** - Event subscribers, integration events, and extensibility patterns +- **[AL Testing & Project Structure](al-testing/)** - AL-Go workspace structure, test generation, and project organization + +## Getting Started + +To get started with Vibe Coding for AL: + +1. Review the rule categories that apply to your development needs +2. Adapt the rules to your specific project requirements +3. Configure your AI assistant to use these guidelines +4. Share your experiences and contribute improvements back to the community + +## Future Roadmap + +### Phase 1: Foundation ✅ +- Host rules in AL Guidelines repository +- Community contribution process +- Initial rule sets from key contributors + +### Phase 2: Integration 🔄 +- AL extension support for local rules generation +- Enhanced AI agent compatibility +- MCP server integration + +### Phase 3: Expansion 🚀 +- Convert legacy C/AL patterns where applicable +- Generate new AL-specific patterns +- Establish as central trust source for AL AI agents + +## Contributing + +This initiative thrives on community contributions! Here's how you can help: + +- **Submit Rule Sets**: Share your proven coding rules via pull requests +- **Improve Existing Rules**: Suggest enhancements to current guidelines +- **Test & Validate**: Try the rules in your projects and provide feedback +- **Share Examples**: Contribute real-world examples of rule applications + +## Community & Support + +- **GitHub Repository**: [Microsoft AL Guidelines](https://github.com/microsoft/alguidelines) +- **Discussions**: Join conversations about Vibe Coding rules +- **Issues**: Report problems or suggest new features + +--- + +*The Vibe Coding for AL initiative is a collaborative effort between the Business Central community and Microsoft, aimed at revolutionizing how we write AL code in the age of AI.* \ No newline at end of file diff --git a/content/docs/vibe-coding/al-code-style.md b/content/docs/vibe-coding/al-code-style.md new file mode 100644 index 00000000..6e1aea24 --- /dev/null +++ b/content/docs/vibe-coding/al-code-style.md @@ -0,0 +1,167 @@ +--- +title: "AL Code Style & Formatting Rules" +description: > + AL Code structure, formatting, and folder organization guidelines for AL development +globs: ["*.al"] +alwaysApply: true +--- + +# AL Code Style & Formatting Rules + +These rules ensure consistent code structure and organization across AL projects, making code more maintainable and AI-assistant friendly. + +## Style guidelines for AL code + - Always use camelCase for variable and function names. + - Use PascalCase for object names (e.g., tables, pages, reports). + - Maintain a consistent indentation style (2 spaces preferred). + +## Commonly used methods and patterns + - Temporary tables for performance optimization + - Use of events for extensibility + +## Rule 1: Consistent Indentation and Formatting + +### Intent +Maintain consistent code formatting to improve readability and enable better AI understanding of code structure. Use 2-space indentation consistently throughout your project and maintain consistent formatting within functions and procedures. + +### Examples + +```al +// Good example +procedure CalculateDiscount(Amount: Decimal; DiscountPct: Decimal): Decimal +begin + if DiscountPct > 0 then + exit(Amount * DiscountPct / 100); + + exit(0); +end; +``` + +## Rule 2: Feature-Based Folder Organization + +### Intent +Organize code by business features rather than object types to improve maintainability and logical grouping. Use feature-based organization with `src/feature/subfeature/` structure and place shared components in `Common` or `Shared` folders. + +### Examples + +``` +// Good example - Feature-based organization +src/ +├── NoSeries/ +│ ├── NoSeries.Table.al +│ ├── NoSeries.Page.al +│ └── NoSeriesSetup.Codeunit.al +├── Sales/ +│ ├── Invoice/ +│ │ ├── SalesInvoice.Page.al +│ │ └── SalesInvoicePosting.Codeunit.al +│ └── Order/ +│ └── SalesOrder.Page.al +└── Common/ + ├── Helpers/ + │ └── DateHelper.Codeunit.al + └── Interfaces/ + └── IPostable.Interface.al +``` + +``` +// Bad example (avoid object-type segregation) +src/ +├── Tables/ +│ ├── NoSeries.Table.al +│ └── SalesHeader.Table.al +├── Pages/ +│ ├── NoSeries.Page.al +│ └── SalesInvoice.Page.al +└── Codeunits/ + ├── NoSeriesSetup.Codeunit.al + └── SalesInvoicePosting.Codeunit.al +``` + +## Rule 3: Code Documentation and Comments + +### Intent +Provide clear documentation for global functions using XML documentation comments. Code should be self-documenting through clear naming, but global functions in codeunits require proper documentation for API clarity. + +### Examples + +```al +// Good example - XML documentation for global functions +codeunit 50100 "Base64 Convert" +{ + /// + /// Converts the value of the input string to its equivalent string representation that is encoded with base-64 digits. + /// + /// The string to convert. + /// The string representation, in base-64, of the input string. + procedure ToBase64(String: Text): Text + begin + exit(Base64ConvertImpl.ToBase64(String)); + end; + + /// + /// Validates discount percentage against business rules. + /// + /// The discount percentage to validate. + procedure ValidateDiscountPercentage(DiscountPct: Decimal) + begin + if DiscountPct > 50 then + Error('Discount cannot exceed 50% due to company policy'); + + if DiscountPct < 0 then + Error('Discount percentage cannot be negative'); + end; +} +``` + +```al +// Bad example (avoid inline comments for obvious operations) +procedure ValidateDiscountPercentage(DiscountPct: Decimal) +begin + // Check if discount is greater than 50 + if DiscountPct > 50 then + Error('Discount cannot exceed 50%'); + + // Check if discount is less than 0 + if DiscountPct < 0 then + Error('Discount percentage cannot be negative'); +end; +``` + +## Rule 4: Modular and Reusable Code Structure + +### Intent +Keep code modular and reusable to enhance maintainability and reduce duplication. Write small, focused procedures that do one thing well and use interfaces and patterns where appropriate. + +### Examples + +```al +// Good example - Modular approach +procedure PostDocument(var DocumentHeader: Record "Sales Header") +begin + ValidateDocument(DocumentHeader); + CalculateTotals(DocumentHeader); + CreateLedgerEntries(DocumentHeader); + UpdateStatus(DocumentHeader); +end; + +local procedure ValidateDocument(var DocumentHeader: Record "Sales Header") +begin + if DocumentHeader."No." = '' then + Error('Document number cannot be empty'); +end; + +local procedure CalculateTotals(var DocumentHeader: Record "Sales Header") +begin + DocumentHeader.CalcFields(Amount); +end; +``` + +```al +// Bad example (avoid monolithic procedures) +procedure PostDocument(var DocumentHeader: Record "Sales Header") +begin + // All validation, calculation, and posting logic in one procedure + // ... 200+ lines of mixed concerns +end; +``` \ No newline at end of file diff --git a/content/docs/vibe-coding/al-error-handling.md b/content/docs/vibe-coding/al-error-handling.md new file mode 100644 index 00000000..dcc84411 --- /dev/null +++ b/content/docs/vibe-coding/al-error-handling.md @@ -0,0 +1,167 @@ +--- +title: "AL Error Handling & Troubleshooting Rules" +description: > + AL Error handling patterns, debugging techniques, and troubleshooting guidelines for AL development +globs: ["*.al"] +alwaysApply: false +--- + +# AL Error Handling & Troubleshooting Rules + +Robust error handling and effective troubleshooting practices are essential for maintaining reliable Business Central applications. + +## Rule 1: Use TryFunctions for Error Handling + +### Intent +Implement proper error handling using TryFunctions to manage exceptions gracefully and provide meaningful user feedback. Use TryFunctions for error handling in scenarios where rollback is required, implement proper exception handling for external service calls, provide meaningful error messages to users, and log errors appropriately for debugging purposes. When generating code that might fail (external calls, data operations, calculations), implement appropriate TryFunction error handling and provide clear error messages. + +### Examples + +```al +// Good example - TryFunction with proper error handling and error labels +procedure ProcessPayment(Amount: Decimal): Boolean +var + PaymentService: Codeunit "Payment Service"; + ErrorText: Text; + PaymentProcessingFailedLbl: Label 'Payment processing failed: %1', Comment = '%1 = Error message'; + PaymentProcessingFailedTelemetryLbl: Label 'Payment processing failed', Locked = true; +begin + if not TryProcessPaymentInternal(Amount) then begin + ErrorText := GetLastErrorText(); + LogError(PaymentProcessingFailedTelemetryLbl, ErrorText); + Message(PaymentProcessingFailedLbl, ErrorText); + exit(false); + end; + + exit(true); +end; + +[TryFunction] +local procedure TryProcessPaymentInternal(Amount: Decimal) +var + PaymentService: Codeunit "Payment Service"; +begin + PaymentService.ProcessPayment(Amount); +end; +``` + +```al +// Bad example (avoid hardcoded error messages and unhandled errors) +procedure ProcessPayment(Amount: Decimal) +var + PaymentService: Codeunit "Payment Service"; +begin + // No error handling - will cause unhandled exceptions + // Also avoid hardcoded messages like this: + // Message('Payment could not be processed'); + PaymentService.ProcessPayment(Amount); +end; +``` + +## Rule 2: Use Error Labels for All Messages + +### Intent +All error messages, warnings, and user messages must use label variables instead of hardcoded text. This ensures proper localization support and maintainability. Define labels with appropriate comments for translators and use Locked = true for technical messages that should not be translated. + +### Examples + +```al +// Good example - Using error labels +procedure ValidateBusinessLogic(SalesHeader: Record "Sales Header") +var + Customer: Record Customer; + CustomerNotFoundErr: Label 'Customer %1 does not exist for sales document %2.', Comment = '%1 = Customer No., %2 = Sales Header No.'; + CustomerBlockedErr: Label 'Customer %1 is blocked (%2). Cannot process sales document %3.', Comment = '%1 = Customer No., %2 = Blocked reason, %3 = Sales Header No.'; + EmptyHeaderNoErr: Label 'Sales header number cannot be empty.'; +begin + if SalesHeader."No." = '' then + Error(EmptyHeaderNoErr); + + if not Customer.Get(SalesHeader."Sell-to Customer No.") then + Error(CustomerNotFoundErr, SalesHeader."Sell-to Customer No.", SalesHeader."No."); + + if Customer.Blocked <> Customer.Blocked::" " then + Error(CustomerBlockedErr, Customer."No.", Customer.Blocked, SalesHeader."No."); +end; +``` + +```al +// Bad example (avoid hardcoded error messages) +procedure ValidateBusinessLogic(SalesHeader: Record "Sales Header") +var + Customer: Record Customer; +begin + if not Customer.Get(SalesHeader."Sell-to Customer No.") then + Error('Customer not found'); // Hardcoded - avoid this + + if Customer.Blocked <> Customer.Blocked::" " then + Error('Customer blocked'); // Hardcoded - avoid this +end; +``` + +## Rule 3: Code Compilation and Correctness Priority + +### Intent +Generated AL code should prioritize correctness over immediate compilation. Code can fail to compile if AI suggests base functions or events that don't exist, or if variables in event subscriptions are incorrect. When this happens, leave space for manual fixes rather than changing the intended behavior. If you're confident the logic should work as suggested but there are naming or parameter issues, leave it for user correction rather than altering the business logic. + +### Examples + +```al +// Good example - Correct logic even if function names need verification +procedure HandleCustomerModification(var Customer: Record Customer) +var + CustomerValidation: Codeunit "Customer Validation"; // May need verification +begin + // Correct business logic - even if codeunit name needs adjustment + if not CustomerValidation.ValidateCustomerData(Customer) then + Error(ValidationFailedErr); + + Customer.Modify(true); +end; +``` + +```al +// Good example - Event subscription with correct intent +[EventSubscriber(ObjectType::Table, Database::Customer, OnAfterModifyEvent, '', false, false)] +local procedure OnAfterCustomerModify(var Rec: Record Customer; var xRec: Record Customer; RunTrigger: Boolean) +var + CustomerChangeLog: Codeunit "Customer Change Log"; // Function may need verification +begin + // Correct logic - even if codeunit or method names need adjustment + CustomerChangeLog.LogCustomerChange(Rec, xRec); +end; +``` + +## Rule 4: Custom Telemetry Implementation + +### Intent +Add custom telemetry for tracking business-critical operations, but only when explicitly requested by the user. Use Session.LogMessage for custom telemetry with appropriate verbosity levels and data classification. Include relevant custom dimensions for context and use proper telemetry scope for extension publishers. + +### Examples + +```al +// Good example - Custom telemetry (only when user explicitly requests it) +procedure PostSalesDocument(var SalesHeader: Record "Sales Header") +var + TelemetryCustomDimensions: Dictionary of [Text, Text]; + SalesDocPostedMsg: Label 'Sales document posted successfully', Locked = true; + SalesDocPostFailedMsg: Label 'Sales document posting failed', Locked = true; +begin + // Add context for telemetry + TelemetryCustomDimensions.Add('DocumentType', Format(SalesHeader."Document Type")); + TelemetryCustomDimensions.Add('CustomerNo', SalesHeader."Sell-to Customer No."); + + if TryPostSalesDocument(SalesHeader) then begin + // Log successful operation + Session.LogMessage('SAL001', SalesDocPostedMsg, + Verbosity::Normal, DataClassification::SystemMetadata, + TelemetryScope::ExtensionPublisher, TelemetryCustomDimensions); + end else begin + // Log failed operation with error details + TelemetryCustomDimensions.Add('ErrorText', GetLastErrorText()); + Session.LogMessage('SAL002', SalesDocPostFailedMsg, + Verbosity::Error, DataClassification::SystemMetadata, + TelemetryScope::ExtensionPublisher, TelemetryCustomDimensions); + end; +end; +``` \ No newline at end of file diff --git a/content/docs/vibe-coding/al-events.md b/content/docs/vibe-coding/al-events.md new file mode 100644 index 00000000..20afabfc --- /dev/null +++ b/content/docs/vibe-coding/al-events.md @@ -0,0 +1,120 @@ +--- +title: "Event-Driven Development Rules" +description: > + Guidelines for implementing event-driven patterns and extensibility in AL development +globs: ["*.al"] +alwaysApply: false +--- + +# Event-Driven Development Rules + +Event-driven development is fundamental to creating extensible and maintainable Business Central applications that follow the platform's architecture principles. + +## Rule 1: Use Events for Extensibility + +### Intent +Implement proper event patterns to enable extensibility without modifying base application code. Subscribe to relevant Business Central events (OnBeforeInsert, OnAfterModify, etc.), create integration events in your code for future extensibility, use extension objects or events for all changes to standard application objects. When implementing business logic, prioritize event subscribers and suggest appropriate event subscription patterns and integration event creation. + +### Examples + +```al +// Good example - Event subscriber implementation with Handler suffix +codeunit 50100 "Sales Document Events Handler" +{ + [EventSubscriber(ObjectType::Table, Database::"Sales Header", OnBeforeInsert, '', false, false)] + local procedure OnBeforeInsertSalesHeader(var Rec: Record "Sales Header"; RunTrigger: Boolean) + begin + // Custom validation logic + ValidateCustomFields(Rec); + end; +} +``` + +## Rule 2: Add Integration Events for Extensibility + +### Intent +Use integration events to provide better extensibility points and clearer API contracts for other developers. Create integration events at logical business process points, document integration event parameters and expected behavior, provide meaningful event names that describe the business context, and implement handled patterns to allow subscribers to control execution flow. When designing extensible code, suggest integration events at appropriate business logic points with clear documentation and meaningful names. + +### Examples + +```al +// Good example - Integration events with handled pattern +codeunit 50101 "Customer Management" +{ + procedure CreateCustomer(var Customer: Record Customer): Boolean + var + IsHandled: Boolean; + begin + OnBeforeCreateCustomer(Customer, IsHandled); + if IsHandled then + exit(true); + + if not Customer.Insert(true) then + exit(false); + + OnAfterCreateCustomer(Customer); + exit(true); + end; + + [IntegrationEvent(false, false)] + procedure OnBeforeCreateCustomer(var Customer: Record Customer; var IsHandled: Boolean) + begin + // Allow extensions to modify customer data before creation + // Set IsHandled to true to skip default processing + end; + + [IntegrationEvent(false, false)] + procedure OnAfterCreateCustomer(var Customer: Record Customer) + begin + // Allow extensions to perform additional actions after customer creation + end; +} +``` + +```al +// Extension subscribing to integration events with Handler suffix +codeunit 50102 "Customer Validation Handler" +{ + [EventSubscriber(ObjectType::Codeunit, Codeunit::"Customer Management", OnBeforeCreateCustomer, '', false, false)] + local procedure ValidateCustomerOnBeforeCreate(var Customer: Record Customer; var IsHandled: Boolean) + begin + // Custom validation logic + ValidateCustomerCreditLimit(Customer); + + // Optionally handle the event to skip default processing + if ShouldSkipDefaultProcessing(Customer) then + IsHandled := true; + end; +} +``` + +## Rule 3: Event Parameter Best Practices + +### Intent +Design event parameters that provide sufficient context while maintaining performance and usability. Pass record variables by reference when possible, include relevant context parameters, use meaningful parameter names, consider performance implications of parameter passing, and implement handled patterns where appropriate. When creating events, ensure parameters provide sufficient context for subscribers while maintaining good performance and use descriptive parameter names that clearly indicate their purpose. + +### Examples + +```al +// Good example - Well-designed event parameters with handled pattern +codeunit 50103 "Document Posting Events" +{ + [IntegrationEvent(false, false)] + procedure OnBeforePostDocument(var DocumentHeader: Record "Sales Header"; var DocumentLines: Record "Sales Line"; PostingDate: Date; var IsHandled: Boolean) + begin + // Comprehensive context for document posting + // - Document header and lines for full context + // - Posting date for temporal context + // - IsHandled flag for control flow + end; + + [IntegrationEvent(false, false)] + procedure OnAfterPostDocument(DocumentHeader: Record "Sales Header"; PostedDocumentNo: Code[20]; PostingResult: Boolean) + begin + // Results context after posting + // - Original document for reference + // - Posted document number for tracking + // - Success/failure indication + end; +} +``` \ No newline at end of file diff --git a/content/docs/vibe-coding/al-guidelines-rules.md b/content/docs/vibe-coding/al-guidelines-rules.md new file mode 100644 index 00000000..e01462bd --- /dev/null +++ b/content/docs/vibe-coding/al-guidelines-rules.md @@ -0,0 +1,58 @@ +--- +description: AL Guidelines - Comprehensive AI-optimized coding rules for Microsoft Dynamics 365 Business Central development +globs: ["*.al", "*.json", "app.json", "launch.json"] +alwaysApply: true +--- + +# AL Guidelines - Vibe Coding Rules + +You are an AI assistant designed to aid in AL development, particularly for Microsoft Dynamics 365 Business Central. Your role is to assist developers in writing efficient, maintainable code following established patterns and best practices. + +## Core Principles + +- Follow event-driven programming model; never modify standard application objects +- Use clear, meaningful names and maintain consistent code structure +- Prioritize performance optimization and proper error handling +- Focus on main application implementation by default +- Only generate test code when explicitly requested +- Maintain proper AL-Go workspace structure separation + +## Rule Categories + +The following rule sets provide comprehensive guidance for AL development: + +@al-code-style.md + +@al-naming-conventions.md + +@al-performance.md + +@al-error-handling.md + +@al-events.md + +@al-testing.md + +## Key Guidelines Summary + +- **File Naming**: Use `..al` pattern consistently +- **Code Style**: Use 2-space indentation and camelCase for variables, PascalCase for objects +- **Folder Structure**: Organize by feature (`src/feature/subfeature/`) not by object type +- **Performance**: Filter data early, use temporary tables, avoid unnecessary loops +- **Events**: Prefer integration events over direct modifications for extensibility +- **Testing**: Separate App and Test projects, generate tests only when requested +- **Error Handling**: Use TryFunctions, provide meaningful error messages, implement telemetry + +## AL-Go Workspace Structure + +When working in AL-Go environments: +- **App project**: Contains all application logic (tables, pages, codeunits, reports) +- **Test project**: Contains all test code and references App project as dependency +- **Never mix**: Application code stays in App, test code stays in Test project + +## AI Response Behavior + +- Provide concise, actionable advice with specific AL method references +- Always explain the reasoning behind recommendations +- Reference Business Central architecture patterns and established best practices +- Focus on practical implementation guidance that can be immediately applied \ No newline at end of file diff --git a/content/docs/vibe-coding/al-naming-conventions.md b/content/docs/vibe-coding/al-naming-conventions.md new file mode 100644 index 00000000..0de377f6 --- /dev/null +++ b/content/docs/vibe-coding/al-naming-conventions.md @@ -0,0 +1,133 @@ +--- +title: "Naming Conventions Rules" +description: > + Comprehensive naming conventions for AL files, objects, variables, and functions +globs: ["*.al"] +alwaysApply: true +--- + +# Naming Conventions Rules + +Consistent naming conventions improve code readability, maintainability, and help AI assistants understand code structure and intent. + +## Rule 1: Object Naming Conventions + +### Intent +Use consistent naming patterns for all AL objects to improve discoverability and maintain professional standards. Use PascalCase for object names (tables, pages, reports, codeunits) and meaningful, descriptive names that clearly indicate the object's purpose. Object names must not exceed 30 characters total, with a maximum of 26 characters for the name itself to reserve space for prefixes/affixes (3 characters + 1 space). + +### Examples + +```al +// Good examples (within 26 character limit) +table 50100 "Customer Ledger Entry" // 20 chars +page 50101 "Sales Invoice" // 13 chars +codeunit 50102 "Sales Invoice Posting" // 21 chars +report 50103 "Customer Statement" // 18 chars +``` + +```al +// Bad examples (avoid abbreviations, unclear names, or length violations) +table 50100 "CustLE" // Too abbreviated +page 50101 "SalesInv" // Too abbreviated +table 50104 "Very Long Customer Ledger Entry" // 32 chars - exceeds limit +codeunit 50102 "SIPoster" // Unclear abbreviation +``` + +## Rule 2: File Naming Conventions + +### Intent +Establish consistent file naming patterns that clearly identify object types and facilitate organized development. Use pattern `..al` and maintain consistency across all file names. Ensure file names are descriptive and match the AL object name within the files. + +### Examples + +```al +// Good examples +NoSeries.Page.al +NoSeries.Table.al +NoSeriesErrorsImpl.Codeunit.al +NoSeriesSetup.Codeunit.al +CustomerCard.Page.al +SalesHeader.Table.al +PostSalesInvoice.Codeunit.al +ItemLedgerEntry.Report.al +InventorySetup.PageExt.al +SalesHeader.TableExt.al + +// For implementations and interfaces +INoSeries.Interface.al +NoSeriesImpl.Codeunit.al + +// For test files +NoSeriesTests.Codeunit.al +SalesPostingTests.Codeunit.al +``` + +## Rule 3: Variable and Function Naming + +### Intent +Use consistent naming conventions for variables and functions to improve code readability. Use camelCase for variable and function names, descriptive names that clearly indicate purpose, and avoid abbreviations unless they are well-known business terms. Use consistent parameter naming in procedures. + +### Examples + +```al +// Good examples - Variables +var + customerLedgerEntry: Record "Cust. Ledger Entry"; + totalAmount: Decimal; + discountPercentage: Decimal; + isValidTransaction: Boolean; +``` + +```al +// Good examples - Functions +procedure calculateCustomerBalance(customerNo: Code[20]): Decimal +procedure validateSalesDocument(var salesHeader: Record "Sales Header") +procedure updateInventoryQuantity(itemNo: Code[20]; quantity: Decimal) +``` + +## Rule 4: Parameter Naming in Event Subscribers + +### Intent +Use meaningful parameter names in event subscribers to improve code clarity and maintainability. Use descriptive parameter names that clearly indicate their purpose, follow Business Central conventions for common parameter types, and maintain consistency across similar event subscribers. Avoid unclear generic names like "Rec" - use specific descriptive names. + +### Examples + +```al +// Good example - Descriptive parameter names +[EventSubscriber(ObjectType::Table, Database::"Sales Header", OnBeforeInsert, '', false, false)] +local procedure AddDefaultValuesOnBeforeInsertSalesHeader(var SalesHeader: Record "Sales Header"; RunTrigger: Boolean) +begin + // Event handling logic +end; + +[EventSubscriber(ObjectType::Table, Database::Customer, OnBeforeModify, '', false, false)] +local procedure CheckBalanceOnBeforeModifyCustomer(var Customer: Record Customer; var xCustomer: Record Customer) +begin + // Event handling logic +end; +``` + +## Rule 5: Interface and Implementation Naming + +### Intent +Clearly distinguish between interfaces and their implementations using consistent naming patterns. Prefix interfaces with "I" (e.g., `INoSeries`), use "Impl" suffix for implementation codeunits, and keep interface and implementation names closely related. Ensure names stay within the 26-character limit. + +### Examples + +```al +// Good examples (within character limits) +// Interface file: ICustomerService.Interface.al +interface ICustomerService +{ + procedure GetCustomerBalance(CustomerNo: Code[20]): Decimal; +} + +// Implementation file: CustomerServiceImpl.Codeunit.al +codeunit 50100 "Customer Service Impl" implements ICustomerService +{ + procedure GetCustomerBalance(CustomerNo: Code[20]): Decimal + begin + // Implementation logic + end; +} +``` \ No newline at end of file diff --git a/content/docs/vibe-coding/al-performance.md b/content/docs/vibe-coding/al-performance.md new file mode 100644 index 00000000..e9d9c156 --- /dev/null +++ b/content/docs/vibe-coding/al-performance.md @@ -0,0 +1,225 @@ +--- +title: "AL Performance Optimization Rules" +description: > + Performance optimization guidelines and best practices for AL development +globs: ["*.al"] +alwaysApply: true +--- + +# AL Performance Optimization Rules + +These rules focus on writing performant AL code that scales well and provides optimal user experience in Business Central environments. + +## AL Performance Guidelines Summary +- Always analyze performance impact when adding new features +- Optimize queries by filtering data as early as possible +- Avoid unnecessary loops; use set-based operations when possible +- Use SetLoadFields to minimize data retrieval +- Use temporary tables, dictionaries, or lists for temporary data storage + +## Rule 1: Early Data Filtering and Query Optimization + +### Intent +Optimize queries by filtering data as early as possible to reduce data transfer and processing overhead. Apply filters before processing records, use appropriate table keys and sorting, minimize the amount of data retrieved from the database, and use SetRange and SetFilter methods effectively. + +### Examples + +```al +// Good example - Early filtering +procedure GetCustomersByCity(CityFilter: Text): Integer +var + Customer: Record Customer; +begin + Customer.SetRange(City, CityFilter); + Customer.SetRange(Blocked, Customer.Blocked::" "); + if Customer.FindSet() then + repeat + // Process only filtered customers + until Customer.Next() = 0; + + exit(Customer.Count); +end; +``` + +```al +// Bad example (avoid processing all records) +procedure GetCustomersByCity(CityFilter: Text): Integer +var + Customer: Record Customer; + Count: Integer; +begin + if Customer.FindSet() then + repeat + // Processing all customers then filtering + if (Customer.City = CityFilter) and (Customer.Blocked = Customer.Blocked::" ") then + Count += 1; + until Customer.Next() = 0; + + exit(Count); +end; +``` + +## Rule 2: Use SetLoadFields for Optimal Data Retrieval + +### Intent +Use SetLoadFields to minimize data retrieval from the database by loading only the fields you need. Place SetLoadFields before the Get or Find operation, and include only the fields that will be used in your code. + +### Examples + +```al +// Good example - SetLoadFields with filtering +Item.SetRange("Third Party Item Exists", false); +Item.SetLoadFields("Item Category Code"); +Item.FindFirst(); +``` + +```al +// Bad example (avoid SetLoadFields after filtering) +Item.SetLoadFields("Item Category Code"); +Item.SetRange("Third Party Item Exists", false); +Item.FindFirst(); +``` + +## Rule 3: Use Temporary Tables, Dictionaries, and Lists for Performance + +### Intent +Leverage temporary tables, dictionaries, and lists to improve performance in read-heavy scenarios and complex data processing. Use temporary tables for structured record data, dictionaries for key-value pairs, and lists for simple collections that are only temporarily needed. + +### Examples + +```al +// Good example - Using temporary tables for structured data +procedure ProcessSalesData(var TempSalesLine: Record "Sales Line" temporary) +var + SalesLine: Record "Sales Line"; +begin + // Load data into temporary table once + if SalesLine.FindSet() then + repeat + TempSalesLine := SalesLine; + TempSalesLine.Insert(); + until SalesLine.Next() = 0; + + // Process temporary data multiple times without database hits + ProcessDiscounts(TempSalesLine); + CalculateTotals(TempSalesLine); + ValidateInventory(TempSalesLine); +end; +``` + +```al +// Good example - Using dictionaries for key-value temporary data +procedure CacheCustomerData() +var + Customer: Record Customer; + CustomerCache: Dictionary of [Code[20], Text]; +begin + if Customer.FindSet() then + repeat + CustomerCache.Add(Customer."No.", Customer.Name); + until Customer.Next() = 0; + + // Use cached data for lookups + ProcessOrdersWithCache(CustomerCache); +end; +``` + +```al +// Good example - Using lists for simple collections +procedure GetBlockedCustomers(): List of [Code[20]] +var + Customer: Record Customer; + BlockedCustomers: List of [Code[20]]; +begin + Customer.SetRange(Blocked, Customer.Blocked::All); + if Customer.FindSet() then + repeat + BlockedCustomers.Add(Customer."No."); + until Customer.Next() = 0; + + exit(BlockedCustomers); +end; +``` + +## Rule 4: Avoid Unnecessary Loops - Use Set-Based Operations + +### Intent +Minimize looping operations and favor set-based approaches when possible to improve performance. Use built-in aggregation methods (CalcSums, CalcFields), leverage SQL-based operations through AL, avoid nested loops when possible, and use batch operations for multiple record updates. + +### Examples + +```al +// Good example - Set-based operation +procedure GetTotalSalesAmount(CustomerNo: Code[20]): Decimal +var + CustLedgerEntry: Record "Cust. Ledger Entry"; +begin + CustLedgerEntry.SetRange("Customer No.", CustomerNo); + CustLedgerEntry.CalcSums(Amount); + exit(CustLedgerEntry.Amount); +end; +``` + +```al +// Bad example (avoid manual loops for aggregation) +procedure GetTotalSalesAmount(CustomerNo: Code[20]): Decimal +var + CustLedgerEntry: Record "Cust. Ledger Entry"; + TotalAmount: Decimal; +begin + CustLedgerEntry.SetRange("Customer No.", CustomerNo); + if CustLedgerEntry.FindSet() then + repeat + TotalAmount += CustLedgerEntry.Amount; + until CustLedgerEntry.Next() = 0; + + exit(TotalAmount); +end; +``` + +## Rule 5: Performance Impact Analysis + +### Intent +Always analyze and consider performance impact when adding new features or modifying existing code. While the AL compiler does not have direct access to performance profilers, you should implement performance-optimal code patterns from the start and consider scalability implications of code changes. + +### Examples + +```al +// Good example - Performance-conscious implementation +procedure UpdatePricesForItems(var Item: Record Item) +var + ItemCount: Integer; +begin + // Check data volume before processing + ItemCount := Item.Count(); + + if ItemCount > 1000 then begin + // Use batch processing for large datasets + UpdatePricesInBatches(Item); + end else begin + // Direct processing for smaller datasets + UpdatePricesDirectly(Item); + end; +end; +``` + +```al +// Good example - Batched modifications to minimize database writes +procedure UpdateCustomerStatistics(CustomerNo: Code[20]) +var + Customer: Record Customer; + TotalBalance: Decimal; + LastPaymentDate: Date; +begin + // Calculate all values first + CalculateCustomerTotals(CustomerNo, TotalBalance, LastPaymentDate); + + // Single database write with all changes + Customer.SetLoadFields("Balance (LCY)", "Last Payment Date"); + if Customer.Get(CustomerNo) then begin + Customer."Balance (LCY)" := TotalBalance; + Customer."Last Payment Date" := LastPaymentDate; + Customer.Modify(true); + end; +end; +``` \ No newline at end of file diff --git a/content/docs/vibe-coding/al-testing.md b/content/docs/vibe-coding/al-testing.md new file mode 100644 index 00000000..68bc9370 --- /dev/null +++ b/content/docs/vibe-coding/al-testing.md @@ -0,0 +1,195 @@ +--- +title: "AL Testing & Project Structure Rules" +description: > + AL-Go workspace structure, test generation guidelines, and project organization rules +globs: ["*.al", "app.json"] +alwaysApply: false +--- + +# AL Testing & Project Structure Rules + +These rules ensure proper project organization, test implementation, and workspace structure in AL-Go based development environments. + +## Rule 1: AL-Go Workspace Structure Guidelines + +### Intent +Establish clear separation between application code and test code in AL-Go workspace environments. + +- App project contains Tables, Pages, Codeunits, Reports, APIs, Enums, etc. +- Test project contains Test Codeunits, Test Pages, Mock objects, Test data. +- Each project has its own app.json with appropriate dependencies. +- Test project references the App project as a dependency. +- Use App project ONLY for main application implementation, use Test project ONLY for test implementation, never include test files in the main App folder, and never include application logic in the Test folder. +- When working in AL-Go workspace, always place files in the correct project based on their purpose. + +### Examples + +``` +// Good example - Proper AL-Go workspace structure +Repository/ +├── .AL-Go/ +├── .github/ +├── App/ +│ ├── src/ +│ │ ├── Setup/ +│ │ ├── Feature1/ +│ │ ├── Feature2/ +│ │ ├── APIs/ +│ ├── app.json +│ └── launch.json +├── Test/ +│ ├── src/ +│ │ ├── SetupTests/ +│ │ ├── Feature1Tests/ +│ │ ├── Feature2Tests/ +│ │ ├── IntegrationTests/ +│ ├── app.json +│ └── launch.json +└── al.code-workspace +``` + +## Rule 2: Test Generation Guidelines + +### Intent +Control when and how test code is generated to maintain focus on main application implementation. + +- DO NOT automatically generate test code unless explicitly requested +- Focus on main application implementation by default +- When user asks for implementation create only the main application objects +- Only generate test files when user specifically requests "Create tests for...", "Generate unit tests...", "Add test coverage...", or "Write tests..." +- If tests are requested, place them in the Test project following the folder structure where test files should mirror the App project structure but in the Test project +- Unless the user explicitly requests tests, focus only on main application implementation + +## Rule 3: Project Dependencies Configuration + +### Intent +Establish correct dependency relationships between App and Test projects. + +- App project app.json should NOT reference Test project +- Test project app.json MUST reference App project as dependency +- Test project should include testing frameworks (e.g., "Any", "Library Assert"), and each project maintains its own dependencies. +- When configuring project dependencies, ensure Test project references App project but never the reverse and include appropriate testing frameworks in Test project. + +### Examples + +```json +// Good example - Test project app.json +{ + "dependencies": [ + { + "id": "your-app-id", + "name": "Your App Name", + "publisher": "Your Publisher", + "version": "1.0.0.0" + }, + { + "id": "dd0be2ea-f733-4d65-bb34-a28f4624fb14", + "name": "Library Assert", + "publisher": "Microsoft", + "version": "20.0.0.0" + } + ] +} +``` + +## Rule 4: Unit Testing Best Practices + +### Intent +Write comprehensive unit tests that ensure reliability of business logic. + +- Write unit tests for all business logic +- Follow given/when/then structure for test naming +- Use Assert statements for validating critical conditions +- Create test data factories for consistent test setup +- Always try to use standard library codeunits to create data and post documents +- When creating tests, use descriptive names that follow given/when/then pattern and include comprehensive assertions to validate expected behavior. + +### Examples + +```al +// Good example - Well-structured unit test with standard library codeunits +codeunit 50200 "Customer Management Tests" +{ + Subtype = Test; + + var + Assert: Codeunit Assert; + LibrarySales: Codeunit "Library - Sales"; + LibraryInventory: Codeunit "Library - Inventory"; + LibraryRandom: Codeunit "Library - Random"; + LibraryERM: Codeunit "Library - ERM"; + + [Test] + procedure GivenValidCustomer_WhenCreatingCustomer_ThenCustomerIsCreated() + var + Customer: Record Customer; + CustomerManagement: Codeunit "Customer Management"; + CustomerNo: Code[20]; + begin + // Given - Valid customer data using library + LibrarySales.CreateCustomer(Customer); + Customer."Credit Limit (LCY)" := LibraryRandom.RandDec(10000, 2); + + // When - Creating customer + CustomerNo := CustomerManagement.CreateCustomer(Customer); + + // Then - Customer is created successfully + Assert.IsTrue(Customer.Get(CustomerNo), 'Customer should be created'); + Assert.AreEqual(Customer.Name, Customer.Name, 'Customer name should match'); + end; + + [Test] + procedure GivenSalesOrder_WhenPostingOrder_ThenInvoiceIsCreated() + var + SalesHeader: Record "Sales Header"; + SalesLine: Record "Sales Line"; + Item: Record Item; + PostedInvoiceNo: Code[20]; + begin + // Given - Sales order with library-created data + LibraryInventory.CreateItem(Item); + LibrarySales.CreateSalesHeader(SalesHeader, SalesHeader."Document Type"::Order, ''); + LibrarySales.CreateSalesLine(SalesLine, SalesHeader, SalesLine.Type::Item, Item."No.", LibraryRandom.RandInt(10)); + + // When - Posting sales order + PostedInvoiceNo := LibrarySales.PostSalesDocument(SalesHeader, true, true); + + // Then - Posted invoice exists + Assert.AreNotEqual('', PostedInvoiceNo, 'Posted invoice should be created'); + end; +} +``` + +## Rule 5: Feature-Based Test Organization + +### Intent +Organize test files to mirror the application structure while maintaining clear separation. + +- Test files should mirror App project structure in Test project +- Use same feature-based organization for tests +- Place shared test utilities in Common folder +- Maintain consistent naming patterns +- When creating test files, mirror the feature organization of the App project but place all tests in the Test project structure + +### Examples + +``` +// Good example - Mirrored test structure +App/src/ +├── NoSeries/ +│ ├── NoSeries.Table.al +│ └── NoSeries.Page.al +└── Sales/ + └── Invoice/ + └── SalesInvoice.Page.al + +Test/src/ +├── NoSeries/ +│ └── NoSeriesTests.Codeunit.al +├── Sales/ +│ └── Invoice/ +│ └── SalesInvoiceTests.Codeunit.al +└── Common/ + └── TestHelpers/ + └── TestDataFactory.Codeunit.al +``` \ No newline at end of file