AWS CDK
resource identifiers
readability
cloud development
best practices

AWS CDK generated resource identifiers are horrible and not readable. Any way to fix this?

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

AWS CDK names can look ugly because several different identifiers are involved at once: construct IDs, CloudFormation logical IDs, and physical resource names in AWS. CDK intentionally adds hashes and path-derived suffixes to keep resources stable and avoid collisions, so some generated identifiers are not meant to be pretty.

The good news is that you can improve readability, but the right lever depends on which identifier is bothering you. The safest fixes are usually better construct IDs, explicit physical names where appropriate, and better outputs or tags for humans.

Know Which Name You Are Looking At

A lot of confusion comes from mixing these together:

  • construct ID: the name you use in CDK code
  • logical ID: the CloudFormation identifier inside the synthesized template
  • physical name: the actual AWS resource name, when that service exposes one

For example:

typescript
import * as s3 from 'aws-cdk-lib/aws-s3';

new s3.Bucket(this, 'UploadsBucket');

UploadsBucket is the construct ID. The synthesized logical ID will typically include a generated suffix for uniqueness. The actual bucket name may be auto-generated by CloudFormation unless you set it explicitly.

That means "make CDK names readable" can mean three different things.

Start With Better Construct IDs

The simplest improvement is to use clear construct IDs in code. Those IDs influence the synthesized logical IDs and make the stack easier to read during review.

typescript
1new s3.Bucket(this, 'CustomerUploadsBucket');
2new lambda.Function(this, 'ImageResizeFunction', {
3  runtime: lambda.Runtime.NODEJS_20_X,
4  handler: 'index.handler',
5  code: lambda.Code.fromAsset('lambda'),
6});

If your construct IDs are vague, the generated names will inherit that vagueness. Good construct naming is the first, safest readability improvement.

Set Physical Names Only When You Need Them

Some AWS resources let you choose the physical name directly. For example, an S3 bucket can take bucketName, and a Lambda function can take functionName.

typescript
new s3.Bucket(this, 'CustomerUploadsBucket', {
  bucketName: 'customer-uploads-prod-123456',
});

This improves readability in the AWS console, but it comes with tradeoffs. Explicit physical names reduce CDK's flexibility for replacement and can create cross-environment collisions if you reuse the same name in multiple accounts or regions. Use them when human readability or external integration really needs a stable name, not as a blanket rule for every resource.

Override Logical IDs Sparingly

You can override CloudFormation logical IDs, but this is usually a last resort because it ties your CDK code more tightly to the generated template and can make refactoring riskier.

typescript
const cfnBucket = bucket.node.defaultChild as s3.CfnBucket;
cfnBucket.overrideLogicalId('CustomerUploadsBucket');

This can make a template nicer to read, but it is not the first tool to reach for. Logical IDs are part of how CloudFormation tracks resources, so manual overrides deserve extra caution.

Use Outputs And Tags For Human Readability

Sometimes you do not need prettier generated IDs at all. You need better human-facing labels and easier discovery.

typescript
1new cdk.CfnOutput(this, 'UploadsBucketName', {
2  value: bucket.bucketName,
3});
4
5cdk.Tags.of(bucket).add('Name', 'customer-uploads');
6cdk.Tags.of(bucket).add('Environment', 'prod');

Outputs help operators find the important resources after deployment. Tags help the AWS console, cost tools, and team workflows stay readable even when generated logical IDs remain noisy.

Accept That Some Generated Names Are Intentionally Mechanical

CDK-generated suffixes are not accidental ugliness. They help preserve uniqueness and stable mapping between constructs and deployed resources. Trying to remove every hash often means fighting the framework instead of working with it.

A pragmatic approach is:

  • keep construct IDs descriptive
  • set physical names only where the service or humans truly need them
  • use tags and outputs for discoverability
  • avoid overriding logical IDs unless the benefit clearly outweighs the maintenance risk

That gives you most of the readability benefit without sacrificing CDK's deployment model.

Common Pitfalls

One common mistake is trying to force explicit names onto every resource. That creates naming collisions and makes replacement operations harder. Another is overriding logical IDs casually, which can complicate future refactors. Developers also sometimes optimize only for console aesthetics and forget that CDK is designed first around safe synthesis and deployment. Finally, if the team struggles to identify resources, the missing piece is often outputs, tags, or clearer construct naming rather than total control over every generated identifier.

Summary

  • CDK uses several different identifiers, and each one is controlled differently.
  • Improve readability first with clearer construct IDs.
  • Set physical names only where human or integration needs justify the tradeoff.
  • Treat logical ID overrides as an advanced tool, not the default fix.
  • Use tags and outputs to make deployed resources easier for humans to find and understand.

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design