-
Notifications
You must be signed in to change notification settings - Fork 1
Add GitHub Copilot review instructions #11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,397 @@ | ||
| # GitHub Copilot Review Instructions | ||
|
|
||
| ## Project Context | ||
|
|
||
| This is a Twilio Segment Destination Function template written in JavaScript (Node.js 18). The function processes customer event data from Segment and executes custom logic for each event type. | ||
|
|
||
| ## Code Review Philosophy | ||
|
|
||
| Review code like a maintainer who cares about production stability, developer experience, and long-term maintainability. Focus on catching issues that would cause runtime failures, security vulnerabilities, or make the codebase harder to maintain. | ||
|
|
||
| - Only comment when you have HIGH CONFIDENCE (>80%) that an issue exists | ||
| - Be concise: one sentence per comment when possible | ||
| - Focus on actionable feedback, not observations | ||
| - When reviewing text, only comment on clarity issues if the text is genuinely confusing or could lead to errors. | ||
|
|
||
| ## Repository-Specific Patterns | ||
|
|
||
| ### Twilio Segment Function Structure | ||
|
|
||
| - Main function is `onTrack(event, settings)` in `src/index.js` | ||
| - Must be an `async function` that handles `SegmentTrackEvent` and `FunctionSettings` parameters | ||
| - Functions are deployed to Segment's serverless runtime (no traditional server environment) | ||
| - Code runs in a sandboxed environment with limited access to Node.js APIs | ||
|
|
||
| ### Conditional Exports for Testing | ||
|
|
||
| ```javascript | ||
| // CORRECT: Export only in test environment | ||
| if (process.env.NODE_DEV === 'TEST') { | ||
| module.exports = { onTrack }; | ||
| } | ||
|
|
||
| // INCORRECT: Always exporting breaks Segment runtime | ||
| module.exports = { onTrack }; // ❌ Don't do this | ||
| ``` | ||
|
|
||
| ### Required Dependencies | ||
|
|
||
| All external dependencies must be declared in `package.json`. The Segment runtime does NOT have built-in access to npm packages beyond what's explicitly included. | ||
|
|
||
| ## Code Review Checklist | ||
|
|
||
| ### 1. Functional Requirements | ||
|
|
||
| **✅ Check for:** | ||
|
|
||
| - The `onTrack` function signature matches: `async function onTrack(event, settings)` | ||
| - All async operations use `await` (not callbacks or bare promises) | ||
| - Function properly handles the event payload structure | ||
| - Settings are accessed correctly from the `settings` parameter | ||
|
|
||
| **❌ Reject if:** | ||
|
|
||
| - Using callbacks instead of async/await | ||
| - Function is not async but performs asynchronous operations | ||
| - Direct use of `process.env` for configuration (should use `settings` parameter) | ||
|
|
||
| ### 2. Error Handling | ||
|
|
||
| **✅ Check for:** | ||
|
|
||
| - Try-catch blocks around external API calls | ||
| - Graceful degradation when non-critical operations fail | ||
| - Proper error logging with sufficient context | ||
| - Retry logic for transient failures (using `fetch-retry`) | ||
|
|
||
| **❌ Reject if:** | ||
|
|
||
| - Unhandled promise rejections | ||
| - Silent failure (catching errors without logging) | ||
| - Generic error messages without context | ||
| - Throwing errors that would crash the function without recovery | ||
|
|
||
| **Example:** | ||
|
|
||
| ```javascript | ||
| // CORRECT: Proper error handling | ||
| async function onTrack(event, settings) { | ||
| try { | ||
| const response = await fetch(settings.apiUrl, { | ||
| method: 'POST', | ||
| body: JSON.stringify(event), | ||
| headers: { 'Content-Type': 'application/json' } | ||
| }); | ||
|
|
||
| if (!response.ok) { | ||
| console.error(`API error: ${response.status} - ${response.statusText}`); | ||
| return; // Graceful degradation | ||
| } | ||
|
|
||
| console.log('Event sent successfully'); | ||
| } catch (error) { | ||
| console.error('Failed to send event:', error.message, { | ||
| eventId: event.messageId | ||
| }); | ||
| // Don't throw - function should complete even if external service fails | ||
| } | ||
| } | ||
|
|
||
| // INCORRECT: Poor error handling | ||
| async function onTrack(event, settings) { | ||
| const response = await fetch(settings.apiUrl); // ❌ No error handling | ||
| console.log('Done'); // ❌ Doesn't check response status | ||
| } | ||
| ``` | ||
|
|
||
| ### 3. Performance and Resource Usage | ||
|
|
||
| **✅ Check for:** | ||
|
|
||
| - Efficient use of Array methods (map, filter, reduce) instead of for loops | ||
| - Appropriate use of caching for repeated calculations | ||
| - Batching of API calls when possible | ||
| - Reasonable timeout values for external requests | ||
|
|
||
| **❌ Reject if:** | ||
|
|
||
| - Using for loops (violates project style guide) | ||
JasonSooter marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| - Synchronous blocking operations (like `fs.readFileSync`) | ||
| - Excessive memory allocation (large data structures) | ||
| - Missing timeout configuration on fetch requests | ||
| - N+1 query patterns (making API calls in loops) | ||
|
|
||
| **Example:** | ||
|
|
||
| ```javascript | ||
| // CORRECT: Efficient Array methods | ||
| const validEvents = events.filter(event => event.userId); | ||
| const userIds = validEvents.map(event => event.userId); | ||
|
|
||
| // INCORRECT: Using for loops | ||
| const validEvents = []; // ❌ Don't use for loops | ||
| for (let i = 0; i < events.length; i++) { | ||
| if (events[i].userId) { | ||
| validEvents.push(events[i]); | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| ### 4. Testing Requirements | ||
|
|
||
| **✅ Check for:** | ||
|
|
||
| - Every new function has corresponding unit tests in `src/index.test.js` | ||
| - Tests use proper Jest patterns (describe, it, expect) | ||
| - Mocking of external dependencies (fetch, etc.) | ||
| - Tests verify both success and error cases | ||
| - `process.env['NODE_DEV'] = 'TEST'` set before importing functions | ||
| - Console methods properly mocked to avoid noisy test output | ||
|
|
||
| **❌ Reject if:** | ||
|
|
||
| - New functions added without tests | ||
| - Tests that make real network requests | ||
| - Missing error case testing | ||
| - Tests that don't use `expect.assertions(n)` for async tests | ||
| - Forgotten to mock console methods | ||
|
|
||
JasonSooter marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| **Example:** | ||
|
|
||
| ```javascript | ||
| // CORRECT: Proper test structure | ||
| process.env['NODE_DEV'] = 'TEST'; | ||
JasonSooter marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| const { onTrack } = require('./index.js'); | ||
|
|
||
| jest.spyOn(global.console, 'log').mockImplementation(); | ||
| jest.spyOn(global.console, 'error').mockImplementation(); | ||
|
|
||
| describe('onTrack', () => { | ||
| beforeEach(() => { | ||
| fetch.resetMocks(); | ||
| }); | ||
|
|
||
| it('should send event to API successfully', async () => { | ||
| expect.assertions(2); | ||
| fetch.mockResponseOnce(JSON.stringify({ success: true })); | ||
|
|
||
| await onTrack({ userId: '123' }, { apiUrl: 'https://api.example.com' }); | ||
|
|
||
| expect(fetch).toHaveBeenCalledTimes(1); | ||
| expect(console.log).toHaveBeenCalledWith('Event sent successfully'); | ||
| }); | ||
|
|
||
| it('should handle API errors gracefully', async () => { | ||
| expect.assertions(1); | ||
| fetch.mockReject(new Error('Network error')); | ||
|
|
||
| await onTrack({ userId: '123' }, { apiUrl: 'https://api.example.com' }); | ||
|
|
||
| expect(console.error).toHaveBeenCalled(); | ||
| }); | ||
| }); | ||
| ``` | ||
|
|
||
| ### 5. Security Concerns | ||
|
|
||
| **✅ Check for:** | ||
|
|
||
| - No hardcoded credentials (use `settings` parameter) | ||
| - Proper handling of sensitive data in logs | ||
| - Input validation for event data | ||
| - Safe URL construction (no string concatenation with user input) | ||
| - HTTPS for all external API calls | ||
|
|
||
| **❌ Reject if:** | ||
|
|
||
| - Hardcoded API keys, tokens, or secrets | ||
| - Logging sensitive PII without redaction | ||
| - SQL injection vulnerabilities (if using databases) | ||
| - Command injection vulnerabilities | ||
| - XSS vulnerabilities in any HTML generation | ||
| - Unsafe eval() or Function() constructor usage | ||
|
|
||
| **Example:** | ||
|
|
||
| ```javascript | ||
| // CORRECT: Safe credential handling | ||
| async function onTrack(event, settings) { | ||
| const response = await fetch(settings.apiUrl, { | ||
| headers: { | ||
| Authorization: `Bearer ${settings.apiToken}` // ✅ From settings | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| // INCORRECT: Hardcoded credentials | ||
| async function onTrack(event, settings) { | ||
| const response = await fetch('https://api.example.com', { | ||
| headers: { | ||
| Authorization: 'Bearer sk-1234567890' // ❌ Hardcoded secret | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| // CORRECT: Safe logging (redact PII) | ||
| console.log('Processing event', { userId: event.userId, type: event.type }); | ||
|
|
||
| // INCORRECT: Logging sensitive data | ||
| console.log('Event:', event); // ❌ May contain PII like email, phone, etc. | ||
| ``` | ||
|
|
||
| ### 6. Code Style and Maintainability | ||
|
|
||
| **✅ Check for:** | ||
|
|
||
| - Descriptive variable and function names | ||
| - Functional programming principles (immutability, pure functions) | ||
| - Comments only where logic is not self-evident | ||
| - Consistent use of ESLint and Prettier formatting | ||
| - Native Fetch API usage (not Axios or other HTTP libraries) | ||
| - ES6+ syntax (const/let, arrow functions, destructuring, template literals) | ||
|
|
||
| **❌ Reject if:** | ||
|
|
||
| - Cryptic variable names (x, temp, data123) | ||
| - Global variables | ||
| - Using `var` instead of `const`/`let` | ||
| - Installing Axios or other HTTP libraries (use native fetch) | ||
| - Mutating function parameters | ||
| - Unnecessary comments explaining obvious code | ||
JasonSooter marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| **Example:** | ||
|
|
||
| ```javascript | ||
| // CORRECT: Clean, functional style | ||
| async function onTrack(event, settings) { | ||
| const { userId, properties } = event; | ||
| const enrichedProperties = { | ||
| ...properties, | ||
| timestamp: new Date().toISOString() | ||
| }; | ||
|
|
||
| await sendToAPI(userId, enrichedProperties, settings); | ||
| } | ||
|
|
||
| // INCORRECT: Poor style | ||
| async function onTrack(event, settings) { | ||
| var x = event.userId; // ❌ Using var | ||
| event.properties.timestamp = new Date(); // ❌ Mutating parameter | ||
| temp = x; // ❌ Global variable, poor naming | ||
| await sendToAPI(temp, event.properties, settings); | ||
| } | ||
| ``` | ||
|
|
||
| ### 7. Deployment Considerations | ||
|
|
||
| **✅ Check for:** | ||
|
|
||
| - No dependencies on local file system (no `fs` module usage) | ||
| - Code is stateless (no in-memory state between invocations) | ||
| - Function completes quickly (avoid long-running operations) | ||
| - Proper validation of `settings` before use | ||
|
|
||
| **❌ Reject if:** | ||
|
|
||
| - Using `fs.readFile()` or `fs.writeFile()` (no file system in Segment runtime) | ||
| - Storing state in module-level variables | ||
| - Operations that take >30 seconds | ||
| - Missing validation of required settings | ||
|
|
||
| ## Common Pitfalls to Avoid | ||
|
|
||
| ### 1. Missing Retry Logic for External APIs | ||
|
|
||
| ```javascript | ||
| // CORRECT: Use fetch-retry for resilience | ||
| const fetchRetry = require('fetch-retry')(fetch); | ||
|
|
||
| const response = await fetchRetry(url, { | ||
| retries: 3, | ||
| retryDelay: 1000 | ||
| }); | ||
|
|
||
| // INCORRECT: No retry on transient failures | ||
| const response = await fetch(url); // ❌ Single attempt | ||
| ``` | ||
|
|
||
| ### 2. Not Validating Settings | ||
|
|
||
| ```javascript | ||
| // CORRECT: Validate settings exist | ||
| async function onTrack(event, settings) { | ||
| if (!settings.apiUrl || !settings.apiToken) { | ||
| console.error('Missing required settings: apiUrl and apiToken'); | ||
| return; | ||
| } | ||
| // Proceed with logic | ||
| } | ||
|
|
||
| // INCORRECT: Assume settings are always present | ||
| async function onTrack(event, settings) { | ||
| await fetch(settings.apiUrl); // ❌ May be undefined | ||
| } | ||
| ``` | ||
|
|
||
| ### 3. Breaking Conditional Exports | ||
|
|
||
| ```javascript | ||
| // CORRECT: Only export in test mode | ||
| if (process.env.NODE_DEV === 'TEST') { | ||
JasonSooter marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| module.exports = { onTrack, helperFunction }; | ||
| } | ||
|
|
||
| // INCORRECT: Always exporting | ||
| module.exports = { onTrack }; // ❌ Breaks Segment runtime | ||
| ``` | ||
|
|
||
| ### 4. Improper Use of Globals Available in Tests | ||
|
|
||
| The test environment provides global mocks (`fetch`, `btoa`, `_`, `crypto`, `moment`, `cache`), but production code should explicitly require dependencies. | ||
|
|
||
| ```javascript | ||
| // CORRECT: Explicit require in production code | ||
| const moment = require('moment-timezone'); | ||
| const _ = require('lodash'); | ||
|
|
||
| // INCORRECT: Relying on test globals in production | ||
| // Using _ or moment without require will fail in Segment runtime | ||
| ``` | ||
|
|
||
| ## Review Response Format | ||
|
|
||
| When providing code review feedback, structure your response as: | ||
|
|
||
| 1. **Summary**: Brief overview of the change and overall assessment | ||
| 2. **Critical Issues**: Blocking problems that must be fixed (security, crashes, breaking changes) | ||
| 3. **Warnings**: Important issues that should be addressed but aren't blocking | ||
| 4. **Suggestions**: Nice-to-have improvements for code quality | ||
| 5. **Positive Feedback**: Highlight what was done well | ||
|
|
||
| **Example Review:** | ||
|
|
||
| ``` | ||
| ## Summary | ||
| Adding new email notification feature to send event data to external API. Overall approach is sound but has several issues that need addressing. | ||
|
|
||
| ## Critical Issues | ||
| 1. **Security**: API token is hardcoded on line 45. Move to `settings.emailApiToken` | ||
| 2. **Error Handling**: Unhandled promise rejection on line 52. Wrap fetch in try-catch | ||
|
|
||
| ## Warnings | ||
| 1. **Testing**: Missing test case for error scenario when API returns 500 | ||
| 2. **Performance**: Making API call inside map() on line 38 creates N+1 pattern. Consider batching | ||
|
|
||
| ## Suggestions | ||
| 1. Consider adding request timeout to prevent hanging on slow API | ||
| 2. Variable name `temp` on line 31 could be more descriptive (e.g., `formattedEvent`) | ||
|
|
||
| ## Positive Feedback | ||
| - Excellent use of destructuring to extract event properties | ||
| - Good logging with context for debugging | ||
| - Proper use of async/await instead of callbacks | ||
| ``` | ||
JasonSooter marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| ## Continuous Improvement | ||
|
|
||
| This document should evolve as the project grows. When new patterns emerge or common mistakes are identified, update these instructions to teach Copilot to catch them in future reviews. | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.