|
| 1 | +--- |
| 2 | +title: "Custom Semantic Code Checks for PRs" |
| 3 | +date: 2025-06-26T17:16:50+01:00 |
| 4 | +draft: true |
| 5 | +toc: false |
| 6 | +images: |
| 7 | +tags: |
| 8 | + - untagged |
| 9 | +--- |
| 10 | + |
| 11 | +# Custom Semantic Code Checks for PRs |
| 12 | + |
| 13 | +<!-- TODO: add a Recurse screenshot as a header here --> |
| 14 | + |
| 15 | +Maintaining consistent code quality while scaling the team is difficult. |
| 16 | +Well-intentioned Notion style guides are time-consuming to write and are often disregarded as soon as they're written. |
| 17 | +In this post I'll describe how you can use Recurse ML's new Custom Rules Feature to put the code style guide inside of your PR process. |
| 18 | + |
| 19 | +Recurse ML is a GitHub App that identifies bugs introduced in PRs. |
| 20 | +It focuses on semantic issues that slip past static analysers. |
| 21 | + |
| 22 | +We realised that every team's "source of paranoia" is different. |
| 23 | +While some universal bugs exist, the most valuable catches are often team-specific. |
| 24 | +That's why we built custom rules - a way to teach Recurse ML about your codebase's unique requirements. |
| 25 | + |
| 26 | +## Quickstart |
| 27 | + |
| 28 | +First off, install Recurse ML [GitHub app](https://github.com/apps/recurseml/) if you haven't already. |
| 29 | + |
| 30 | +Then, create a `.recurseml.yaml` file (you can place it anywhere in your project): |
| 31 | + |
| 32 | +```yaml |
| 33 | +# put this in .recurseml.yaml anywhere in your repo |
| 34 | +custom_rules: |
| 35 | + - name: "Effective Code Comments" # https://blog.codinghorror.com/code-tells-you-how-comments-tell-you-why/ |
| 36 | + applicable_files: |
| 37 | + - "**/*.js" |
| 38 | + - "**/*.ts" |
| 39 | + - "**/*.py" |
| 40 | + - "**/*.go" |
| 41 | + - "**/*.java" |
| 42 | + - "**/*.rb" |
| 43 | + - "**/*.cs" |
| 44 | + description: "Explain WHY not WHAT the code does. Document complex business logic, clarify non-obvious implementations, warn about gotchas, and provide context for maintainers. Use proper documentation comment format for functions/methods. Keep TODO comments specific with assignees. Update comments when code changes." |
| 45 | +``` |
| 46 | +
|
| 47 | +The full docs for custom rules can be found [here](https://recurse-ml.notion.site/Custom-Rules-21106defaa718059beabfb583f2ad066). |
| 48 | +
|
| 49 | +Now, every time a PR is submitted in your repo, a check will run, that verifies whether comments in the newly introduced code adhere to these guidelines. |
| 50 | +If they don't, Recurse ML will leave a review comment. |
| 51 | +
|
| 52 | +
|
| 53 | +For tips on how to write high quality rules, I recommend reading [bdougie's](https://x.com/bdougieYO) excellent blogpost [The Anatomy of Rules](https://blog.continue.dev/the-anatomy-of-rules-writing-effective-boundaries-for-ai-agents-in-ruby/). |
| 54 | +Even though his examples are in Ruby, the principles are universal. |
| 55 | +
|
| 56 | +## Inspiration Gallery |
| 57 | +
|
| 58 | +If you're still wondering what custom rules to introduce into your repo, I've gathered some examples. |
| 59 | +
|
| 60 | +### Performance & Scaling |
| 61 | +
|
| 62 | +These rules catch the performance killers that don't show up until you're under real load. |
| 63 | +For most applications, they're probably overkill - but if you're one of the lucky few startups with actual users, these patterns become critical. |
| 64 | +They're designed to prevent issues that work fine in development but can bring down your entire API when traffic picks up. |
| 65 | +
|
| 66 | +```yaml |
| 67 | +custom_rules: |
| 68 | + - name: "Avoid Blocking Operations in Request Handlers" |
| 69 | + applicable_files: |
| 70 | + - "routes/**/*.js" |
| 71 | + - "api/**/*.js" |
| 72 | + - "**/*handler*.js" |
| 73 | + description: "Don't use synchronous operations (fs.readFileSync, crypto.pbkdf2Sync) in request handlers as they block the event loop and kill performance under load. Use async alternatives or move to worker threads. One blocking operation can take down your entire API response time." |
| 74 | +``` |
| 75 | +
|
| 76 | +
|
| 77 | +```yaml |
| 78 | +custom_rules: |
| 79 | + - name: "Avoid Blocking Operations in Request Handlers" |
| 80 | + applicable_files: |
| 81 | + - "routes/**/*.js" |
| 82 | + - "api/**/*.js" |
| 83 | + - "**/*handler*.js" |
| 84 | + description: "Don't use synchronous operations (fs.readFileSync, crypto.pbkdf2Sync) in request handlers as they block the event loop and kill performance under load. Use async alternatives or move to worker threads. One blocking operation can take down your entire API response time." |
| 85 | +``` |
| 86 | +
|
| 87 | +```yaml |
| 88 | +custom_rules: |
| 89 | + - name: "Database Query Optimisation" |
| 90 | + applicable_files: |
| 91 | + - "**/*.js" |
| 92 | + - "**/*.ts" |
| 93 | + description: "Avoid N+1 queries and missing database indexes. Use eager loading, batch queries, or data loaders. Flag loops that contain database queries or ORM calls. These issues don't show up in development but will crash production performance as data grows." |
| 94 | +``` |
| 95 | +
|
| 96 | +## Operational Reliability |
| 97 | +
|
| 98 | +
|
| 99 | +Production systems have a way of failing in the most inconvenient moments. |
| 100 | +These rules focus on the defensive coding practices that make the difference between a system that gracefully handles errors and one that silently loses data or crashes under pressure. |
| 101 | +Most codebases won't need this level of rigour, but when reliability matters to your users (and your sleep schedule), these patterns become essential. |
| 102 | +
|
| 103 | +```yaml |
| 104 | +custom_rules: |
| 105 | + - name: "Proper Error Handling in Critical Paths" |
| 106 | + applicable_files: |
| 107 | + - "**/*.js" |
| 108 | + - "**/*.ts" |
| 109 | + description: "Critical operations (payments, user registration, data processing) must have comprehensive error handling with logging, monitoring, and graceful degradation. Avoid silent failures that could lose revenue or corrupt user data. Include error context for debugging production issues." |
| 110 | +``` |
| 111 | +
|
| 112 | +```yaml |
| 113 | +custom_rules: |
| 114 | + - name: "Memory Leak Prevention" |
| 115 | + applicable_files: |
| 116 | + - "**/*.js" |
| 117 | + - "**/*.ts" |
| 118 | + description: "Clean up event listeners, timers, and streams to prevent memory leaks. Use weak references for caches, clear intervals/timeouts, and properly close database connections. Memory leaks in Node.js can silently kill server performance and require expensive restarts." |
| 119 | +``` |
| 120 | +
|
| 121 | +### Wisdom from the Elders |
| 122 | +
|
| 123 | +What if instead of sending a link-dump of classic blogposts to new-hires, we could encode the knowledge right inside of the repo? |
| 124 | +Wonder no more. |
| 125 | +As you probably noticed, the example rule in Quickstart session was inspired by [Jeff Atwood's](https://blog.codinghorror.com/about-me/) [classic](https://blog.codinghorror.com/code-tells-you-how-comments-tell-you-why/). |
| 126 | +I decided to distil some more classic software engineering blogposts into rules. |
| 127 | +
|
| 128 | +```yaml |
| 129 | +custom_rules: |
| 130 | + - name: "Don't DRY Your Code Prematurely" # https://testing.googleblog.com/2024/05/dont-dry-your-code-prematurely.html |
| 131 | + applicable_files: |
| 132 | + - "**/*.js" |
| 133 | + - "**/*.ts" |
| 134 | + - "**/*.py" |
| 135 | + - "**/*.go" |
| 136 | + - "**/*.java" |
| 137 | + - "**/*.rb" |
| 138 | + - "**/*.cs" |
| 139 | + description: "Consider carefully if code duplication is truly redundant or just superficially similar before applying DRY principles. Functions or classes may look the same but serve different contexts and business requirements that evolve differently over time. Avoid premature abstractions that couple behaviours which may need to evolve separately. When in doubt, keep behaviours separate until enough common patterns emerge over time that justify the coupling. Think about long-term evolution, not just making code shorter. Apply YAGNI principle - tolerate a little duplication in early development stages and wait to abstract until clear patterns emerge." |
| 140 | +``` |
| 141 | +
|
| 142 | +```yaml |
| 143 | +custom_rules: |
| 144 | + - name: "Make Wrong Code Look Wrong" # https://www.joelonsoftware.com/2005/05/11/making-wrong-code-look-wrong/ |
| 145 | + applicable_files: |
| 146 | + - "**/*.js" |
| 147 | + description: "Use naming conventions that make security vulnerabilities and type mismatches immediately visible. For web applications, prefix all user-supplied strings with 'unsafe_' or 'us' and all sanitized strings with 'safe_' or 's'. For coordinate systems, use prefixes like 'window_x' vs 'layout_x' to distinguish semantic differences. Apply Apps Hungarian notation principles - use prefixes that indicate the semantic meaning or origin of data, not just the data type. Make code self-documenting by ensuring that dangerous or incorrect operations look obviously wrong at the point of use. Collocate all information needed to verify correctness in the same line of code." |
| 148 | +``` |
| 149 | +
|
| 150 | +Now, the rules act as a JIT learning tool for new-joiners, too! |
| 151 | +
|
| 152 | +
|
| 153 | +## Your Usecase Here |
| 154 | +
|
| 155 | +Do you have a pet peeve that you fight for like it's the Battle of Verdun? |
| 156 | +Is there part of your project that requires extra care during every PR? |
| 157 | +Maybe you just found a weird and wacky way to use this feature? |
| 158 | +I'd like to hear all about it. |
| 159 | +My email's my name at recurse.ml. |
| 160 | +And for higher bandwidth comms, you can find our team on Discord: https://discord.gg/qEjHQk64Z9 |
0 commit comments