Best Practices
Learn industry best practices for CDK development with real-world examples. See how to build secure, performant, and cost-effective infrastructure using AWS CDK.
💡 Note: These are general AWS CDK best practices. CDK Insights automatically detects many of these issues, including: unencrypted S3 buckets, overly permissive IAM policies, missing versioning, public access configurations, and more. The examples below show both what to avoid (which CDK Insights will flag) and recommended implementations.
Best Practice Categories
Security
Essential security practices for CDK applications
- Principle of least privilege
- Encryption at rest and in transit
- Network security controls
- Secrets management
Performance
Optimization strategies for better performance
- Resource sizing optimization
- Caching strategies
- Connection pooling
- Auto-scaling configuration
Cost Optimization
Cost-effective infrastructure patterns
- Right-sizing resources
- Reserved instances usage
- Lifecycle policies
- Monitoring and alerting
Maintainability
Code organization and maintainability
- Consistent naming conventions
- Modular architecture
- Comprehensive documentation
- Testing strategies
Security Best Practices
S3 Bucket Security
Implement secure S3 bucket configuration
❌ Avoid This:
// ❌ Insecure S3 bucket
new s3.Bucket(this, 'DataBucket', {
  publicReadAccess: true,
  removalPolicy: RemovalPolicy.DESTROY,
  // No encryption, no access logging
});✅ Do This Instead:
// ✅ Secure S3 bucket
new s3.Bucket(this, 'DataBucket', {
  // Block all public access
  blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
  
  // Enable encryption
  encryption: s3.BucketEncryption.S3_MANAGED,
  
  // Enable versioning
  versioned: true,
  
  // Enable access logging
  serverAccessLogsBucket: accessLogsBucket,
  
  // Lifecycle policy for cost optimization
  lifecycleRules: [
    {
      id: 'DeleteIncompleteMultipartUploads',
      abortIncompleteMultipartUploadAfter: Duration.days(1),
    },
    {
      id: 'TransitionToIA',
      transitions: [
        {
          storageClass: s3.StorageClass.INFREQUENT_ACCESS,
          transitionAfter: Duration.days(30),
        },
      ],
    },
  ],
  
  // Prevent accidental deletion
  removalPolicy: RemovalPolicy.RETAIN,
});Benefits:
IAM Role Best Practices
Create least-privilege IAM roles
❌ Avoid This:
// ❌ Overly permissive IAM role
new iam.Role(this, 'LambdaRole', {
  assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'),
  managedPolicies: [
    iam.ManagedPolicy.fromAwsManagedPolicyName('AdministratorAccess'), // Too broad!
  ],
});✅ Do This Instead:
// ✅ Least-privilege IAM role
new iam.Role(this, 'LambdaRole', {
  assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'),
  
  // Use minimal managed policies
  managedPolicies: [
    iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaBasicExecutionRole'),
  ],
  
  // Specific inline policies
  inlinePolicies: {
    S3Access: new iam.PolicyDocument({
      statements: [
        new iam.PolicyStatement({
          effect: iam.Effect.ALLOW,
          actions: ['s3:GetObject'],
          resources: [
            `${dataBucket.bucketArn}/specific-folder/*`,
          ],
        }),
        new iam.PolicyStatement({
          effect: iam.Effect.ALLOW,
          actions: ['s3:PutObject'],
          resources: [
            `${outputBucket.bucketArn}/processed/*`,
          ],
        }),
      ],
    }),
  },
  
  // Set maximum session duration
  maxSessionDuration: Duration.hours(1),
});Benefits:
VPC Security Configuration
Secure network configuration for VPC
❌ Avoid This:
// ❌ Insecure VPC configuration
const vpc = new ec2.Vpc(this, 'VPC', {
  maxAzs: 2,
});
// Public subnet with no restrictions
const publicSubnet = vpc.publicSubnets[0];
new ec2.SecurityGroup(this, 'WebSG', {
  vpc: vpc,
  allowAllOutbound: true,
});
publicSubnet.securityGroup.addIngressRule(
  ec2.Peer.anyIpv4(),
  ec2.Port.allTraffic(), // Too permissive!
);✅ Do This Instead:
// ✅ Secure VPC configuration
const vpc = new ec2.Vpc(this, 'VPC', {
  maxAzs: 2,
  subnetConfiguration: [
    {
      cidrMask: 24,
      name: 'Public',
      subnetType: ec2.SubnetType.PUBLIC,
    },
    {
      cidrMask: 24,
      name: 'Private',
      subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS,
    },
    {
      cidrMask: 28,
      name: 'Database',
      subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
    },
  ],
});
// Web tier security group
const webSecurityGroup = new ec2.SecurityGroup(this, 'WebSG', {
  vpc: vpc,
  description: 'Security group for web servers',
  allowAllOutbound: false,
});
webSecurityGroup.addIngressRule(
  ec2.Peer.anyIpv4(),
  ec2.Port.tcp(80),
  'Allow HTTP from anywhere'
);
webSecurityGroup.addIngressRule(
  ec2.Peer.anyIpv4(),
  ec2.Port.tcp(443),
  'Allow HTTPS from anywhere'
);
// Application tier security group
const appSecurityGroup = new ec2.SecurityGroup(this, 'AppSG', {
  vpc: vpc,
  description: 'Security group for application servers',
  allowAllOutbound: false,
});
appSecurityGroup.addIngressRule(
  webSecurityGroup,
  ec2.Port.tcp(8080),
  'Allow traffic from web tier'
);Benefits:
Performance Best Practices
Lambda Function Optimization
Optimize Lambda function configuration
❌ Inefficient:
// ❌ Inefficient Lambda configuration
new lambda.Function(this, 'DataProcessor', {
  runtime: lambda.Runtime.NODEJS_14_X, // Outdated runtime
  handler: 'index.handler',
  code: lambda.Code.fromAsset('lambda'),
  memorySize: 3008, // Maximum memory (expensive)
  timeout: Duration.minutes(15), // Too long
  // No environment variables, no dead letter queue
});✅ Optimized:
// ✅ Optimized Lambda configuration
new lambda.Function(this, 'DataProcessor', {
  runtime: lambda.Runtime.NODEJS_18_X, // Latest LTS
  handler: 'index.handler',
  code: lambda.Code.fromAsset('lambda'),
  
  // Right-sized memory based on actual usage
  memorySize: 512,
  
  // Appropriate timeout
  timeout: Duration.minutes(5),
  
  // Environment variables
  environment: {
    LOG_LEVEL: 'INFO',
    MAX_RETRIES: '3',
    S3_BUCKET: dataBucket.bucketName,
  },
  
  // Dead letter queue for failed invocations
  deadLetterQueue: new sqs.Queue(this, 'DLQ'),
  
  // Reserved concurrency to prevent cost spikes
  reservedConcurrency: 10,
  
  // Enable X-Ray tracing
  tracing: lambda.Tracing.ACTIVE,
  
  // Retry configuration
  retryAttempts: 2,
});Benefits:
RDS Database Optimization
Optimize RDS database configuration
❌ Inefficient:
// ❌ Inefficient RDS configuration
new rds.DatabaseInstance(this, 'Database', {
  engine: rds.DatabaseInstanceEngine.mysql({
    version: rds.MysqlEngineVersion.VER_5_7, // Outdated version
  }),
  instanceType: ec2.InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.XLARGE),
  allocatedStorage: 100, // Fixed storage
  storageEncrypted: false, // No encryption
  backupRetention: Duration.days(7), // Short retention
  // No monitoring, no parameter group
});✅ Optimized:
// ✅ Optimized RDS configuration
new rds.DatabaseInstance(this, 'Database', {
  engine: rds.DatabaseInstanceEngine.mysql({
    version: rds.MysqlEngineVersion.VER_8_0, // Latest version
  }),
  
  // Right-sized instance
  instanceType: ec2.InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.MEDIUM),
  
  // Auto-scaling storage
  allocatedStorage: 20,
  maxAllocatedStorage: 100,
  
  // Enable encryption
  storageEncrypted: true,
  storageEncryptionKey: encryptionKey,
  
  // Enhanced monitoring
  monitoringInterval: Duration.seconds(60),
  enablePerformanceInsights: true,
  
  // Backup configuration
  backupRetention: Duration.days(30),
  deleteAutomatedBackups: false,
  
  // Parameter group for optimization
  parameterGroup: new rds.ParameterGroup(this, 'DBParams', {
    engine: rds.DatabaseInstanceEngine.mysql({
      version: rds.MysqlEngineVersion.VER_8_0,
    }),
    parameters: {
      innodb_buffer_pool_size: '{DBInstanceClassMemory*3/4}',
      max_connections: '100',
    },
  }),
  
  // Multi-AZ for high availability
  multiAz: true,
  
  // VPC configuration
  vpc: vpc,
  vpcSubnets: {
    subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
  },
});Benefits:
Cost Optimization Best Practices
Auto Scaling Configuration
Implement cost-effective auto scaling
// ✅ Cost-optimized auto scaling
const autoScalingGroup = new autoscaling.AutoScalingGroup(this, 'ASG', {
  vpc: vpc,
  instanceType: ec2.InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.MICRO),
  machineImage: ec2.MachineImage.latestAmazonLinux(),
  minCapacity: 1,
  maxCapacity: 10,
  desiredCapacity: 2,
  
  // Use Spot instances for cost savings
  mixedInstancesPolicy: {
    instancesDistribution: {
      onDemandPercentageAboveBaseCapacity: 20, // 20% on-demand, 80% spot
      spotAllocationStrategy: autoscaling.SpotAllocationStrategy.CAPACITY_OPTIMIZED,
    },
    instancesTypes: [
      ec2.InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.MICRO),
      ec2.InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.SMALL),
    ],
  },
  
  // Scaling policies
  scalingPolicies: [
    {
      id: 'ScaleUp',
      scalingTarget: {
        metric: new cloudwatch.Metric({
          namespace: 'AWS/EC2',
          metricName: 'CPUUtilization',
          statistic: 'Average',
        }),
        targetValue: 70,
      },
      scaleOutCooldown: Duration.minutes(5),
      scaleInCooldown: Duration.minutes(10),
    },
  ],
  
  // Lifecycle hooks for graceful shutdowns
  lifecycleHook: {
    defaultResult: autoscaling.DefaultResult.ABANDON,
    heartbeatTimeout: Duration.minutes(5),
  },
});Benefits:
Implementation Checklist
Security Checklist
- Enable encryption for all storage services
- Implement least-privilege IAM policies
- Use VPC security groups appropriately
- Enable CloudTrail logging
- Implement secrets management
- Use HTTPS/TLS everywhere
Performance Checklist
- Right-size all compute resources
- Implement auto-scaling policies
- Use appropriate caching strategies
- Enable monitoring and alerting
- Optimize database configurations
- Use CDN for static content
Ready to Implement Best Practices?
Start applying these best practices to your CDK projects and use CDK Insights to validate your implementations.