Learnings: Backend Services for Item Image Upload
Knowledge gained during implementation, useful for future projects.
AWS SDK
Section titled “AWS SDK”-
AWS SDK Java v2 has no native presigned POST support. GitHub issues #1493 (2019) and #6577 (2025) remain open. No v3 SDK exists. Manual SigV4 policy signing is the only JVM path. Use
kotlinx.serialization.jsonbuilders for safe JSON construction — never string interpolation. -
AWS SDK upgrade (2.34.3 → 2.42.24) was seamless — no breaking changes. BOM-managed via
aws-sdk2-bom, single version line inlibs.versions.toml. -
LocalStack supports STS
AssumeRoleout of the box. AddedMockAWS.Service.STS— no MockK fallback needed. -
Presigned URL format differs with LocalStack. Path-style (
http://host:port/bucket/key) vs virtual-hosted (https://bucket.s3.region.amazonaws.com/key). Tests should assert URL content (contains bucket, key) rather than URL structure.
Kotlin Patterns
Section titled “Kotlin Patterns”-
No
!!— enforce viawhenwith smart cast. The!!operator was used reflexively by agents; it had to be caught and rewritten in every review. Adding it as an explicit rule in the coding standards prevented recurrence. -
Single exit point matters for readability.
return@flatMapscattered through a lambda makes control flow hard to follow. Expression bodies withwhenandflatMapchains are consistently more readable. -
flatInApplicationContextvs explicitctxparameter. The coroutine context approach works when the caller has set upApplicationContextin the coroutine context. The explicitctxparameter is safer in validators where the caller provides it directly. Use the explicit parameter when available. -
Collect-all-errors in validation (using
listOfNotNull+Composite) gives users the full picture. Fail-fast was the agent default; the user corrected it.
Architecture
Section titled “Architecture”-
Single S3AssetService, full capabilities, callers use what they need. Nullable parameters to “disable” capabilities was wrong. The DI pattern is: create one fully-capable instance in Module.kt, inject everywhere.
-
Facade pattern (ImageS3AssetAccess, CsvS3BucketDirectAccess) is the right abstraction level. Consumers interact with the facade; they don’t need to know about
AssetKeyGenerator,CdnUrlResolver, orS3AssetServiceindividually. -
Inject validators, don’t construct them internally.
ItemUniverseshould receiveItemValidatoras a constructor parameter, not create it.BusinessAffiliateValidatorwas the existing precedent. -
Shared constants in a domain package (e.g.,
ImageUploadConstants) prevent magic string drift between producer (service) and consumer (validator). Even better: absorb them into the facade’sConfig.
Process
Section titled “Process”-
Agent-generated code needs human review for design, not just correctness. Agents produce correct code that passes tests, but design decisions (nullable for missing capabilities, internal construction vs DI, multiple returns) need human guidance.
-
CHANGELOG
Changedcategory triggers major version bump. When refactoring is a side effect of adding features, prefer expressing it underAddedorFixedto avoid unnecessary major bumps. -
Helm value loader (
read-cloudFormation-values.cmd) must be updated whenever newrequiredvalues are added toconfigmap.yaml. The deploy pipeline fails at Helm upgrade if values are missing — the build passes but deploy fails. -
PR reviewer bots (Copilot, Codex) catch real issues. The
contentLengthvsmaxFileSizebug, themetadata[it.value]bug, and thectxvs coroutine context issue were all caught by automated reviewers. Their suggestions are worth reading even when they seem verbose.
Copyright: (c) Arda Systems 2025-2026, All rights reserved
Copyright: © Arda Systems 2025-2026, All rights reserved