You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Raised from the review of #375, which flagged this in its description rather than deciding it inline.
ADR-0009 closes with an explicit escalation clause:
The fix is per-instance. If default-vs-sibling collisions recur, that is the signal to weigh a generic, typed primitive in @composurecdk/core rather than repeating the pattern.
#375 added three more instances at once, bringing the total to six across five packages — and, more tellingly, they are written in three different shapes.
The instances
Package
Default
Yields to
Shape
dynamodb
billingMode: PAY_PER_REQUEST
readCapacity / writeCapacity
inline delete merged.billingMode
dynamodb
encryption: AWS_MANAGED
encryptionKey
inline merged.encryption = CUSTOMER_MANAGED
ec2
VPC maxAzs
availabilityZones
(pre-existing)
s3
encryption: S3_MANAGED
encryptionKey
extracted encryptionKeyProps() helper
sqs
encryption: SQS_MANAGED
encryptionMasterKey
extracted encryptionMasterKeyProps() helper
kms
enableKeyRotation: true
a non-rotatable keySpec
inline delete merged.enableKeyRotation
Two of those helpers (s3 and sqs) are the same function under two names — identical signature and body, differing only in the prop name and the enum constant.
Why it is worth deciding now rather than at instance seven
No canonical form to copy. A seventh author picks one of three shapes by coin flip. The three are not equivalent either: "delete the default and let CDK infer" and "assign the customer-managed value explicitly" produce the same template today but rest on different assumptions about CDK's inference staying put.
The rule is subtle enough to get wrong. Each site has to distinguish the user set this prop from the default set it — reading the user's props, not the merged object. That is the kind of thing a primitive should encode once.
A bug in the yield logic is currently a six-place fix.
What to weigh
Sketch, not a proposal — the shape is the actual question:
// in @composurecdk/core — CDK-agnostic, so no aws-cdk-lib importyieldsTo(defaults,userProps,[{default: "encryption",sibling: "encryptionKey",replaceWith: BucketEncryption.KMS},{default: "billingMode",sibling: ["readCapacity","writeCapacity"]},// drop, no replacement]);
Open questions the ADR did not settle:
Drop vs. replace. Some sites drop the default and rely on CDK's inference; others substitute a specific value. Does the primitive support both, or standardise on one? (Verified for feat(kms): add @composurecdk/kms and widen key-consuming props to Resolvable #375: Bucket, Queue and the classic Table all infer the customer-key mode from the key alone, so "drop" is sufficient in every current encryption case — but that is a CDK behaviour the library would then be depending on.)
Typed how? Keying on prop-name strings is weakly typed and loses the compile-time link to the props interface. Something like keyof Props helps, but the replacement value's type has to line up with the prop it replaces.
Where?core is CDK-agnostic and stays that way, which the sketch above respects. But if the primitive wants to know about CDK enums, cloudformation is the precedent home (see ADR-0010's reasoning for putting the constraint mechanism there rather than in core).
Is it worth it at all? A legitimate outcome is "no" — six call sites of 6-10 lines each, and a primitive that is harder to read than the code it replaces is a bad trade. The ADR says weigh, not build. If the answer is no, the cheaper fix is to standardise the three shapes on one and say so in the ADR.
Acceptance
A decision recorded — either a new ADR superseding/amending ADR-0009's escalation clause, or an amendment to ADR-0009 saying the pattern stays per-instance and naming the canonical shape
If a primitive is built: the six existing sites migrated to it, with no template changes (assert via the examples' snapshot tests)
Raised from the review of #375, which flagged this in its description rather than deciding it inline.
ADR-0009 closes with an explicit escalation clause:
#375 added three more instances at once, bringing the total to six across five packages — and, more tellingly, they are written in three different shapes.
The instances
dynamodbbillingMode: PAY_PER_REQUESTreadCapacity/writeCapacitydelete merged.billingModedynamodbencryption: AWS_MANAGEDencryptionKeymerged.encryption = CUSTOMER_MANAGEDec2maxAzsavailabilityZoness3encryption: S3_MANAGEDencryptionKeyencryptionKeyProps()helpersqsencryption: SQS_MANAGEDencryptionMasterKeyencryptionMasterKeyProps()helperkmsenableKeyRotation: truekeySpecdelete merged.enableKeyRotationTwo of those helpers (
s3andsqs) are the same function under two names — identical signature and body, differing only in the prop name and the enum constant.Why it is worth deciding now rather than at instance seven
What to weigh
Sketch, not a proposal — the shape is the actual question:
Open questions the ADR did not settle:
Bucket,Queueand the classicTableall infer the customer-key mode from the key alone, so "drop" is sufficient in every current encryption case — but that is a CDK behaviour the library would then be depending on.)keyof Propshelps, but the replacement value's type has to line up with the prop it replaces.coreis CDK-agnostic and stays that way, which the sketch above respects. But if the primitive wants to know about CDK enums,cloudformationis the precedent home (see ADR-0010's reasoning for putting the constraint mechanism there rather than incore).Acceptance