Taskly
Reduced team coordination time 40% with serverless AWS migration handling 10,000+ requests/day
10,000+ requests/day
95% cost reduction
Sub-200ms response
Overview
Full-stack task management platform built for individuals and small teams. Started as a MERN stack app (Express, MongoDB, React) and migrated to a production-grade AWS serverless architecture. The app handles task creation, team collaboration, project tracking, calendar views, productivity analytics, and real-time notifications. The AWS migration replaced the monolithic backend with Lambda functions behind API Gateway, swapped MongoDB Atlas for DocumentDB in a private VPC, and moved file storage to S3 with CloudFront CDN. Infrastructure is fully codified in Terraform (12 modules), deployed via GitHub Actions with OIDC authentication and canary deployments.
Architecture Diagram
Design Decisions
- →Chose serverless over containers (ECS/Fargate) because the traffic pattern is bursty. Lambda scales to zero and costs nothing at idle. DocumentDB was chosen over DynamoDB to keep the existing Mongoose data models intact during migration.
- →VPC isolation for the database layer. Lambda functions run in private subnets with security groups restricting DocumentDB access to port 27017 from Lambda only. No public internet access to the database.
- →Canary deployments with automatic rollback. New Lambda versions receive 10% traffic for 5 minutes. If error rate exceeds 1%, traffic shifts back to the previous version automatically.
- →Infrastructure as Code with Terraform modules. 12 reusable modules (VPC, Lambda, IAM, S3, DocumentDB, WAF, SES, API Gateway, CloudFront, Secrets, Monitoring, DR) that can be composed for any environment.
- →WAF at the API Gateway level with rate limiting (1000 req/15min), IP reputation filtering, SQL injection rules, and XSS protection. Defense in depth rather than relying on application-level validation alone.
Deployment
Production runs on AWS serverless infrastructure deployed via Terraform and GitHub Actions CI/CD. Compute: Lambda functions (Node.js 20) behind HTTP API Gateway. Database: DocumentDB cluster in private VPC subnets (2 AZs). CDN: CloudFront distributions for frontend (S3) and uploads (S3). Security: WAF, Cognito JWT auth, VPC with private subnets, Secrets Manager with auto-rotation. Email: SES with SQS buffering for reliable delivery. Events: EventBridge for async processing (achievements, notifications). Monitoring: CloudWatch dashboards, alarms, SNS alerts. CI/CD: GitHub Actions with OIDC (no stored credentials), canary Lambda deploys. DR: Cross-region S3 replication, Route 53 health checks with DNS failover. All secrets managed via AWS Secrets Manager with KMS encryption. Zero secrets in environment variables or source control.
Lessons Learned
The biggest lesson was that migrating a session-based Express app to stateless Lambda requires rethinking authentication entirely. Sessions don't work when every request might hit a different Lambda instance. The solution was dual-mode auth: Cognito JWT for production (stateless), session-based for local development (familiar DX). DocumentDB's MongoDB compatibility is about 95%, not 100%. Some aggregation operators and index types behave differently. Testing against the actual DocumentDB cluster (not just local MongoDB) caught issues that would have been production bugs. Terraform state management in a team requires discipline. Using S3 backend with DynamoDB locking prevented state corruption, but the real win was splitting infrastructure into composable modules rather than one massive main.tf.


