Articles
AWS CDK Interview Questions: Constructs, Tokens, Logical IDs, and Safe Refactoring
Practice AWS CDK interview questions with a concrete construct-tree refactor, token timing, logical-ID checks, and safer change review.
A CDK interview question often looks like a code-cleanup question until the interviewer asks what will happen to a deployed bucket. The decisive answer is to trace the construct path into the synthesized CloudFormation logical ID, then separate an unresolved token from a value the program can know at synthesis. A prettier class hierarchy is not automatically a safe deployment. Compare the old and new templates, inspect replacement risk, and make any resource-preserving refactor a deliberate operation.
This is a worked engineering guide, not a report of an AWS hiring loop. Official facts below come from AWS CDK and CloudFormation documentation. Editorial inference labels the hypothetical OrdersStack exercise and review decisions. No candidate report establishes that AWS or another employer asks these exact prompts.
First question: what is a construct, and which ID matters?
A construct is a node in CDK’s tree. Its second constructor argument is an ID unique within its parent scope, not a globally unique resource name. A stack, a reusable wrapper, an L2 Bucket, and its underlying L1 CloudFormation resource all occupy positions in that tree. node.path gives a path for reasoning about ownership; cdk synth produces the template that CloudFormation actually receives. AWS’s identifier guide distinguishes construct IDs, paths, unique IDs, logical IDs, and physical names. Collapsing them into “the resource name” conceals the deployment hazard.
CloudFormation tracks a resource by logical ID within a stack. CDK ordinarily derives that ID from its construct path, with readable components and an eight-character hash where required. The algorithm has documented exceptions: Default is omitted from both the readable part and hash input; Resource is hidden from the readable portion but remains in the hash input. Avoid predicting a real ID by eye. Synthesize both versions and compare actual Resources keys. A physical bucket name can be explicitly supplied or service-generated and is a different identity. An unchanged physical-name property does not by itself prove CloudFormation will retain the same resource.
An interviewer might ask why two children can both be called Logs. If they have different parents, their scoped IDs are legal and paths differ. Two children named Logs under the same parent collide at construction. The distinction matters when wrapping resources in a shared component: the wrapper’s ID becomes another path segment unless a documented convention removes it.
A refactor exercise: move code without moving a bucket
Assume a deployed OrdersStack has a bucket declared directly under the stack. Product engineers want a reusable OrderStorage construct, and the bucket holds retained order exports. The code fragments are deliberately simplified; properties, policies, and asset dependencies must still be compared in the real template.
// Before: path OrdersStack/OrderExports/Resource
new s3.Bucket(this, 'OrderExports');
// Proposed wrapper usage
new OrderStorage(this, 'OrderExports');
class OrderStorage extends Construct {
constructor(scope: Construct, id: string) {
super(scope, id);
new s3.Bucket(this, 'Default');
}
}
Under AWS’s documented Default path rule, the primary bucket’s new path is OrdersStack/OrderExports/Default/Resource, but the omitted Default component can preserve its generated logical ID. That is a conditional design technique, not a blanket proof of safe deployment. Only one child per scope can have the ID Default; newly added policy or notification resources can receive different paths and may change the template. If you rename the wrapper to Storage at the same time, the retained path component changes and the technique no longer protects the old bucket ID.
| Comparison item | Before | After the proposed wrapper | Interview decision |
|---|---|---|---|
| Primary bucket path | OrdersStack/OrderExports/Resource | OrdersStack/OrderExports/Default/Resource | Check the actual synthesized logical ID. |
| Wrapper ID | None | OrderExports | Retain the meaningful old path component. |
| Bucket properties | Existing template | Must match unless change is intentional | Separate configuration changes from tree movement. |
| Generated child resources | Existing policy, if any | Potentially new paths | Review every resource, not only the bucket. |
To make the exercise testable, save or version the deployed baseline template, synthesize the proposed change using the same application context, and diff the Resources maps. A template assertion can lock the bucket’s logical ID and its important properties. AWS’s best-practices guide explicitly recommends keeping logical IDs of stateful resources stable and testing them. A unit test checks the candidate template; cdk diff adds a view of the proposed deployment. Neither is a substitute for reviewing account state and the CloudFormation change set for the target environment.
What actually happens if the logical ID changes?
If CloudFormation sees the old logical ID removed and a new one added, it normally treats them as different resource identities. AWS warns that this can create a new resource and delete the old one, with interruption or data loss possible. Whether the deletion succeeds, whether retention policies apply, whether a physical name conflicts, and whether an update requires replacement are resource-specific details. Do not answer “CDK renames the bucket” merely because the TypeScript variable was renamed. A TypeScript local variable can be freely renamed without changing the construct tree; changing the construct ID or scope is materially different.
cdk diff is an early alarm, but describe its limits. A template-only difference may not show every service-level consequence, and a change set is tied to a particular deployed stack. Check the diff for [-] and [+] resources, property changes, IAM changes, and output/reference changes. When a bucket appears to be deleted and created, stop the routine deploy. Capture the intended mapping, data-retention requirements, rollback plan, and the current stack template. The safe answer is a review decision, not a memorized CLI incantation.
For an already-changed path, AWS now documents cdk refactor, a preview resource-preserving mechanism. Its current documentation says a refactoring operation must be separate from additions, deletions, and property modifications; it also has environment and construct restrictions and uses an unstable option. That makes it a targeted migration tool, not a general “ignore the diff” switch. If your organization cannot meet those constraints, redesign the extraction to preserve IDs or plan an explicit CloudFormation resource migration with operator review. State which option you are proposing and why.
Second question: why is bucket.bucketName a token?
An automatically named bucket may not have its final physical name when the CDK application is constructed. AWS calls the placeholder a token: the value is carried through synthesis and resolved in the deployed CloudFormation environment. Logging it in TypeScript may display an encoded token, not a live bucket name. In the synthesized template, the property can become a CloudFormation Ref or another intrinsic expression. AWS’s token guide shows this exact bucket-name pattern.
This means bucket.bucketName is appropriate when a CDK property accepts a deploy-time value, such as an environment variable on a Lambda function. It is not suitable as input to a synthesis-time branch that expects a concrete string. For example, testing if (bucket.bucketName === 'prod-exports') to decide which constructs to add asks the program to know something not yet resolved. Use explicit environment configuration or construct props for synth-time decisions. Preserve tokens as values when passing them into compatible CDK properties; do not parse the displayed ${Token[...]} representation as if it were the real resource name.
The inverse trap is assuming every CDK value is late-bound. A specified literal bucket name, a context value loaded before synthesis, or an environment value provided to the app can be known early. Ask “at which phase is this value known?” before classifying it. Token.isUnresolved can help detect unresolved values, but an unresolved token is not automatically an error. It is an error only when your operation needs a concrete value earlier than CDK can provide it.
A useful three-phase answer is: construct the tree using code and known configuration; synthesize a CloudFormation template containing resolved literals and intrinsics; deploy the template so CloudFormation and AWS services supply late-bound values. Avoid promising that cdk synth contacts a service to discover a newly generated physical name. Review the synthesized expression instead. This phase model also explains why an output value can be printed after deployment while remaining a token in the source program.
Third question: how do you review a permission change?
Suppose the wrapper adds bucket.grantRead(worker) while the bucket is being moved. The source change is one line; the synthesized change may include an IAM policy resource or modifications to an existing policy. The safe review reads the relevant policy statement, its principal, actions, resource ARN, and conditions. A “no bucket replacement” result does not mean the deployment is unchanged. Conversely, a new policy logical ID may be expected while the bucket ID remains stable.
Keep the ownership boundary clear. CDK convenience methods can synthesize supporting resources and grant statements, but permissions are judged by the resulting IAM policy. Re-run cdk synth and cdk diff, then inspect the candidate change set. If the reviewer sees wildcard actions or a broadened resource scope, do not hide behind the construct API name. Explain the intended read path and smallest permission surface. Where cross-stack references or imported resources are involved, check which stack owns the policy and how exports/imports change; the wrapper’s nice abstraction does not cancel CloudFormation ownership rules.
For the interview, state an ordered review: freeze the baseline, make one kind of change, synthesize, compare logical IDs, inspect IAM and other supporting resources, review the environment-specific change set, deploy through normal controls, and verify the actual resource identity afterward. This is editorial operational advice based on the mechanisms above, not an AWS guarantee that these steps alone prevent every deployment failure.
Fast answers to CDK follow-ups
Does changing a TypeScript variable rename a CloudFormation resource? No, not by itself. The construct ID, scope/path, and generated template are what matter.
Why do logical IDs contain a hash? CDK needs unique CloudFormation IDs even when concatenated paths can look alike. Direct stack children and path heuristics have documented exceptions, so inspect synthesis rather than constructing the value manually.
Can I always use Default to refactor safely? No. The wrapper ID must preserve the relevant old path; only one Default child is allowed per scope; supporting resources and properties still need a diff.
Does an unchanged physical name prove an unchanged resource? No. CloudFormation tracks the logical ID, and service-specific replacement, retention, and naming behavior can matter.
Is a token an unresolved bug? Usually it is an intentional placeholder. It becomes a problem when code tries to branch, parse, or validate it as a concrete synth-time value.
When would you use cdk refactor? For a resource move or rename that meets the current preview feature’s restrictions, after reviewing its mapping and keeping the operation separate from property changes.