


Change a High-Performance Database Codebase Without Breaking Speed or Compatibility
Use it when an AI changes a speed-focused database that spans Java, SQL, Rust, native code, and CI. It sets rules for code style, NULL handling, memory cleanup, query tests, failure checks, pull requests, and builds, and ends with a check before calling the work done.
To use this AI rule
## Keep Database Modules Separate and Protect Allocation-Free Data Paths
- Preserve explicit module boundaries for the storage engine, SQL engine, network protocols, utilities, worker queues, logging, background tasks, benchmarks, compatibility tests, and examples.
- Keep production code separate from benchmarks, build utilities, examples, and platform wrappers.
- Treat a nested client repository or submodule as an independent repository with its own commits and compatibility target.
- Identify the correct entry point before changing behavior: server startup, storage engine, SQL compiler, table reader, or table writer.
- Protect zero-allocation data paths during query execution and ingestion.
- Prefer object pools, reusable buffers, column-oriented storage, vectorized execution, and native acceleration where the architecture requires them.
- Do not introduce third-party runtime dependencies when the project intentionally implements performance-critical algorithms in-house.
- Keep native code limited to operations that justify its memory, platform, and failure risks.
## Order Java Class Members and Keep Older Java Clients Compatible
- Group class members by static or instance kind and visibility, then keep members alphabetically ordered within each group.
- Insert new fields and methods at their correct sorted position instead of creating decorative section groups.
- Never add banner comments as artificial Java section headings.
- Use the modern language features supported by the main module, including enhanced switches, multiline strings, and pattern variables.
- Respect older language targets in compatibility modules; do not let main-module syntax leak into a client that targets an earlier Java release.
- Prefix boolean variables, fields, and methods with `is` or `has` as appropriate.
- Keep log messages strictly ASCII when the logging pipeline cannot reliably render Unicode punctuation or symbols.
- Use the project's resource-aware collection types instead of generic object arrays when they integrate with established cleanup utilities.
- Follow existing naming, ownership, and allocation patterns before introducing a new abstraction.
## Decide NULL Behavior and Clean Up Memory on Every Error Path
- Decide NULL behavior explicitly for column data, expression results, SQL statements, and intermediate state.
- Distinguish a NULL sentinel meaning “not initialized yet” from a genuine data NULL.
- Trace every allocation and closeable resource through success, error, cancellation, and early-return paths.
- Use the repository cleanup helpers consistently for lists and closeable elements.
- Treat leak prevention as a design requirement, not a test-only concern.
- Add memory-leak assertions to tests that allocate native memory.
- Narrow unit tests that provably allocate no native memory may omit the leak harness.
- Verify cleanup on every relevant error path, not only on the successful path.
- Never hide an ownership ambiguity behind a convenient container or helper.
## Test Queries With Strict Checks That Reread the Results
- Prefer the canonical fluent query assertion framework over legacy single-pass print-and-compare helpers.
- For deterministic queries, require a second cursor pass, size cross-checks, variable-column checks, and relevant factory-property assertions.
- Use a one-pass assertion only when the projection is genuinely unstable across rereads, and state the reason.
- Examples of valid instability include unseeded random functions and time-varying functions.
- Execute DDL and other non-query statements through the non-query execution path.
- Assert execution plans with the framework's plan helpers when optimizer behavior is part of the contract.
- Use one INSERT statement for multiple test rows when the dialect supports it.
- Use multiline strings for long SQL and multiline expected results.
- Write SQL keywords consistently, preferably in uppercase.
- Separate thousands with underscores in literals of five or more digits when that syntax is supported.
## Follow the SQL Dialect and Point Errors at the Exact Character
- Treat multidimensional array types as distinct types when dimensionality is encoded in the column type.
- Use the dialect's preferred cast syntax consistently instead of mixing equivalent forms.
- Keep numeric separator support aligned between the parser, implementation code, and tests.
- Do not assume standard SQL features exist; document and test unsupported operations.
- When row deletion is unsupported, model soft deletion explicitly in application logic.
- Keep partition-level drop operations distinct from row-level deletion because their scope and safety differ.
- Point a reported SQL error position at the exact offending character, not merely the start of the expression.
- Test parser errors for both the message and the precise position.
- Preserve the difference between syntax, semantic, type, and execution errors.
## Treat Every Failing Test as a Live Bug Until Evidence Says Otherwise
- Treat every red test, failed CI job, and surprising log entry as a live defect until evidence proves otherwise.
- “Pre-existing,” “flaky,” “unrelated,” and “known issue” are hypotheses, not conclusions.
- Reproduce on the relevant baseline, inspect version history, measure timing constraints, and check authoritative upstream reports.
- Set a failure aside only after the evidence forces that conclusion.
- Report the evidence that justified classification so another reviewer can verify it.
- Do not make a green rerun the sole proof that a failure was harmless.
- Investigate interaction failures when several changes share one CI lineage.
- Preserve logs, failing inputs, and the smallest reliable reproduction needed for diagnosis.
- Keep the fix focused on the proven cause and add a regression test that fails without it.
## Fit Branches and Pull Requests to the Merge Style and CI Cost
- Match branch and pull-request strategy to the repository's merge model and CI economics.
- In a squash-merge workflow, do not spend effort polishing throwaway branch history.
- Add a clear fix-up commit instead of rewriting prior commits or force-pushing only for cosmetic history.
- Bundle related fixes when splitting would multiply long CI runs, flake exposure, and merge coordination.
- Split changes only when the user requests it or when changes must merge or revert independently.
- When updating an existing pull request, synchronize with its remote head, add the changes to that branch, and update its metadata.
- Do not open a second pull request merely to rerun validation for the same change lineage.
- Do not create review worktrees or checkout branches when repository policy defines another branch as the canonical review state.
- Fetch a pull-request diff without switching branches when only the diff is needed.
- Commit a nested repository internally before recording its parent submodule pointer.
## Write Pull Requests and Commits That Show Gains and Downsides Equally
- Use the repository's required pull-request title convention and make the description meaningful to end users.
- Describe positive effects, negative effects, regressions, and tradeoffs with equal prominence.
- Avoid sales language, superlatives, selective metrics, and decorative emphasis.
- Reference a fixed issue in the required closing form at the top of the pull-request body.
- Keep commit titles short, descriptive, and in the repository's required style.
- Include a substantive commit body that explains the change rather than repeating the title.
- Use plain bullets in test plans when checkboxes or check marks are prohibited.
- Apply labels that match the change domain and title.
- Use active voice in commits, pull requests, and code comments.
- Name the acting class, method, caller, or component instead of hiding it in passive constructions.
## Check the Environment, Then Build and Test From the Smallest Scope
- Verify the required language runtime, build tool, architecture, and environment variables before building.
- Choose the smallest build profile that proves the current change, then use fuller profiles when web or native artifacts are affected.
- Rebuild dependent modules when a local artifact cache may contain a stale client or library.
- Run expensive test invocations sequentially when parallel builds interfere with each other.
- Start with the narrowest relevant test class or method, then expand according to risk and repository policy.
- Do not treat compilation alone as proof of runtime behavior.
- Validate the server through the supported startup path and inspect the directly affected endpoint or interface.
- Keep build artifacts in their designated module-owned locations.
- Record the exact failed command and first actionable error when a build stops.
## Clear All Rust Warnings and Stop Panics From Crashing the JVM Through JNI
- Before completing Rust work, run formatting, all-target compilation, linting, and library tests from the crate root.
- Require zero errors and zero warnings across all mandatory checks.
- Measure coverage after adding or changing tests.
- For every uncovered line, add a test or prove the line unreachable and mark that invariant explicitly.
- Never call `unwrap()` or `expect()` on file-derived or externally supplied data.
- Propagate recoverable failures with `Result`, `Option`, and the language's standard error propagation.
- Remember that a Rust panic crossing JNI can abort the entire JVM without recovery.
- Build native C and C++ libraries with the repository's release configuration and verify the expected artifacts.
- Treat FFI ownership, lifetime, alignment, and error conversion as explicit contracts.
- Test boundary failures on every supported platform affected by the change.
## Avoid Heap Allocation and Measure Speed Gains With Benchmarks
- Avoid heap allocation in data paths where zero-GC behavior is an architectural requirement.
- Reuse buffers and pooled objects, and make ownership transfer explicit.
- Store analytical data by column when compression and vectorized operations depend on that layout.
- Keep SIMD, memory management, and platform-specific optimizations behind narrow native boundaries.
- Separate storage, parsing, planning, code generation, execution, protocol handling, and background work.
- Use custom collections only when their allocation and cleanup behavior is understood and tested.
- Measure performance changes with representative benchmarks rather than intuition.
- Pair an optimization with correctness tests, resource checks, and a documented tradeoff.
- Do not weaken compatibility or error precision to obtain an unmeasured speedup.
## Review the Final Diff and Report Results Before Calling the Work Done
- Review the final diff for member ordering, compatibility boundaries, NULL behavior, resource ownership, and accidental Unicode in logs.
- Run the focused Java, SQL, Rust, native, and integration checks affected by the change.
- Confirm deterministic queries use the strong reread assertion path.
- Confirm external input cannot trigger an unrecoverable native panic.
- Verify any investigated failure has evidence-backed classification and a reported reproduction.
- Ensure pull-request metadata, labels, test plan, commit text, and issue references match repository policy.
- Verify nested repositories and parent pointers are committed in the correct order.
- Do not claim completion while mandatory checks have errors or warnings.
- Report the commands run, observed results, remaining risks, and intentionally untested scope.
