当ブログではアフィリエイト広告を利用しています

生成AI(Generative AI)

AIエディタを使ったVibe codingの実際の開発事例(ユースケース)

AIエディタを使ったVibe codingの実際の開発事例(ユースケース)

Lightning Web Componentの開発例

要件:顧客管理ダッシュボードの作成

自然言語での要求:

"取引先の情報を表示し、関連する商談と取引先責任者を
タブ形式で切り替えて表示できるダッシュボードコンポーネントを作成したい。
リアルタイムでデータを更新し、Excel出力機能も必要。"

AIエディタによる段階的実装

ステップ1: 基本構造の生成

html
<!-- accountDashboard.html -->
<template>
    <lightning-card title="顧客管理ダッシュボード" icon-name="standard:account">
        <div slot="actions">
            <lightning-button 
                label="Excel出力" 
                onclick={exportToExcel}
                variant="brand">
            </lightning-button>
        </div>
        
        <lightning-tabset>
            <lightning-tab label="取引先情報" value="account">
                <c-account-detail account-id={accountId}></c-account-detail>
            </lightning-tab>
            
            <lightning-tab label="商談" value="opportunities">
                <c-opportunity-list account-id={accountId}></c-opportunity-list>
            </lightning-tab>
            
            <lightning-tab label="取引先責任者" value="contacts">
                <c-contact-list account-id={accountId}></c-contact-list>
            </lightning-tab>
        </lightning-tabset>
    </lightning-card>
</template>

ステップ2: JavaScriptコントローラーの実装

javascript
// accountDashboard.js
import { LightningElement, api, wire, track } from 'lwc';
import { refreshApex } from '@salesforce/apex';
import getAccountData from '@salesforce/apex/AccountDashboardController.getAccountData';
import exportAccountData from '@salesforce/apex/AccountDashboardController.exportAccountData';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';

export default class AccountDashboard extends LightningElement {
    @api accountId;
    @track accountData;
    wiredAccountData;

    @wire(getAccountData, { accountId: '$accountId' })
    wireAccountData(result) {
        this.wiredAccountData = result;
        if (result.data) {
            this.accountData = result.data;
        } else if (result.error) {
            this.showToast('エラー', result.error.body.message, 'error');
        }
    }

    async handleRefresh() {
        try {
            await refreshApex(this.wiredAccountData);
            this.showToast('成功', 'データが更新されました', 'success');
        } catch (error) {
            this.showToast('エラー', 'データの更新に失敗しました', 'error');
        }
    }

    async exportToExcel() {
        try {
            const result = await exportAccountData({ accountId: this.accountId });
            // ファイルダウンロード処理
            const link = document.createElement('a');
            link.href = 'data:application/vnd.ms-excel;base64,' + result;
            link.download = `account_${this.accountId}_data.xlsx`;
            link.click();
            
            this.showToast('成功', 'Excelファイルをダウンロードしました', 'success');
        } catch (error) {
            this.showToast('エラー', 'Excel出力に失敗しました', 'error');
        }
    }

    showToast(title, message, variant) {
        this.dispatchEvent(new ShowToastEvent({
            title: title,
            message: message,
            variant: variant
        }));
    }
}

ステップ3: Apexコントローラーの実装

apex
public with sharing class AccountDashboardController {
    
    @AuraEnabled(cacheable=true)
    public static AccountDataWrapper getAccountData(Id accountId) {
        try {
            if (!Schema.sObjectType.Account.isAccessible()) {
                throw new AuraHandledException('取引先への読み取り権限がありません');
            }

            Account account = [
                SELECT Id, Name, BillingAddress, Phone, Website, 
                       AnnualRevenue, NumberOfEmployees, Industry
                FROM Account 
                WHERE Id = :accountId 
                LIMIT 1
            ];

            List<Opportunity> opportunities = [
                SELECT Id, Name, StageName, Amount, CloseDate
                FROM Opportunity 
                WHERE AccountId = :accountId 
                ORDER BY CloseDate DESC
                LIMIT 10
            ];

            List<Contact> contacts = [
                SELECT Id, Name, Email, Phone, Title
                FROM Contact 
                WHERE AccountId = :accountId 
                ORDER BY LastModifiedDate DESC
                LIMIT 20
            ];

            return new AccountDataWrapper(account, opportunities, contacts);
            
        } catch (Exception e) {
            throw new AuraHandledException('データの取得に失敗しました: ' + e.getMessage());
        }
    }

    @AuraEnabled
    public static String exportAccountData(Id accountId) {
        try {
            AccountDataWrapper data = getAccountData(accountId);
            
            // Excel生成ロジック(簡略化)
            String csvData = generateCSVData(data);
            return EncodingUtil.base64Encode(Blob.valueOf(csvData));
            
        } catch (Exception e) {
            throw new AuraHandledException('Excel出力に失敗しました: ' + e.getMessage());
        }
    }

    private static String generateCSVData(AccountDataWrapper data) {
        List<String> csvLines = new List<String>();
        
        // ヘッダー
        csvLines.add('タイプ,名前,ステージ/タイトル,金額/メール,完了日/電話');
        
        // 取引先データ
        csvLines.add(String.format('取引先,{0},{1},{2},{3}', 
            new String[]{
                data.account.Name,
                data.account.Industry,
                String.valueOf(data.account.AnnualRevenue),
                data.account.Phone
            }));
        
        // 商談データ
        for (Opportunity opp : data.opportunities) {
            csvLines.add(String.format('商談,{0},{1},{2},{3}', 
                new String[]{
                    opp.Name,
                    opp.StageName,
                    String.valueOf(opp.Amount),
                    String.valueOf(opp.CloseDate)
                }));
        }
        
        return String.join(csvLines, '\n');
    }

    public class AccountDataWrapper {
        @AuraEnabled public Account account { get; set; }
        @AuraEnabled public List<Opportunity> opportunities { get; set; }
        @AuraEnabled public List<Contact> contacts { get; set; }
        
        public AccountDataWrapper(Account acc, List<Opportunity> opps, List<Contact> cons) {
            this.account = acc;
            this.opportunities = opps;
            this.contacts = cons;
        }
    }
}

Apex クラスの実装例

要件:複雑な承認プロセスの自動化

自然言語での要求:

"商談の金額に応じて、異なる承認者ルートを自動設定し、
承認状況をリアルタイムで追跡できるシステムを実装したい。
承認者の不在時は代理承認者に自動エスカレーションする機能も必要。"

AIエディタによる実装

apex
public class OpportunityApprovalManager {
    
    // 承認ルール設定
    private static final Map<Decimal, String> APPROVAL_RULES = new Map<Decimal, String>{
        100000 => 'MANAGER_APPROVAL',
        500000 => 'DIRECTOR_APPROVAL',
        1000000 => 'VP_APPROVAL',
        5000000 => 'EXECUTIVE_APPROVAL'
    };
    
    @InvocableMethod(label='承認プロセス開始')
    public static List<ApprovalResult> initiateApprovalProcess(List<ApprovalRequest> requests) {
        List<ApprovalResult> results = new List<ApprovalResult>();
        
        for (ApprovalRequest request : requests) {
            try {
                ApprovalResult result = processOpportunityApproval(request.opportunityId);
                results.add(result);
            } catch (Exception e) {
                ApprovalResult errorResult = new ApprovalResult();
                errorResult.success = false;
                errorResult.message = 'エラー: ' + e.getMessage();
                errorResult.opportunityId = request.opportunityId;
                results.add(errorResult);
            }
        }
        
        return results;
    }
    
    private static ApprovalResult processOpportunityApproval(Id opportunityId) {
        // 商談データの取得
        Opportunity opp = [
            SELECT Id, Name, Amount, OwnerId, StageName, Account.OwnerId
            FROM Opportunity 
            WHERE Id = :opportunityId 
            LIMIT 1
        ];
        
        // 承認レベルの決定
        String approvalLevel = determineApprovalLevel(opp.Amount);
        
        // 承認者の特定
        List<Id> approvers = getApprovers(opp, approvalLevel);
        
        // 承認プロセスの作成
        Approval.ProcessSubmitRequest req = new Approval.ProcessSubmitRequest();
        req.setComments('システムによる自動承認申請');
        req.setObjectId(opportunityId);
        req.setSubmitterId(UserInfo.getUserId());
        req.setProcessDefinitionNameOrId('OpportunityApprovalProcess');
        req.setSkipEntryCriteria(false);
        
        // 承認申請の実行
        Approval.ProcessResult result = Approval.process(req);
        
        // 結果の作成
        ApprovalResult approvalResult = new ApprovalResult();
        approvalResult.success = result.isSuccess();
        approvalResult.opportunityId = opportunityId;
        approvalResult.processInstanceId = result.getInstanceId();
        approvalResult.message = result.isSuccess() ? 
            '承認プロセスが開始されました' : 
            '承認プロセスの開始に失敗しました: ' + String.join(result.getErrors(), ', ');
        
        // 承認状況の追跡レコード作成
        createApprovalTracking(opp, approvers, result.getInstanceId());
        
        return approvalResult;
    }
    
    private static String determineApprovalLevel(Decimal amount) {
        if (amount == null) return 'MANAGER_APPROVAL';
        
        List<Decimal> thresholds = new List<Decimal>(APPROVAL_RULES.keySet());
        thresholds.sort();
        
        for (Decimal threshold : thresholds) {
            if (amount <= threshold) {
                return APPROVAL_RULES.get(threshold);
            }
        }
        
        return 'EXECUTIVE_APPROVAL';
    }
    
    private static List<Id> getApprovers(Opportunity opp, String approvalLevel) {
        List<Id> approvers = new List<Id>();
        
        // ユーザーの役職情報を取得
        User owner = [
            SELECT Id, ManagerId, UserRole.Name, Department
            FROM User 
            WHERE Id = :opp.OwnerId 
            LIMIT 1
        ];
        
        switch on approvalLevel {
            when 'MANAGER_APPROVAL' {
                if (owner.ManagerId != null) {
                    approvers.add(owner.ManagerId);
                }
            }
            when 'DIRECTOR_APPROVAL' {
                // 部門ディレクターを検索
                List<User> directors = [
                    SELECT Id FROM User 
                    WHERE UserRole.Name LIKE '%Director%' 
                    AND Department = :owner.Department 
                    AND IsActive = true
                    LIMIT 3
                ];
                for (User director : directors) {
                    approvers.add(director.Id);
                }
            }
            when 'VP_APPROVAL' {
                // VP以上の役職者を検索
                List<User> vps = [
                    SELECT Id FROM User 
                    WHERE (UserRole.Name LIKE '%VP%' OR UserRole.Name LIKE '%Vice President%')
                    AND IsActive = true
                    LIMIT 5
                ];
                for (User vp : vps) {
                    approvers.add(vp.Id);
                }
            }
            when 'EXECUTIVE_APPROVAL' {
                // 役員クラスを検索
                List<User> executives = [
                    SELECT Id FROM User 
                    WHERE UserRole.Name LIKE '%Executive%' 
                    OR UserRole.Name LIKE '%CEO%'
                    OR UserRole.Name LIKE '%President%'
                    AND IsActive = true
                    LIMIT 5
                ];
                for (User exec : executives) {
                    approvers.add(exec.Id);
                }
            }
        }
        
        return approvers;
    }
    
    private static void createApprovalTracking(Opportunity opp, List<Id> approvers, Id processInstanceId) {
        List<ApprovalTracking__c> trackingRecords = new List<ApprovalTracking__c>();
        
        for (Id approverId : approvers) {
            ApprovalTracking__c tracking = new ApprovalTracking__c();
            tracking.Opportunity__c = opp.Id;
            tracking.Approver__c = approverId;
            tracking.ProcessInstanceId__c = processInstanceId;
            tracking.Status__c = 'Pending';
            tracking.CreatedDate__c = System.now();
            trackingRecords.add(tracking);
        }
        
        if (!trackingRecords.isEmpty()) {
            insert trackingRecords;
        }
    }
    
    // 代理承認者への自動エスカレーション
    @future
    public static void checkForEscalation(Set<Id> processInstanceIds) {
        List<ProcessInstance> processes = [
            SELECT Id, Status, CreatedDate, TargetObjectId
            FROM ProcessInstance 
            WHERE Id IN :processInstanceIds
            AND Status = 'Pending'
        ];
        
        for (ProcessInstance process : processes) {
            // 24時間経過した場合のエスカレーション
            if (process.CreatedDate.addHours(24) < System.now()) {
                escalateToDelegate(process.Id);
            }
        }
    }
    
    private static void escalateToDelegate(Id processInstanceId) {
        // 現在の承認者を取得
        List<ProcessInstanceWorkitem> workitems = [
            SELECT Id, ActorId, ProcessInstanceId
            FROM ProcessInstanceWorkitem 
            WHERE ProcessInstanceId = :processInstanceId
        ];
        
        for (ProcessInstanceWorkitem workitem : workitems) {
            // 代理承認者を検索
            List<User> delegates = [
                SELECT Id FROM User 
                WHERE ManagerId = :workitem.ActorId 
                AND IsActive = true
                LIMIT 1
            ];
            
            if (!delegates.isEmpty()) {
                // 代理承認者に再アサイン
                Approval.ProcessWorkitemRequest req = new Approval.ProcessWorkitemRequest();
                req.setComments('自動エスカレーション: 代理承認者へのアサイン');
                req.setAction('Reassign');
                req.setWorkitemId(workitem.Id);
                req.setNewActorIds(new List<Id>{delegates[0].Id});
                
                Approval.process(req);
            }
        }
    }
    
    // データクラス
    public class ApprovalRequest {
        @InvocableVariable(required=true)
        public Id opportunityId;
    }
    
    public class ApprovalResult {
        @InvocableVariable
        public Boolean success;
        @InvocableVariable
        public String message;
        @InvocableVariable
        public Id opportunityId;
        @InvocableVariable
        public Id processInstanceId;
    }
}

テストコードの自動生成

AIエディタによるテストクラスの生成

apex
@isTest
public class OpportunityApprovalManagerTest {
    
    @TestSetup
    static void setupTestData() {
        // テスト用のユーザー作成
        Profile profile = [SELECT Id FROM Profile WHERE Name = 'Standard User' LIMIT 1];
        
        User manager = new User(
            FirstName = 'Test',
            LastName = 'Manager',
            Email = 'testmanager@example.com',
            Username = 'testmanager@example.com.test',
            Alias = 'tmgr',
            ProfileId = profile.Id,
            TimeZoneSidKey = 'Asia/Tokyo',
            LocaleSidKey = 'ja_JP',
            EmailEncodingKey = 'UTF-8',
            LanguageLocaleKey = 'ja'
        );
        insert manager;
        
        User salesUser = new User(
            FirstName = 'Test',
            LastName = 'Sales',
            Email = 'testsales@example.com',
            Username = 'testsales@example.com.test',
            Alias = 'tsales',
            ProfileId = profile.Id,
            ManagerId = manager.Id,
            TimeZoneSidKey = 'Asia/Tokyo',
            LocaleSidKey = 'ja_JP',
            EmailEncodingKey = 'UTF-8',
            LanguageLocaleKey = 'ja'
        );
        insert salesUser;
        
        // テスト用の取引先作成
        Account testAccount = new Account(
            Name = 'テスト取引先',
            OwnerId = salesUser.Id
        );
        insert testAccount;
        
        // テスト用の商談作成
        List<Opportunity> opportunities = new List<Opportunity>();
        
        // 小額商談(マネージャー承認)
        opportunities.add(new Opportunity(
            Name = 'テスト商談 - 小額',
            AccountId = testAccount.Id,
            Amount = 50000,
            CloseDate = Date.today().addDays(30),
            StageName = 'Prospecting',
            OwnerId = salesUser.Id
        ));
        
        // 中額商談(ディレクター承認)
        opportunities.add(new Opportunity(
            Name = 'テスト商談 - 中額',
            AccountId = testAccount.Id,
            Amount = 300000,
            CloseDate = Date.today().addDays(30),
            StageName = 'Prospecting',
            OwnerId = salesUser.Id
        ));
        
        // 高額商談(VP承認)
        opportunities.add(new Opportunity(
            Name = 'テスト商談 - 高額',
            AccountId = testAccount.Id,
            Amount = 800000,
            CloseDate = Date.today().addDays(30),
            StageName = 'Prospecting',
            OwnerId = salesUser.Id
        ));
        
        insert opportunities;
    }
    
    @isTest
    static void testManagerApprovalProcess() {
        Opportunity smallOpp = [
            SELECT Id FROM Opportunity 
            WHERE Name = 'テスト商談 - 小額' 
            LIMIT 1
        ];
        
        Test.startTest();
        
        List<OpportunityApprovalManager.ApprovalRequest> requests = 
            new List<OpportunityApprovalManager.ApprovalRequest>();
        OpportunityApprovalManager.ApprovalRequest request = 
            new OpportunityApprovalManager.ApprovalRequest();
        request.opportunityId = smallOpp.Id;
        requests.add(request);
        
        List<OpportunityApprovalManager.ApprovalResult> results = 
            OpportunityApprovalManager.initiateApprovalProcess(requests);
        
        Test.stopTest();
        
        // 結果の検証
        System.assertEquals(1, results.size(), '結果が1件返されること');
        System.assertEquals(true, results[0].success, '承認プロセスが成功すること');
        System.assertEquals(smallOpp.Id, results[0].opportunityId, '商談IDが正しく設定されること');
        
        // 追跡レコードの確認
        List<ApprovalTracking__c> trackings = [
            SELECT Id, Status__c 
            FROM ApprovalTracking__c 
            WHERE Opportunity__c = :smallOpp.Id
        ];
        System.assertNotEquals(0, trackings.size(), '追跡レコードが作成されること');
    }
    
    @isTest
    static void testDirectorApprovalProcess() {
        Opportunity mediumOpp = [
            SELECT Id FROM Opportunity 
            WHERE Name = 'テスト商談 - 中額' 
            LIMIT 1
        ];
        
        Test.startTest();
        
        List<OpportunityApprovalManager.ApprovalRequest> requests = 
            new List<OpportunityApprovalManager.ApprovalRequest>();
        OpportunityApprovalManager.ApprovalRequest request = 
            new OpportunityApprovalManager.ApprovalRequest();
        request.opportunityId = mediumOpp.Id;
        requests.add(request);
        
        List<OpportunityApprovalManager.ApprovalResult> results = 
            OpportunityApprovalManager.initiateApprovalProcess(requests);
        
        Test.stopTest();
        
        // 結果の検証
        System.assertEquals(1, results.size(), '結果が1件返されること');
        System.assertEquals(mediumOpp.Id, results[0].opportunityId, '商談IDが正しく設定されること');
    }
    
    @isTest
    static void testVPApprovalProcess() {
        Opportunity largeOpp = [
            SELECT Id FROM Opportunity 
            WHERE Name = 'テスト商談 - 高額' 
            LIMIT 1
        ];
        
        Test.startTest();
        
        List<OpportunityApprovalManager.ApprovalRequest> requests = 
            new List<OpportunityApprovalManager.ApprovalRequest>();
        OpportunityApprovalManager.ApprovalRequest request = 
            new OpportunityApprovalManager.ApprovalRequest();
        request.opportunityId = largeOpp.Id;
        requests.add(request);
        
        List<OpportunityApprovalManager.ApprovalResult> results = 
            OpportunityApprovalManager.initiateApprovalProcess(requests);
        
        Test.stopTest();
        
        // 結果の検証
        System.assertEquals(1, results.size(), '結果が1件返されること');
        System.assertEquals(largeOpp.Id, results[0].opportunityId, '商談IDが正しく設定されること');
    }
    
    @isTest
    static void testErrorHandling() {
        Test.startTest();
        
        List<OpportunityApprovalManager.ApprovalRequest> requests = 
            new List<OpportunityApprovalManager.ApprovalRequest>();
        OpportunityApprovalManager.ApprovalRequest request = 
            new OpportunityApprovalManager.ApprovalRequest();
        request.opportunityId = null; // 不正なID
        requests.add(request);
        
        List<OpportunityApprovalManager.ApprovalResult> results = 
            OpportunityApprovalManager.initiateApprovalProcess(requests);
        
        Test.stopTest();
        
        // エラーハンドリングの検証
        System.assertEquals(1, results.size(), '結果が1件返されること');
        System.assertEquals(false, results[0].success, 'エラーが正しく処理されること');
        System.assert(results[0].message.contains('エラー'), 'エラーメッセージが設定されること');
    }
    
    @isTest
    static void testBulkProcessing() {
        List<Opportunity> allOpps = [SELECT Id FROM Opportunity];
        
        Test.startTest();
        
        List<OpportunityApprovalManager.ApprovalRequest> requests = 
            new List<OpportunityApprovalManager.ApprovalRequest>();
        
        for (Opportunity opp : allOpps) {
            OpportunityApprovalManager.ApprovalRequest request = 
                new OpportunityApprovalManager.ApprovalRequest();
            request.opportunityId = opp.Id;
            requests.add(request);
        }
        
        List<OpportunityApprovalManager.ApprovalResult> results = 
            OpportunityApprovalManager.initiateApprovalProcess(requests);
        
        Test.stopTest();
        
        // バルク処理の検証
        System.assertEquals(allOpps.size(), results.size(), '全ての商談が処理されること');
        
        for (OpportunityApprovalManager.ApprovalResult result : results) {
            System.assertNotEquals(null, result.opportunityId, '商談IDが設定されること');
        }
    }
}

6. 注意点と課題

セキュリティ考慮事項

AIエディタ使用時のセキュリティリスク

1. 機密情報の漏洩リスク AIエディタにコードを送信する際、顧客情報や機密データが含まれる可能性があります。以下の対策が必要です:

  • 実際の顧客データを含むコードをAIエディタに送信しない
  • テストデータやサンプルデータを使用してコード生成を行う
  • 社内のデータ分類ポリシーに従った利用ガイドラインの策定
  • AIエディタの利用ログの監査と定期的なレビュー

2. 生成されたコードのセキュリティ脆弱性 AIが生成したコードには、セキュリティ上の問題が含まれる可能性があります:

apex
// 危険な例:SQLインジェクション脆弱性
public static List<Account> searchAccounts(String searchTerm) {
    // AIが生成した危険なコード例
    String query = 'SELECT Id, Name FROM Account WHERE Name LIKE \'%' + searchTerm + '%\'';
    return Database.query(query); // SQLインジェクション脆弱性
}

// 安全な実装例
public static List<Account> searchAccountsSafe(String searchTerm) {
    String safeSearchTerm = '%' + String.escapeSingleQuotes(searchTerm) + '%';
    return [SELECT Id, Name FROM Account WHERE Name LIKE :safeSearchTerm];
}

3. 権限とアクセス制御 AIが生成するコードが適切な権限チェックを含んでいるか確認が必要です:

apex
// 権限チェックを含む安全なコード例
public static List<Account> getAccounts() {
    // オブジェクトレベルの権限チェック
    if (!Schema.sObjectType.Account.isAccessible()) {
        throw new AuraHandledException('取引先への読み取り権限がありません');
    }
    
    // フィールドレベルの権限チェック
    if (!Schema.sObjectType.Account.fields.Name.isAccessible()) {
        throw new AuraHandledException('取引先名フィールドへの読み取り権限がありません');
    }
    
    return [SELECT Id, Name FROM Account WITH SECURITY_ENFORCED LIMIT 100];
}

コード品質の担保方法

1. AIによるコード生成後の必須チェック項目

Salesforce固有の制約チェック

  • ガバナーリミット(SOQL実行回数、DML操作回数、ヒープサイズなど)への対応
  • バルク処理の実装
  • 適切な例外処理の実装
  • トランザクション管理の考慮
apex
// 良い例:ガバナーリミットを考慮したコード
public class ContactProcessor {
    public static void processContacts(List<Contact> contacts) {
        List<Contact> contactsToUpdate = new List<Contact>();
        
        // バルク処理でSOQL実行回数を最小化
        Map<Id, Account> accountMap = new Map<Id, Account>([
            SELECT Id, Industry 
            FROM Account 
            WHERE Id IN (SELECT AccountId FROM Contact WHERE Id IN :contacts)
        ]);
        
        for (Contact contact : contacts) {
            if (accountMap.containsKey(contact.AccountId)) {
                contact.Industry__c = accountMap.get(contact.AccountId).Industry;
                contactsToUpdate.add(contact);
            }
        }
        
        // バルクDML操作
        if (!contactsToUpdate.isEmpty()) {
            try {
                Database.update(contactsToUpdate, false);
            } catch (DmlException e) {
                System.debug('DML Error: ' + e.getMessage());
                // 適切なエラーハンドリング
            }
        }
    }
}

2. 静的コード解析ツールの活用

  • PMD for Salesforce
  • SonarQube Salesforce Plugin
  • Salesforce Code Analyzer

3. コードレビュープロセスの強化 AIが生成したコードについては、特に以下の観点でのレビューを強化:

  • ビジネスロジックの正確性
  • パフォーマンスの最適化
  • セキュリティ要件の満足
  • 保守性とテスタビリティ

チーム開発での運用ポイント

1. AIエディタ利用のガイドライン策定

利用ルールの明文化

markdown
## AIエディタ利用ガイドライン

### 基本方針
1. 本番データを含むコードをAIエディタに送信しない
2. 生成されたコードは必ず動作確認とセキュリティチェックを行う
3. AIの提案をそのまま採用せず、チームの規約に合わせて調整する

### 利用可能な範囲
- ボイラープレートコードの生成
- テストコードの初期実装
- リファクタリングの提案
- ドキュメンテーションの作成

### 利用禁止事項
- 本番環境の機密データを含むコードの送信
- 未検証のコードの直接的な本番環境への適用
- ライセンスが不明なコードの利用

2. 品質基準の統一

コード品質チェックリスト

  • Salesforceガバナーリミットへの対応
  • 適切な例外処理の実装
  • セキュリティ要件の満足
  • テストカバレッジ75%以上
  • コメントとドキュメンテーションの充実
  • チームのコーディング規約への準拠

3. 知識共有とベストプラクティスの蓄積

効果的なプロンプトの共有 チーム内で効果的だったプロンプトや手法を共有し、組織全体のVibe Coding能力を向上させます。

markdown
## 有効なプロンプト例集

### Apexクラス生成
「Salesforceのガバナーリミットを考慮し、バルク処理に対応した
[機能名]のApexクラスを作成してください。エラーハンドリングと
ログ出力も含めて実装してください。」

### テストクラス生成
「先ほど作成したApexクラスに対して、以下の条件を満たす
テストクラスを作成してください:
- 正常系・異常系の両方をテスト
- バルク処理のテスト
- 75%以上のカバレッジ確保」

4. 継続的な改善プロセス

定期的な振り返り

  • 月次でのAIエディタ利用状況のレビュー
  • 問題点や改善点の特定と対策の検討
  • 新機能や新しい手法の評価と導入検討

メトリクスの計測

  • 開発速度の向上度
  • コード品質の指標(バグ発生率、レビュー指摘事項数)
  • 開発者の満足度
  • 学習コストの変化

関連記事

2025/6/22

AIエディタを使ったVibe CordingでSalesforceの開発を行う【①用語の理解とツールの説明】

Vibe Codingとは何か(感覚的・直感的なコーディング手法) Vibe Coding(バイブコーディング)とは、従来の詳細な設計書やドキュメントベースの開発から脱却し、開発者の直感と感覚、そしてAIの支援を活用した新しいコーディング手法です。 開発者が「こんな機能を作りたい」という漠然としたアイデアや要求を、AIエディタとの対話を通じて具体的なコードに変換していくため、完璧な設計よりもスピードや柔軟性を重視しており、Salesforceの開発と非常に相性がよい手法となります。 サポーターさんVibe ...

ReadMore

2025/6/22

AIエディタを使ったVibe codingの実際の開発事例(ユースケース)

AIエディタを使ったVibe codingの実際の開発事例(ユースケース) Lightning Web Componentの開発例 要件:顧客管理ダッシュボードの作成 自然言語での要求: "取引先の情報を表示し、関連する商談と取引先責任者を タブ形式で切り替えて表示できるダッシュボードコンポーネントを作成したい。 リアルタイムでデータを更新し、Excel出力機能も必要。" AIエディタによる段階的実装 ステップ1: 基本構造の生成 html <!-- accountDashboard.html -- ...

ReadMore

napkin.ai

2025/6/8

【2025年最新】Napkin AI完全ガイド-生成AIを使って効率的に資料やスライド用の文章や図解を作成しよう

このアイキャッチ画像もnapkin.aiで自動生成されたものです。 【2025年最新】Napkin AI完全ガイド テキストから文章や図解を用いたスライドを自動生成する革新的AIツール 図解生成時間:5秒 時間短縮効果:90% 図解タイプ:10+ 生成AIの進化により、私たちの働き方は日々変化しています。その中でも特に注目されているのが、テキストから美しい図解を自動生成するNapkin AIです。プレゼンテーション資料の作成に時間を取られがちなビジネスパーソンにとって、このツールは真の業務効率化をもたらす ...

ReadMore

2025/6/3

ビジネスを変革する生成AI「ジェネレーティブAI(Generative AI)」活用ガイド

はじめに 私が初めて本サイトで以下のChatGPTの記事を公開したのが、2023年2月21日でした。当時から衝撃を受けて、数年でシステム開発は生成AIに置き換わると想像していましたが、ほぼ予想通りの展開になってきました。 ここまでできる今話題のAI(ChatGPT)を使ったSaleforce開発(自動コード生成) 今回は、今話題の文章生成AI(ChatGPT)を使ってSalesforceの開発が現状どこまでできるのか検証してみた結果の記事となります。 過去にAIの機能を使ったことがある人は、「結局AIとい ...

ReadMore

2025/6/8

生成AIとは?初心者向け完全ガイド:基本から活用方法まで徹底解説

はじめに 「生成AI(ジェネレーティブAI)という言葉を最近よく耳にするけれど、実際にどんなものなのか分からない。ChatGPTは知っているけれど、生成AI全体について詳しく知りたい。そんな方のために、この記事では生成AIの基本から最新動向まで、初心者にもわかりやすく解説します。 読み終える頃には、生成AIとは何なのか、私たちの生活やビジネスにどのような影響を与えるのかが理解できるはずです。 生成AIとは?基本を理解しよう 生成AIの定義 生成AI(ジェネレーティブAI)とは、新しいコンテンツを創造するこ ...

ReadMore

-生成AI(Generative AI)
-, , , , , ,