mirror of
https://github.com/google/nomulus
synced 2026-04-13 04:57:19 +00:00
Our existing precision is milliseconds so we want to stick with that for Instants. If we want to increase the precision globally after that we can do so all in one go post-migration, but for now, it would be a bad thing to have mixed precision going on just depending on whether a class happens to be migrated yet or not. This PR also migrates all existing DateTime.nowUtc() calls to use the Clock interface, so that when they are migrated they will get the benefit of this precision-setting as well. BUG= http://b/496985355
4.7 KiB
4.7 KiB
Engineering Standards for Gemini CLI
This document outlines foundational mandates, architectural patterns, and project-specific conventions to ensure high-quality, idiomatic, and consistent code from the first iteration.
Core Mandates
1. Rigorous Import Management
- Addition: When adding new symbols, ensure the corresponding import is added.
- Removal: When removing the last usage of a class or symbol from a file (e.g., removing a
@Inject Clock clock;field), immediately remove the associated import. Do not wait for a build failure to identify unused imports. - Checkstyle: Proactively fix common checkstyle errors (line length > 100, formatting, unused imports) during the initial code write. Do not wait for CI/build failures to address these, as iterative fixes are inefficient.
- Verification: Before finalizing any change, scan the imports section for redundancy.
2. Time and Precision Handling (java.time Migration)
- Millisecond Precision: Always truncate
Instant.now()to milliseconds (using.truncatedTo(ChronoUnit.MILLIS)) to maintain consistency with JodaDateTimeand the PostgreSQL schema (which enforces millisecond precision via JPA converters). - Clock Injection:
- Avoid direct calls to
Instant.now(),DateTime.now(),ZonedDateTime.now(), orSystem.currentTimeMillis(). - Inject
google.registry.util.Clock(production) orgoogle.registry.testing.FakeClock(tests). - Use
clock.nowDate()to get aZonedDateTimein UTC.
- Avoid direct calls to
- Beam Pipelines:
- Ensure
Clockis serializable (it is by default in this project) when used in BeamDoFns. - Pass the
Clockthrough the constructor or via Dagger provider methods in the pipeline module.
- Ensure
- Command-Line Tools:
- Use
@Inject Clock clock;inCommandimplementations. - The
clockfield should be package-private (no access modifier) to allow manual initialization in corresponding test classes. - In test classes (e.g.,
UpdateDomainCommandTest), manually setcommand.clock = fakeClock;in the@BeforeEachmethod. - Base test classes like
EppToolCommandTestCaseshould handle this assignment for their generic command types where applicable.
- Use
3. Dependency Injection (Dagger)
- Concrete Types: Dagger
injectmethods must use explicit concrete types. Genericinject(Command)methods will not work. - Test Components: Use
TestRegistryToolComponentfor command-line tool tests to bridge the gap betweenmainandnonprod/testsource sets.
4. Database Consistency
- JPA Converters: Be aware that JPA converters (like
DateTimeConverter) may perform truncation or transformation. Ensure application-level logic matches these transformations to avoid "dirty" state or unexpected diffs. - Transaction Management:
- Top-Level: Define database transactions (
tm().transact(...)) at the highest possible level in the call chain (e.g., in an Action, a Command, or a Flow). This ensures all operations are atomic and handled by the retry logic. - DAO Methods: Avoid declaring transactions inside low-level DAO methods. Use
tm().assertInTransaction()to ensure that these methods are only called within a valid transactional context. - Utility/Cache Methods: Use
tm().reTransact(...)for utility methods or Caffeine cache loaders that might be invoked from both transactional and non-transactional paths.reTransactwill join an existing transaction if one is present (acting as a no-op) or start a new one if not.- This is particularly useful for in-memory caches where the loader must be able to fetch data regardless of whether the caller is currently in a transaction.
- Transactional Time: Ensure code that relies on
tm().getTransactionTime()is executed within a transaction context.
- Top-Level: Define database transactions (
5. Testing Best Practices
- FakeClock and Sleeper: Use
FakeClockandSleeperfor any logic involving timeouts, delays, or expiration. - Empirical Reproduction: Before fixing a bug, always create a test case that reproduces the failure.
- Base Classes: Leverage
CommandTestCase,EppToolCommandTestCase, etc., to reduce boilerplate and ensure consistent setup (e.g., clock initialization).
6. Project Dependencies
- Common Module: When using
Clockor other core utilities in a new or separate module (likeload-testing), ensureimplementation project(':common')is added to the module'sbuild.gradle.
Performance and Efficiency
- Turn Minimization: Aim for "perfect" code in the first iteration. Iterative fixes for checkstyle or compilation errors consume significant context and time.
- Context Management: Use sub-agents for batch refactoring or high-volume output tasks to keep the main session history lean and efficient.