# SAM Project Layout ## Standard Directory Structure ``` project-name/ ├── template.yaml # SAM template at root ├── samconfig.toml # Deploy config (gitignored) ├── samconfig.toml.example # Template for onboarding (committed) ├── src/ │ ├── function_name/ │ │ ├── app.py # Lambda handler │ │ └── requirements.txt # Per-function dependencies │ └── shared/ # Shared layer code (if needed) └── .gitignore ``` ## File Purposes ### `template.yaml` The SAM/CloudFormation template. Lives at the project root. Defines all Lambda functions, IAM roles, API Gateway endpoints, DynamoDB tables, and other resources. ### `samconfig.toml` Contains real ARNs, S3 bucket names, and deploy parameters. **Gitignored** because it varies per environment and may contain account-specific values. ### `samconfig.toml.example` A committed template showing the expected structure and parameter names. New contributors copy this to `samconfig.toml` and fill in their values. ### `src/function_name/` One directory per Lambda function. Each contains its own handler (`app.py`) and dependencies (`requirements.txt`). This keeps functions independently deployable and avoids bloating one function with another's dependencies. ### `src/shared/` Optional. Used for code shared across multiple functions, typically deployed as a Lambda layer. See the next section for layout details. ## Lambda Layers When sharing code across functions via a layer, the source layout matters because SAM transforms `ContentUri` depending on whether `BuildMethod` is set. ### Correct layout (with `BuildMethod`) ``` src/shared/ ├── shared/ # Package goes here directly — SAM wraps in python/ at build time │ ├── __init__.py │ └── utils.py └── requirements.txt # Layer-level pip dependencies ``` Template: ```yaml SharedLayer: Type: AWS::Serverless::LayerVersion Properties: LayerName: my-stack-shared ContentUri: src/shared/ CompatibleRuntimes: - python3.12 CompatibleArchitectures: - arm64 Metadata: BuildMethod: python3.12 BuildArchitecture: arm64 ``` ### BuildMethod nesting gotcha With `BuildMethod: python3.12`, SAM copies `ContentUri` into a `python/` subdirectory during build, then pip-installs `requirements.txt` deps into that same `python/` directory. Do **not** include a `python/` wrapper in your source — SAM adds it. The wrong layout: ``` src/shared/ ├── python/ # SAM wraps this again → python/python/shared/ — module unreachable │ └── shared/ └── requirements.txt ``` A real production incident (~22 hours of outage) traced to this exact pattern when a refactor moved layer code under an extra `python/` directory. ### Without BuildMethod (raw zip) If the layer has no pip dependencies and you omit `BuildMethod`, SAM zips `ContentUri` as-is — you DO need the `python/` wrapper. Reserve raw zip for layers that ship only Python source. ## Standard .gitignore ``` .aws-sam/ __pycache__/ *.pyc .env samconfig.toml ```