- Add 6 MCP server documentation pages (bc-code-intelligence, al-dependency, azure-devops, serena, al-objid, clockify) - Add AI Coding Agents documentation (GitHub Copilot, Claude, Cursor) - Add Getting Started section (what is agentic coding, setup, prompting, best practices, limitations, glossary) - Add Getting More section (code review, documentation, telemetry examples) - Add Community Resources placeholders (articles, videos, tools) - Update Tools section to focus on MCP servers instead of VSCode extensions
368 lines
9.8 KiB
Markdown
368 lines
9.8 KiB
Markdown
---
|
|
title: "AI-Assisted Code Review"
|
|
linkTitle: "Code Review"
|
|
weight: 1
|
|
description: >
|
|
Learn how to use AI assistants to review AL code for quality, performance, and best practices
|
|
---
|
|
|
|
## Scenario
|
|
|
|
You've just finished implementing a new feature: a codeunit that processes sales orders and applies volume-based discounts. Before submitting for peer review, you want to use AI to catch potential issues.
|
|
|
|
## Goal
|
|
|
|
Use AI to identify:
|
|
- Potential bugs or logical errors
|
|
- Performance issues
|
|
- AL best practice violations
|
|
- Missing error handling
|
|
- Code quality improvements
|
|
|
|
## The Code to Review
|
|
|
|
Here's the codeunit we'll review:
|
|
|
|
```al
|
|
codeunit 50100 "Sales Order Discount Processor"
|
|
{
|
|
procedure ApplyVolumeDiscounts(var SalesHeader: Record "Sales Header")
|
|
var
|
|
SalesLine: Record "Sales Line";
|
|
TotalQuantity: Decimal;
|
|
DiscountPct: Decimal;
|
|
begin
|
|
TotalQuantity := 0;
|
|
SalesLine.SetRange("Document Type", SalesHeader."Document Type");
|
|
SalesLine.SetRange("Document No.", SalesHeader."No.");
|
|
if SalesLine.FindSet() then
|
|
repeat
|
|
TotalQuantity := TotalQuantity + SalesLine.Quantity;
|
|
until SalesLine.Next() = 0;
|
|
|
|
if TotalQuantity > 100 then
|
|
DiscountPct := 15
|
|
else if TotalQuantity > 50 then
|
|
DiscountPct := 10
|
|
else if TotalQuantity > 10 then
|
|
DiscountPct := 5;
|
|
|
|
SalesLine.Reset();
|
|
if SalesLine.FindSet() then
|
|
repeat
|
|
SalesLine."Line Discount %" := DiscountPct;
|
|
SalesLine.Modify();
|
|
until SalesLine.Next() = 0;
|
|
end;
|
|
}
|
|
```
|
|
|
|
## Step-by-Step Review Process
|
|
|
|
### Step 1: Initial Quality Review
|
|
|
|
**Prompt**:
|
|
```
|
|
Review this AL codeunit for potential bugs, code quality issues, and best practice violations.
|
|
|
|
[Paste the code above]
|
|
|
|
Specifically check for:
|
|
- Logical errors
|
|
- Missing error handling
|
|
- Variable declarations
|
|
- Commit/transaction issues
|
|
- Performance concerns
|
|
```
|
|
|
|
**Expected AI Findings**:
|
|
The AI should identify issues like:
|
|
- Missing `FindSet(true, false)` parameters for modification
|
|
- No error handling
|
|
- Multiple FindSet operations (inefficient)
|
|
- Hard-coded discount percentages
|
|
- No validation of input parameters
|
|
- Missing ModifyAll opportunity
|
|
- No telemetry or logging
|
|
|
|
### Step 2: Performance Analysis
|
|
|
|
**Prompt**:
|
|
```
|
|
Analyze the performance of this code, especially database operations.
|
|
What are potential performance issues? How can it be optimized?
|
|
|
|
[Include the code or reference it if in context]
|
|
```
|
|
|
|
**Expected AI Suggestions**:
|
|
- Combine the two FindSet loops into one
|
|
- Use ModifyAll instead of looping for discount application
|
|
- Consider using SetLoadFields for better performance
|
|
- Cache discount percentages instead of hard-coding
|
|
|
|
### Step 3: Best Practices Check
|
|
|
|
**Prompt**:
|
|
```
|
|
Check this code against AL coding best practices and Business Central development standards.
|
|
What improvements would you recommend?
|
|
```
|
|
|
|
**Expected AI Recommendations**:
|
|
- Add XML documentation
|
|
- Extract discount calculation to separate function
|
|
- Use constants for discount tiers
|
|
- Add error handling
|
|
- Implement proper transaction handling
|
|
- Add telemetry for monitoring
|
|
|
|
### Step 4: Request Improved Version
|
|
|
|
**Prompt**:
|
|
```
|
|
Rewrite this codeunit implementing all the improvements you suggested.
|
|
Include:
|
|
- XML documentation
|
|
- Error handling
|
|
- Performance optimizations
|
|
- Best practice compliance
|
|
- Telemetry/logging
|
|
- Discount tiers from setup table instead of hard-coded values
|
|
```
|
|
|
|
**Review the Generated Code**:
|
|
The AI should generate something like:
|
|
|
|
```al
|
|
/// <summary>
|
|
/// Processes sales orders and applies volume-based discounts
|
|
/// </summary>
|
|
codeunit 50100 "Sales Order Discount Processor"
|
|
{
|
|
/// <summary>
|
|
/// Applies volume-based discounts to a sales order
|
|
/// </summary>
|
|
/// <param name="SalesHeader">The sales header to process</param>
|
|
procedure ApplyVolumeDiscounts(var SalesHeader: Record "Sales Header")
|
|
var
|
|
SalesLine: Record "Sales Line";
|
|
TotalQuantity: Decimal;
|
|
DiscountPct: Decimal;
|
|
begin
|
|
if SalesHeader."No." = '' then
|
|
Error('Sales order number cannot be empty.');
|
|
|
|
TotalQuantity := CalculateTotalQuantity(SalesHeader);
|
|
DiscountPct := GetDiscountPercentage(TotalQuantity);
|
|
|
|
if DiscountPct > 0 then
|
|
ApplyDiscountToLines(SalesHeader, DiscountPct);
|
|
|
|
LogTelemetry(SalesHeader."No.", TotalQuantity, DiscountPct);
|
|
end;
|
|
|
|
local procedure CalculateTotalQuantity(SalesHeader: Record "Sales Header"): Decimal
|
|
var
|
|
SalesLine: Record "Sales Line";
|
|
TotalQty: Decimal;
|
|
begin
|
|
SalesLine.SetRange("Document Type", SalesHeader."Document Type");
|
|
SalesLine.SetRange("Document No.", SalesHeader."No.");
|
|
SalesLine.SetLoadFields(Quantity);
|
|
|
|
if SalesLine.FindSet() then
|
|
repeat
|
|
TotalQty += SalesLine.Quantity;
|
|
until SalesLine.Next() = 0;
|
|
|
|
exit(TotalQty);
|
|
end;
|
|
|
|
local procedure GetDiscountPercentage(TotalQuantity: Decimal): Decimal
|
|
var
|
|
VolumeDiscountSetup: Record "Volume Discount Setup"; // Assuming this table exists
|
|
begin
|
|
VolumeDiscountSetup.SetFilter("Minimum Quantity", '<=%1', TotalQuantity);
|
|
VolumeDiscountSetup.SetCurrentKey("Minimum Quantity");
|
|
VolumeDiscountSetup.Ascending(false);
|
|
|
|
if VolumeDiscountSetup.FindFirst() then
|
|
exit(VolumeDiscountSetup."Discount Percentage");
|
|
|
|
exit(0);
|
|
end;
|
|
|
|
local procedure ApplyDiscountToLines(SalesHeader: Record "Sales Header"; DiscountPct: Decimal)
|
|
var
|
|
SalesLine: Record "Sales Line";
|
|
begin
|
|
SalesLine.SetRange("Document Type", SalesHeader."Document Type");
|
|
SalesLine.SetRange("Document No.", SalesHeader."No.");
|
|
SalesLine.ModifyAll("Line Discount %", DiscountPct, true);
|
|
end;
|
|
|
|
local procedure LogTelemetry(DocumentNo: Code[20]; TotalQty: Decimal; DiscountPct: Decimal)
|
|
var
|
|
Telemetry: Codeunit Telemetry;
|
|
CustomDimensions: Dictionary of [Text, Text];
|
|
begin
|
|
CustomDimensions.Add('DocumentNo', DocumentNo);
|
|
CustomDimensions.Add('TotalQuantity', Format(TotalQty));
|
|
CustomDimensions.Add('DiscountPercentage', Format(DiscountPct));
|
|
|
|
Telemetry.LogMessage('0001', 'Volume discount applied', Verbosity::Normal,
|
|
DataClassification::SystemMetadata, TelemetryScope::All, CustomDimensions);
|
|
end;
|
|
}
|
|
```
|
|
|
|
## Review Points: What to Check
|
|
|
|
After AI generates the improved code, verify:
|
|
|
|
### ✅ Correctness
|
|
- [ ] Logic matches business requirements
|
|
- [ ] All edge cases handled
|
|
- [ ] No regression in functionality
|
|
- [ ] Error messages are clear
|
|
|
|
### ✅ Performance
|
|
- [ ] Efficient database queries
|
|
- [ ] Proper use of SetLoadFields
|
|
- [ ] ModifyAll used where appropriate
|
|
- [ ] No unnecessary loops
|
|
|
|
### ✅ Best Practices
|
|
- [ ] XML documentation complete
|
|
- [ ] Proper error handling
|
|
- [ ] Appropriate transaction handling
|
|
- [ ] Good function decomposition
|
|
|
|
### ✅ AL Specifics
|
|
- [ ] Correct AL syntax
|
|
- [ ] Proper use of BC APIs
|
|
- [ ] No deprecated features
|
|
- [ ] Appropriate data types
|
|
|
|
### ✅ Maintainability
|
|
- [ ] Clear, descriptive names
|
|
- [ ] Logical organization
|
|
- [ ] Easy to test
|
|
- [ ] Well documented
|
|
|
|
## Additional Review Prompts
|
|
|
|
### Security Review
|
|
```
|
|
Review this code for potential security issues:
|
|
- Input validation
|
|
- Authorization checks
|
|
- Data access controls
|
|
- Potential injection vulnerabilities
|
|
```
|
|
|
|
### Testability Review
|
|
```
|
|
Analyze this code for testability.
|
|
What makes it easy or hard to test?
|
|
How can it be restructured to be more testable?
|
|
```
|
|
|
|
### Documentation Review
|
|
```
|
|
Review the XML documentation for this code.
|
|
Is it complete? Clear? Helpful?
|
|
What additional documentation would be valuable?
|
|
```
|
|
|
|
## Common Issues AI Might Miss
|
|
|
|
Be aware AI might not catch:
|
|
|
|
1. **Business Logic Errors**
|
|
- AI doesn't know your specific discount rules
|
|
- Verify the logic matches actual requirements
|
|
|
|
2. **Integration Issues**
|
|
- AI doesn't know about other extensions
|
|
- Check for conflicts with existing code
|
|
|
|
3. **BC Version Compatibility**
|
|
- AI might use features not in your BC version
|
|
- Verify all APIs are available
|
|
|
|
4. **Organization Standards**
|
|
- AI doesn't know your specific standards
|
|
- Adapt to match your conventions
|
|
|
|
## Best Practices for AI Code Review
|
|
|
|
### 1. Use Specific Review Criteria
|
|
Instead of "review this code", specify what to look for:
|
|
```
|
|
Review for: performance, error handling, AL best practices, testability
|
|
```
|
|
|
|
### 2. Review in Layers
|
|
Don't try to review everything at once:
|
|
- First: Correctness and logic
|
|
- Second: Performance
|
|
- Third: Best practices
|
|
- Fourth: Documentation
|
|
|
|
### 3. Combine with Tools
|
|
Use AI review alongside:
|
|
- AL code analyzers
|
|
- Static analysis tools
|
|
- Peer review
|
|
- Testing
|
|
|
|
### 4. Iterate
|
|
Review, improve, review again:
|
|
```
|
|
Review the updated code. Are there any remaining issues?
|
|
```
|
|
|
|
### 5. Document Findings
|
|
Keep track of:
|
|
- Common issues AI finds
|
|
- Issues AI misses
|
|
- Effective review prompts
|
|
|
|
## Practice Exercise
|
|
|
|
Try reviewing this code with AI:
|
|
|
|
```al
|
|
procedure CalculateShippingCost(SalesHeader: Record "Sales Header"): Decimal
|
|
var
|
|
SalesLine: Record "Sales Line";
|
|
Weight: Decimal;
|
|
begin
|
|
Weight := 0;
|
|
SalesLine.SetRange("Document No.", SalesHeader."No.");
|
|
if SalesLine.Find('-') then
|
|
repeat
|
|
Weight := Weight + SalesLine.Quantity;
|
|
until SalesLine.Next() = 0;
|
|
|
|
if Weight < 10 then
|
|
exit(5.00)
|
|
else
|
|
exit(10.00);
|
|
end;
|
|
```
|
|
|
|
**Your Tasks**:
|
|
1. Ask AI to review for issues
|
|
2. Request performance improvements
|
|
3. Ask for best practice compliance
|
|
4. Get an improved version
|
|
5. Review the improved version yourself
|
|
|
|
## Next Steps
|
|
|
|
- Learn how to use AI for [generating documentation](../documentation)
|
|
- See how AI can help with [adding telemetry](../telemetry)
|
|
- Explore [refactoring legacy code](../refactoring) with AI assistance
|