Scaling Serverless Without Slowing Down Our Development Velocity

Scaling Serverless Without Slowing Down Our Development Velocity

At Hoomanely, we're building a preventive pet healthcare platform for pet parents that has tools and features ranging from a pet-parents community to an AI-powered pet care chat assistant. Our mission demands rapid iteration, we need to test hypotheses quickly, deploy features fast, and scale efficiently without the overhead of managing infrastructure.

During our rapid prototyping phase, we faced a critical challenge: how do we deploy Python applications to AWS Lambda quickly and reliably while maintaining the agility needed for fast experimentation? What started as an exciting serverless journey quickly turned into a deployment nightmare that threatened to slow down our entire development cycle.

The problem: deployment friction at every turn

Our initial approach seemed straightforward, manually package our Flask application, upload it to AWS Lambda, configure API Gateway, set up environment variables, and deploy. Simple, right? Not quite.

  • Packaging hell: every deployment meant creating a deployment package with all dependencies, zipping it up, and uploading to S3. For a typical Flask app with libraries like pandas, numpy, and our ML dependencies, packages easily exceeded 50MB, Lambda's direct upload limit, forcing us to use S3 for every single deployment.
  • Configuration sprawl: each Lambda function needed manual configuration in the AWS Console, memory settings, timeout values, environment variables, IAM roles, VPC settings. Multiply this by multiple environments and multiple functions, and you have a maintenance nightmare.
  • API Gateway complexity: wiring up API Gateway to Lambda functions manually is tedious. Every endpoint requires careful configuration of integration requests, responses, CORS settings, and authorisation. One misconfiguration and your API returns cryptic errors.
  • Rollback anxiety: when something broke in production, rolling back meant remembering which version worked, finding that deployment package, and repeating the entire upload process. No simple undo button.
  • Development vs production gap: code that worked perfectly on our local machines would mysteriously fail on Lambda. Debugging meant adding print statements, repackaging, uploading, and checking CloudWatch logs, a cycle that could take 5-10 minutes per iteration. The most frustrating part? Each deployment cycle took 15-20 minutes of manual work. For a team iterating multiple times daily, this was unacceptable.

Exploring the solution space

We evaluated several approaches. AWS SAM promised infrastructure-as-code with a cleaner deployment process, but still required understanding CloudFormation, configuration files quickly became verbose, and limited support for Flask or Django-specific patterns kept deployment times slow for Python applications with large dependencies.

The Serverless Framework offered a provider-agnostic approach with a large community, but felt JavaScript-centric with Python as a secondary concern, required additional plugins for Python packaging, and dependency management wasn't optimised for Python's ecosystem.

Custom CI/CD scripts gave complete control but demanded significant upfront investment in building and maintaining scripts, and the team would need to become experts in deployment tooling instead of focusing on product. None of these felt like the right fit. We needed something Python-first, dead simple, and fast.

Zappa: the Python-first solution

Zappa caught our attention with a simple promise: deploy Python WSGI applications to AWS Lambda with a single command. No complicated configuration, no infrastructure knowledge required, just zappa deploy.

Zappa is purpose-built for Python web frameworks. It understands Flask, Django, Pyramid, and Bottle out of the box. More importantly, it treats Lambda and API Gateway as implementation details, not configuration surfaces you need to master.

Installing Zappa took one command, pip install zappa. Initialising it for our project took another, zappa init. Zappa asked a few questions, which AWS profile, which environment name, which Python version, and generated a minimal zappa_settings.json:

{
    "dev": {
        "app_function": "app.app",
        "aws_region": "ap-south-1",
        "project_name": "hoomanely-api",
        "runtime": "python3.9",
        "s3_bucket": "hoomanely-zappa-deploys"
    }
}

That's it. Fifty lines of configuration replaced with eight. Running zappa deploy dev triggered magic. Zappa packaged our application and dependencies intelligently, created an optimised deployment package using slim pre-compiled wheels, set up Lambda functions with sensible defaults, configured API Gateway with all routes automatically, and generated a working API endpoint. Total time: 2 minutes. We went from no infrastructure to a working API in 120 seconds.

The benefits we actually got

  • Instant iteration: subsequent deployments use zappa update dev, which is even faster, typically 30-45 seconds. The same deployment that took 15-20 minutes manually now happens while we grab coffee.
  • Intelligent dependency management: Zappa automatically detects which packages need special handling. Libraries like numpy and pandas get replaced with Lambda-optimised versions. Large dependencies are pulled from Lambda Layers. We stopped worrying about package size.
  • Zero-downtime deploys: Zappa handles deployment atomically. It uploads the new version, switches traffic, then cleans up. If something fails, the old version keeps running.
  • Effortless rollback: zappa rollback dev instantly reverts to the previous deployment. We've used this in production twice, each time breathing a sigh of relief that it took 10 seconds instead of 10 minutes.
  • Environment parity: the same Flask app that runs on our laptops runs on Lambda without modification. Zappa handles the WSGI-to-Lambda translation transparently. Debugging became straightforward again.
  • CloudWatch integration: zappa tail dev streams logs in real-time to our terminal. No more switching to the AWS Console and hunting through CloudWatch.
  • Here's what changed for our team: deploy frequency went from 2-3 times per week to 2-3 times per day. Deployment time went from 15-20 minutes to under 1 minute. Failed deployments went from around 20% due to manual errors to near 0%. Time to rollback went from 10-plus minutes to 10 seconds.

Key takeaways

  • Choose tools designed for your stack.
  • Python-first tools understand Python problems.
  • Zappa's deep integration with WSGI frameworks eliminated entire categories of issues we faced with generic serverless tools.
  • Deployment friction kills velocity.
  • Every minute spent on deployment is a minute not spent building features.
  • Simplicity scales better than complexity.
  • Zappa's minimal configuration isn't a limitation, it's a feature.
  • And the best infrastructure is invisible.
  • Zappa abstracted away Lambda and API Gateway complexity without sacrificing power.
  • If you're building Python web applications and considering serverless, don't manually wrestle with Lambda packaging and API Gateway configuration.
  • Start with Zappa, deploy in minutes, and iterate fast.