| name | tigerstyle |
| description | Apply TigerBeetle-inspired Ruby Fast LSP code quality rules for safety, performance, invariants, and maintainability. |
TigerStyle Code Quality Skill
Use this skill when writing new code, reviewing code, or refactoring existing code in the Ruby Fast LSP project. Enforces TigerBeetle-inspired coding standards for safety, performance, and maintainability. Triggers: writing code, code quality, style guide, best practices, coding standards.
Core Philosophy
Priority order: Safety > Performance > Developer Experience
Simplicity and elegance require hard work and discipline. Invest upfront design effort to prevent expensive production problems.
Hard Rules
1. Function Length Limit: 70 Lines Maximum
No function should exceed 70 lines. This prevents cognitive overload and forces proper decomposition.
If a function exceeds 70 lines:
- Identify logical sections
- Extract helper functions with clear names
- Each helper should do one thing well
fn process_document(doc: &Document) -> Result<()> {
}
fn process_document(doc: &Document) -> Result<()> {
let parsed = parse_content(doc)?;
let indexed = index_symbols(&parsed)?;
resolve_references(&indexed)?;
Ok(())
}
2. Minimum Two Assertions Per Function
Assert both positive space (expected behavior) and negative space (invalid states).
fn find_definition(index: &Index, fqn: &Fqn) -> Option<Location> {
assert!(!fqn.is_empty(), "FQN cannot be empty");
let result = index.get(fqn);
if let Some(loc) = &result {
assert!(loc.uri.scheme() == "file", "Only file URIs supported");
}
result
}
3. Explicit Control Flow Only
- No recursion (use iteration with explicit stack)
- No deeply nested conditionals (max 3 levels)
- Push
ifs up, fors down
fn traverse(node: &Node) {
process(node);
for child in node.children() {
traverse(child);
}
}
fn traverse(root: &Node) {
let mut stack = vec![root];
while let Some(node) = stack.pop() {
process(node);
stack.extend(node.children());
}
}
4. Push Ifs Up, Fors Down
Centralize branching logic in parent functions. Keep leaf functions pure.
fn process_item(item: &Item, mode: Mode) {
if mode == Mode::Fast {
} else {
}
}
fn process_items(items: &[Item], mode: Mode) {
match mode {
Mode::Fast => items.iter().for_each(process_fast),
Mode::Slow => items.iter().for_each(process_slow),
}
}
5. Bounds on All Loops and Queues
Every loop must have a clear termination condition. Every queue must have a capacity.
while let Some(item) = queue.pop() {
process(item);
}
const MAX_ITERATIONS: usize = 10_000;
let mut iterations = 0;
while let Some(item) = queue.pop() {
iterations += 1;
assert!(iterations < MAX_ITERATIONS, "Infinite loop detected");
process(item);
}
Naming Conventions
Variables and Functions: snake_case
fn find_definition_at_position(pos: Position) -> Option<Location>
let current_scope = get_scope();
Units as Suffixes, Ordered by Significance
let latency_ms_max = 100;
let timeout_seconds_default = 30;
let buffer_bytes_capacity = 1024;
let max_latency_ms = 100;
let default_timeout_seconds = 30;
No Abbreviations (Except Mathematical Contexts)
let definition_count = 0;
let reference_locations = vec![];
let def_cnt = 0;
let ref_locs = vec![];
Acronyms: Proper Capitalization
struct FqnResolver;
struct LspServer;
struct AstNode;
struct FQNResolver;
struct LSPServer;
struct ASTNode;
Comments and Documentation
Complete Sentences with Punctuation
Explain "Why", Not "What"
Document Edge Cases and Invariants
fn find_references(pos: Position, include_declaration: bool) -> Vec<Location>
Error Handling
CRITICAL: Fail Fast and Loudly (MANDATORY)
Never use debug_assert! - Use assert! and panic! instead.
Production correctness is more important than "graceful degradation" that hides bugs.
let fqn_id = self.get_fqn_id(fqn).unwrap_or_default();
debug_assert!(fqn.is_valid());
let fqn_id = self.get_fqn_id(fqn).expect(
"INVARIANT VIOLATED: FQN not in index. \
This is a bug - index the file first."
);
assert!(fqn.is_valid(), "Invalid FQN: {}", fqn);
Why: Better to crash and fix the bug than silently produce wrong results that corrupt data or mislead users.
Clear Error Messages
Every panic/assert must explain WHAT, WHY, and HOW:
assert!(
matches!(fqn, Namespace(_, _)),
"INVARIANT VIOLATED: get_ancestor_chain called with {}.\n\
Only Namespace FQNs have ancestors (not Constants/Methods).\n\
Fix: Check the FQN type before calling this function.",
fqn
);
assert!(fqn.is_namespace(), "Invalid FQN");
Never Ignore Errors
"Almost all catastrophic system failures result from incorrect handling of non-fatal errors explicitly signaled in software."
let _ = file.write_all(data);
file.write_all(data)?;
if let Err(e) = file.write_all(data) {
warn!("Failed to write cache: {}", e);
}
Use Result for Recoverable, Option for Expected Absence
fn read_file(path: &Path) -> Result<String, IoError>
fn find_symbol(name: &str) -> Option<Symbol>
No Assumptions or Guessing
If data is missing or invalid, PANIC - don't guess:
let kind = fqn.namespace_kind().unwrap_or(Instance);
let kind = match fqn {
Namespace(_, k) => k,
All|Variants => panic!("Expected Namespace FQN, got: {}", fqn),
};
Memory and Performance
Prefer Static Allocation
Allocate at startup when possible. Avoid dynamic allocation in hot paths.
let mut buffer = Vec::with_capacity(EXPECTED_SIZE);
for item in items {
let mut temp = Vec::new();
}
Use Explicitly-Sized Types
let count: u32 = 0;
let offset: usize = 0;
let count = 0;
String Interning for Repeated Strings
Use Ustr for frequently compared or stored strings.
use ustr::Ustr;
let name: Ustr = "MyClass".into();
if name == other_name { ... }
Code Organization
Order Within Files
main or primary entry point first
- Public functions
- Private helpers
- Tests at bottom
Order Within Structs
- Fields
- Associated types
- Methods (public, then private)
Alphabetize When No Natural Order
use crate::analyzer;
use crate::indexer;
use crate::query;
match node {
Node::Class(c) => ...,
Node::Constant(c) => ...,
Node::Method(m) => ...,
Node::Module(m) => ...,
}
Line Length: 100 Characters Maximum
Break long lines for readability:
let result = some_function(
first_argument,
second_argument,
third_argument,
);
let result = some_function(first_argument, second_argument, third_argument, fourth_argument);
Checklist for Every Change