The AWS Certified Developer – Associate (DVA-C02) validates the ability to develop, deploy, and debug cloud applications on AWS. These five concepts show up repeatedly in DVA-C02 scenarios.
1. AWS Lambda and Serverless Development#
Lambda is the backbone of serverless development on AWS and represents a significant portion of the DVA-C02 exam. You must understand execution context, concurrency, and common pitfalls.
Tip
Lambda is pay-per-use compute: you are charged per request and duration, it scales with demand, and AWS operates the runtime.
Lambda Execution Model#
Understanding the Lambda execution lifecycle is critical for performance optimization:
# ✅ CORRECT: Initialize connections OUTSIDE the handler
import boto3
from pymysql import connect
# This runs once per execution environment (cold start)
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('users')
db_connection = connect(host='...', user='...', password='...')
def handler(event, context):
# This runs on EVERY invocation - keep it lean!
user_id = event['user_id']
response = table.get_item(Key={'id': user_id})
return response['Item']
Warning
Common Exam Trap: Questions will present code that creates database connections inside the handler. This anti-pattern causes massive performance variance and connection exhaustion. Always initialize connections in the global scope.
Key Lambda Limits to Memorize#
| Resource | Limit | Exam Trap |
|---|---|---|
| Maximum timeout | 15 minutes | Questions may suggest 30 minutes |
| Max memory | 10,240 MB | CPU scales proportionally with memory |
| Deployment package (zipped) | 50 MB | 250 MB unzipped |
| Ephemeral storage (/tmp) | 10,240 MB | Default is 512 MB |
| Environment variables | 4 KB total | For all variables combined |
Lambda Concurrency#
- Reserved concurrency — Guarantees capacity but limits maximum
- Provisioned concurrency — Keeps execution environments warm to eliminate cold starts
- Unreserved concurrency — Shared pool across all functions (default)
Note
Setting reserved concurrency to 0 effectively disables a Lambda function — this can be used as an emergency kill switch.
Test Your Understanding#
A Lambda function processes files uploaded to S3. Large files (400 MB) cause timeouts. The function has 15-minute timeout and 3,008 MB memory. What should the developer do?
2. DynamoDB for Developers#
DynamoDB is AWS's flagship NoSQL database and appears heavily on the DVA-C02. You must understand data modeling, capacity modes, and the SDK.
Key Concepts#
- Partition Key — Determines data distribution; high cardinality is essential
- Sort Key — Enables range queries within a partition
- Global Secondary Index (GSI) — Alternate partition/sort key for different access patterns
- Local Secondary Index (LSI) — Same partition key, different sort key (must be created at table creation)
Tip
Remember: GSI = Goes anywhere (can add anytime), LSI = Locked at creation.
Capacity Modes#
| Mode | Pricing | Best For | Exam Points |
|---|---|---|---|
| On-Demand | Pay-per-request | Unpredictable workloads | Scales instantly, higher per-request cost |
| Provisioned | Reserved capacity | Predictable workloads | Use Auto Scaling for variable traffic |
DynamoDB Streams and Event-Driven Patterns#
DynamoDB Streams capture item-level changes and can trigger Lambda functions:
// Lambda handler for DynamoDB Stream events
export const handler = async (event) => {
for (const record of event.Records) {
console.log('Event Type:', record.eventName); // INSERT, MODIFY, REMOVE
console.log('New Image:', record.dynamodb.NewImage);
console.log('Old Image:', record.dynamodb.OldImage);
}
};
Warning
Exam Trap: Stream records are available for 24 hours. If your Lambda consumer fails for longer, you'll lose data. Implement a Dead Letter Queue (DLQ) for failed records.
Conditional Writes for Idempotency#
Prevent duplicate processing with conditional expressions:
import boto3
from botocore.exceptions import ClientError
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('orders')
def process_order(order_id, order_data):
try:
table.put_item(
Item={'order_id': order_id, **order_data},
ConditionExpression='attribute_not_exists(order_id)'
)
return {'status': 'created'}
except ClientError as e:
if e.response['Error']['Code'] == 'ConditionalCheckFailedException':
return {'status': 'already_exists'}
raise
A developer needs to query DynamoDB using an attribute that isn't part of the primary key. The table is already in production. What should they use?
3. API Gateway and Authentication#
API Gateway is the front door to your serverless applications. The DVA-C02 tests REST APIs, HTTP APIs, and various authentication/authorization patterns.
API Types Comparison#
| Feature | REST API | HTTP API |
|---|---|---|
| Cost | Higher | ~70% cheaper |
| Features | Full-featured | Core features |
| Authorizers | Lambda, Cognito, IAM | Lambda, JWT, IAM |
| Caching | Built-in | Not available |
| Request validation | Built-in | Not available |
Note
Use REST API when you need caching, request validation, or Cognito User Pool authorizers. Use HTTP API for simple, cost-effective APIs with JWT authorization.
Authorization Patterns#
- IAM Authorization — For AWS service-to-service calls using SigV4
- Cognito User Pool Authorizer — Validates JWT tokens from Cognito
- Lambda Authorizer — Custom logic for tokens or request parameters
# Lambda Authorizer example
def handler(event, context):
token = event['authorizationToken']
method_arn = event['methodArn']
# Custom validation logic
if validate_token(token):
return generate_policy('user123', 'Allow', method_arn)
else:
return generate_policy('user123', 'Deny', method_arn)
def generate_policy(principal_id, effect, resource):
return {
'principalId': principal_id,
'policyDocument': {
'Version': '2012-10-17',
'Statement': [{
'Action': 'execute-api:Invoke',
'Effect': effect,
'Resource': resource
}]
}
}
Warning
Exam Trap: You cannot use BOTH a Cognito User Pool authorizer AND a Lambda authorizer on the same API method. If you need Cognito authentication PLUS custom authorization logic, use a Lambda authorizer that validates the Cognito JWT and implements your business rules.
Stage Variables and Deployment#
- Stages — Named references to deployment (dev, staging, prod)
- Stage Variables — Configuration values that differ by stage
- Canary Deployments — Route percentage of traffic to new deployment
# Access stage variables in Lambda via mapping template
${stageVariables.functionAlias}
${stageVariables.tableName}
A developer needs to authenticate users with Cognito AND authorize based on subscription tier. Which approach should they use?
4. AWS Security for Developers#
Security is 26% of the DVA-C02 exam. You must understand IAM, Cognito, Secrets Manager, and KMS integration patterns.
IAM for Lambda#
Lambda functions assume an execution role that defines what AWS services they can access:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"dynamodb:GetItem",
"dynamodb:PutItem"
],
"Resource": "arn:aws:dynamodb:us-east-1:123456789012:table/users"
},
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "*"
}
]
}
Tip
Follow the principle of least privilege: grant only the permissions required, to only the specific resources needed. Use resource-level ARNs whenever possible.
Secrets Manager vs Parameter Store#
| Feature | Secrets Manager | Parameter Store |
|---|---|---|
| Cost | $0.40/secret/month | Free (standard) |
| Rotation | Built-in automatic | Manual |
| Cross-account | Native support | Via IAM |
| Best for | Database credentials, API keys | Configuration, non-sensitive values |
Secrets Manager Lambda Extension#
For high-throughput functions, use the Lambda extension for caching:
import os
import urllib3
# Secrets Manager extension caches secrets in memory
SECRETS_EXTENSION_PORT = 2773
secrets_url = f'http://localhost:{SECRETS_EXTENSION_PORT}/secretsmanager/get?secretId='
def handler(event, context):
http = urllib3.PoolManager()
response = http.request('GET', f"{secrets_url}my-secret",
headers={'X-Aws-Parameters-Secrets-Token': os.environ['AWS_SESSION_TOKEN']})
secret = response.data.decode('utf-8')
# Use secret...
Warning
Exam Trap: Storing secrets in Lambda environment variables is NOT recommended for sensitive data, even with KMS encryption. Use Secrets Manager or Parameter Store SecureString. Environment variables are visible in the Lambda console.
KMS for Encryption#
- Envelope encryption — Data key encrypts data, KMS key encrypts data key
- Client-side encryption — Encrypt before sending to AWS
- Server-side encryption — AWS encrypts at rest
import boto3
kms = boto3.client('kms')
# Encrypt data
response = kms.encrypt(
KeyId='alias/my-key',
Plaintext=b'sensitive data'
)
ciphertext = response['CiphertextBlob']
# Decrypt data
response = kms.decrypt(
CiphertextBlob=ciphertext
)
plaintext = response['Plaintext']
A Lambda function accesses a third-party API using credentials. It's invoked 10,000 times per minute. What's the MOST cost-effective approach?
5. Deployment and CI/CD#
Deployment patterns and infrastructure as code are critical for the DVA-C02. Understand CodePipeline, SAM, and deployment strategies.
AWS SAM (Serverless Application Model)#
SAM is an extension of CloudFormation specifically for serverless:
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Globals:
Function:
Timeout: 30
Runtime: python3.11
Environment:
Variables:
TABLE_NAME: !Ref UsersTable
Resources:
MyFunction:
Type: AWS::Serverless::Function
Properties:
Handler: app.handler
CodeUri: src/
Events:
Api:
Type: Api
Properties:
Path: /users
Method: GET
Policies:
- DynamoDBReadPolicy:
TableName: !Ref UsersTable
UsersTable:
Type: AWS::DynamoDB::Table
Properties:
BillingMode: PAY_PER_REQUEST
AttributeDefinitions:
- AttributeName: id
AttributeType: S
KeySchema:
- AttributeName: id
KeyType: HASH
Lambda Deployment Strategies#
| Strategy | Description | Use Case |
|---|---|---|
| AllAtOnce | 100% traffic immediately | Development, testing |
| Canary | X% for Y minutes, then 100% | Quick validation |
| Linear | X% every Y minutes | Gradual, safe rollout |
Note
Combine deployment preferences with CloudWatch Alarms for automatic rollback. If the alarm triggers during deployment, traffic shifts back to the original version.
CodeBuild Best Practices#
# buildspec.yml
version: 0.2
phases:
install:
runtime-versions:
python: 3.11
pre_build:
commands:
- pip install -r requirements.txt -t ./package
build:
commands:
- sam build
- sam package --s3-bucket $S3_BUCKET --output-template-file packaged.yaml
post_build:
commands:
- sam deploy --template-file packaged.yaml --stack-name $STACK_NAME --capabilities CAPABILITY_IAM
cache:
paths:
- '/root/.cache/pip/**/*' # Cache pip dependencies
Tip
Enable CodeBuild local caching for dependencies to prevent transient network failures during builds. This dramatically improves build reliability and speed.
Blue/Green Deployments with CodeDeploy#
For Lambda, CodeDeploy manages traffic shifting between function versions:
# SAM template with CodeDeploy integration
MyFunction:
Type: AWS::Serverless::Function
Properties:
AutoPublishAlias: live
DeploymentPreference:
Type: Linear10PercentEvery3Minutes
Alarms:
- !Ref ErrorAlarm
Hooks:
PreTraffic: !Ref PreTrafficHook
PostTraffic: !Ref PostTrafficHook
A developer wants to gradually shift traffic to a new Lambda version over 30 minutes with automatic rollback on errors. Which deployment type?
Know your range before test day#
Quizzes catch knowledge gaps; a timed DVA-C02 sitting shows which items are fluent, fragile, guessed, or a gap under the clock.
- Timed diagnostic with domain-level breakdown
- Clear signal on what to drill next
- Practice free; timed sittings and readiness range on Pro
Take the free Developer Associate assessment →
Next step: Start practicing free on the DVA-C02 hub, or sit a short diagnostic first.