|
| 1 | +--- |
| 2 | +title: AWS SDK for Java v2 General Guidelines |
| 3 | +inclusion: always |
| 4 | +--- |
| 5 | + |
| 6 | +# AWS SDK for Java v2 General Guidelines |
| 7 | + |
| 8 | +## General Principles |
| 9 | + |
| 10 | +- Write clean, readable, and maintainable code |
| 11 | +- Follow the SOLID principles of object-oriented design |
| 12 | +- Favor composition over inheritance |
| 13 | +- Program to interfaces, not implementations |
| 14 | +- Fail fast - detect and report errors as soon as possible |
| 15 | +- Maintain backward compatibility when modifying existing APIs |
| 16 | + |
| 17 | +## Code Style Standards |
| 18 | + |
| 19 | +- Follow Java coding conventions and the existing style in the codebase |
| 20 | +- Use meaningful variable, method, and class names that clearly indicate purpose |
| 21 | +- Include comprehensive Javadoc for public APIs |
| 22 | +- Keep methods short and focused on a single responsibility |
| 23 | +- Limit the number of method parameters (ideally 3 or fewer) |
| 24 | +- Use appropriate access modifiers (private, protected, public) |
| 25 | +- Follow consistent indentation and formatting |
| 26 | + |
| 27 | +## Common Design Patterns |
| 28 | + |
| 29 | +- Use builder pattern for object creation |
| 30 | +- Follow reactive programming principles for async operations |
| 31 | +- Use SdkException hierarchy for error handling |
| 32 | +- Prefer immutable objects where appropriate |
| 33 | + |
| 34 | +## Naming Conventions |
| 35 | + |
| 36 | +### Class Naming |
| 37 | + |
| 38 | +#### General Rules |
| 39 | +- Prefer singular class names: `SdkSystemSetting`, not `SdkSystemSettings` |
| 40 | +- Treat acronyms as a single word: `DynamoDbClient`, not `DynamoDBClient` |
| 41 | + |
| 42 | +#### Classes that instantiate other classes |
| 43 | + |
| 44 | +- If the class's primary purpose is to return instances of another class: |
| 45 | + - If the "get" method has no parameters: |
| 46 | + - If the class implements `Supplier`: `{Noun}Supplier` (e.g. `CachedSupplier`) |
| 47 | + - If the class does not implement `Supplier`: `{Noun}Provider` (e.g. `AwsCredentialsProvider`) |
| 48 | + - If the "get" method has parameters: `{Noun}Factory` (e.g. `AwsJsonProtocolFactory`) |
| 49 | + |
| 50 | +#### Service-specific classes |
| 51 | + |
| 52 | +- If the class makes service calls: |
| 53 | + - If the class can be used to invoke *every* data-plane operation: |
| 54 | + - If the class is code generated: |
| 55 | + - If the class uses sync HTTP: `{ServiceName}Client` (e.g. `DynamoDbClient`) |
| 56 | + - If the class uses async HTTP: `{ServiceName}AsyncClient` (e.g. `DynamoDbAsyncClient`) |
| 57 | + - If the class is hand-written: |
| 58 | + - If the class uses sync HTTP: `{ServiceName}EnhancedClient` (e.g. `DynamoDbEnhancedClient`) |
| 59 | + - If the class uses async HTTP: `{ServiceName}EnhancedAsyncClient` (e.g. `DynamoDbEnhancedAsyncClient`) |
| 60 | + - If the class can be used to invoke only *some* data-plane operations: |
| 61 | + - If the class uses sync HTTP: `{ServiceName}{Noun}Manager` (e.g. `SqsBatchManager`) |
| 62 | + - If the class uses async HTTP: `{ServiceName}Async{Noun}Manager` (e.g. `SqsAsyncBatchManager`) |
| 63 | + - Note: If only one implementation exists and it uses async HTTP, `Async` may be excluded. (e.g. `S3TransferManager`) |
| 64 | +- If the class does not make service calls: |
| 65 | + - If the class creates presigned URLs: `{ServiceName}Presigner` (e.g. `S3Presigner`) |
| 66 | + - If the class is a collection of various unrelated "helper" methods: `{ServiceName}Utilities` (e.g. `S3Utilities`) |
| 67 | + |
| 68 | +## Class Initialization |
| 69 | + |
| 70 | +- Favor static factory methods over constructors: |
| 71 | + - Static factory methods provide meaningful names compared with constructors |
| 72 | + - They are useful when working with immutable classes as we can reuse the same object |
| 73 | + - Static factory methods can return any subtype of that class |
| 74 | + |
| 75 | +### Naming Conventions for Static Factory Methods |
| 76 | +- `create()`, `create(params)` when creating a new instance (e.g., `DynamoDBClient.create()`) |
| 77 | +- `defaultXXX()` when returning an instance with default settings (e.g., `BackoffStrategy.defaultStrategy()`) |
| 78 | + |
| 79 | +## Use of Optional |
| 80 | + |
| 81 | +- `Optional` **MUST NOT** be used when the result will never be null |
| 82 | +- For return types: |
| 83 | + - `Optional` **SHOULD** be used when it is not obvious to a caller whether a result will be null |
| 84 | + - `Optional` **MUST NOT** be used for "getters" in generated service model classes |
| 85 | +- For member variables: `Optional` **SHOULD NOT** be used |
| 86 | +- For method parameters: `Optional` **MUST NOT** be used |
| 87 | + |
| 88 | +## Object Methods (toString, equals, hashCode) |
| 89 | + |
| 90 | +- All public POJO classes **MUST** implement `toString()`, `equals()`, and `hashCode()` methods |
| 91 | +- When adding new fields to existing POJO classes, these methods **MUST** be updated to include the new fields |
| 92 | +- Implementation guidelines: |
| 93 | + - `toString()`: Include class name and all fields with their values |
| 94 | + - **MUST** use the SDK's `ToString` utility class (`utils/src/main/java/software/amazon/awssdk/utils/ToString.java`) for consistent formatting: |
| 95 | + ```java |
| 96 | + @Override |
| 97 | + public String toString() { |
| 98 | + return ToString.builder("YourClassName") |
| 99 | + .add("fieldName1", fieldValue1) |
| 100 | + .add("fieldName2", fieldValue2) |
| 101 | + .build(); |
| 102 | + } |
| 103 | + ``` |
| 104 | + - `equals()`: Compare all fields for equality, including proper null handling |
| 105 | + - `hashCode()`: Include all fields in the hash code calculation |
| 106 | +- Consider using IDE-generated implementations |
| 107 | +- For immutable objects, consider caching the hash code value for better performance |
| 108 | +- Unit tests **MUST** be added using EqualsVerifier to ensure all fields are properly included in equals and hashCode implementations. Example: |
| 109 | + ```java |
| 110 | + @Test |
| 111 | + public void equalsHashCodeTest() { |
| 112 | + EqualsVerifier.forClass(YourClass.class) |
| 113 | + .withNonnullFields("requiredFields") |
| 114 | + .verify(); |
| 115 | + } |
| 116 | + ``` |
| 117 | + |
| 118 | +## Exception Handling |
| 119 | + |
| 120 | +- Avoid throwing checked exceptions in public APIs |
| 121 | +- Don't catch exceptions unless you can handle them properly |
| 122 | +- Always include meaningful error messages |
| 123 | +- Clean up resources in finally blocks or use try-with-resources |
| 124 | +- Don't use exceptions for flow control |
| 125 | + |
| 126 | +## Use of Existing Utility Classes |
| 127 | + |
| 128 | +- Developers **MUST** check for existing utility methods before implementing their own |
| 129 | +- Common utility classes are available in the `utils` module and **SHOULD** be used when applicable |
| 130 | +- Examples of available utility classes: |
| 131 | + - `Lazy` (`utils/src/main/java/software/amazon/awssdk/utils/Lazy.java`): For thread-safe lazy initialization of singleton objects |
| 132 | + - `ToString` (`utils/src/main/java/software/amazon/awssdk/utils/ToString.java`): For consistent toString() implementations |
| 133 | + - `IoUtils`: For safely closing resources and handling I/O operations |
| 134 | + - `StringUtils`: For common string operations |
| 135 | + - `CollectionUtils`: For common collection operations |
| 136 | + - `ValidationUtils`: For input validation |
| 137 | +- Using existing utilities ensures: |
| 138 | + - Consistent behavior across the codebase |
| 139 | + - Thread-safety where applicable |
| 140 | + - Proper handling of edge cases |
| 141 | + - Reduced code duplication |
| 142 | +- Example of using `Lazy` for thread-safe singleton initialization: |
| 143 | + ```java |
| 144 | + private static final Lazy<ExpensiveObject> INSTANCE = new Lazy<>(() -> new ExpensiveObject()); |
| 145 | + |
| 146 | + public static ExpensiveObject getInstance() { |
| 147 | + return INSTANCE.getValue(); |
| 148 | + } |
| 149 | + ``` |
| 150 | + |
| 151 | +## Performance |
| 152 | + |
| 153 | +- Avoid premature optimization |
| 154 | +- Use appropriate data structures for the task |
| 155 | +- Be mindful of memory usage, especially with large collections |
| 156 | +- Consider thread safety in concurrent applications |
| 157 | +- Use profiling tools to identify actual bottlenecks |
| 158 | + |
| 159 | +## References |
| 160 | + |
| 161 | +- [AWS SDK for Java Developer Guide](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/home.html) |
| 162 | +- [Design Documentation](https://github.com/aws/aws-sdk-java-v2/tree/master/docs/design) |
0 commit comments