Skip to main content

Posts

Lambda Cold Starts in Serverless .NET: What Actually Helps and What Doesn't

Cold starts are one of the most discussed and least understood performance topics in serverless .NET. Many of the usual tips either address something you cannot control or were true years ago and no longer are. This guide separates the levers that move the number from the ones that mostly add complexity. What a cold start actually contains When Lambda has no warm execution environment available, it creates one. That includes downloading your code, starting the runtime, and running your initialization code. On .NET, the part you can influence most is the runtime start and your own startup logic: loading assemblies, JIT-compiling code, building the dependency injection container and creating SDK clients. The easiest way to see it is the REPORT line Lambda writes to CloudWatch Logs for each invocation. Cold invocations include an Init Duration field. Query that field in CloudWatch Logs Insights to get p50, p95 and p99 for your own function before changing anything. The levers, fro...
Recent posts

Why Dependency Injection in .NET Modernization Is an Architecture Decision, Not a Testing Tool

Dependency injection (DI) is usually sold as a way to make unit tests easier. In a legacy .NET modernization that is the smallest part of its value. The larger value is that DI forces a team to name the contracts between components that were tangled together for years, and once those contracts exist you can replace what sits behind them one piece at a time. This article is organized as the questions that come up when you start, in the order they usually appear. What problem does DI actually surface? Older .NET code often mixes construction, configuration and execution in one method. A routine that sends an email might create an SMTP client, read a connection string from a config file, build the message, log the attempt, retry on failure and update a database row, all inline. You cannot test it without live dependencies, and you cannot change any one of those concerns without touching the others. DI does not fix that code by itself. What it does is ask an uncomfortable question: wh...

Right-Sizing AWS Lambda Memory: A Measurement Runbook Instead of Guessing

Every Lambda function has a memory setting, and most teams pick a round number such as 512 or 1024 MB and never look at it again. That is a missed chance on both cost and speed, because memory is not only about RAM. Lambda allocates CPU and network capacity in proportion to the memory you configure, so the setting also controls how fast the function runs. This article is a short runbook: what to measure, how to test, and how to decide. Why memory also means speed and cost Lambda bills in gigabyte-seconds: memory (in GB) multiplied by billed duration (in seconds), plus a small per-request charge. A function with 1,024 MB that runs for 500 ms uses 0.5 GB-seconds. At 512 MB with the same duration it would use 0.25 GB-seconds, half the compute cost. But the duration will not necessarily stay the same, because CPU scales with memory. AWS documents that roughly 1.8 GB corresponds to one full vCPU, and the maximum of 10,240 MB provides up to six. A CPU-bound function (parsing, serializat...

Characterization Tests: Locking Down Legacy Behavior Before You Understand It

When you inherit a legacy system that has to be modernized, the usual instinct is to read the code until you understand it and then write tests. In practice that order often fails. The code works in production, nobody is sure why, and your understanding is a set of hypotheses. A better first step is to record what the code actually does today, then change it safely. Tests written for that purpose are called characterization tests , a term popularized by Michael Feathers in Working Effectively with Legacy Code . They document observed behavior, not intended behavior, so they pass even if the behavior is wrong. Their job is to make any change in behavior visible. Why "understand first, test later" breaks down Classic test-first thinking assumes you know the requirements. In a legacy system the requirements are implicit, scattered across service classes, stored procedures and configuration, and shaped by years of patches. If you write tests from your reading of the code, y...

EventBridge Scheduler Pitfalls: Context Attributes, Retries and Dead-Letter Design

EventBridge Scheduler is a good way to run one-off and recurring jobs, but it behaves differently from the event-driven services many teams know, such as SQS or API Gateway triggers. Most problems are not bugs. They come from assumptions about what the scheduler passes to your function, who retries a failure, and what happens to an event nobody catches. This post goes through the pitfalls in a symptom, cause and fix format. Pitfall 1: The function does not know which schedule invoked it Symptom: several schedules call the same function, and logs cannot tell them apart. Cause: the event your function receives is the input you configured on the schedule target, and nothing more. The scheduler does not add an envelope with the rule name and time like an event rule would. If you want metadata, you ask for it in the input. Fix: reference the scheduler context attributes inside the input JSON. The documented attributes are the schedule ARN, the scheduled time, an execution ID and the...

Strangler-Fig Migrations: Replacing a Legacy .NET Monolith One Seam at a Time

A legacy system that runs a real business presents a familiar choice: rewrite it in one risky cutover, or replace it piece by piece while features keep shipping. The strangler-fig pattern, described by Martin Fowler and named after vines that gradually grow around a host tree, takes the second route. You put a routing layer in front of the old system, build new behavior behind it, and move traffic across one slice at a time until the old code has nothing left to do. On a diagram this looks easy. The hard parts are choosing which slice to split first, proving the replacement behaves the same, and living with two systems in production for a long time. This article is a decision guide for those parts. The cycle in five steps Isolate a seam with a clear input and output contract. Shadow it: the new implementation processes the same inputs but does not affect production. Compare the outputs and fix differences. Switch traffic gradually, with an easy way back. Delete the old co...

Least-Privilege IAM and Secret Handling: Design Them In, Don't Clean Them Up Later

IAM policies and secret handling are often treated as a hardening step: get the application working with broad permissions, then tighten things before an audit. That sequence tends to be expensive. Tightening permissions late means rewriting deployment scripts, discovering hidden dependencies and causing outages. Treating least privilege and secret management as design decisions from the first deployment is cheaper and produces systems that are easier to reason about. What broad permissions cost you A role with wide access to S3, a data warehouse and Lambda lets any compromised credential or buggy service touch resources it should never see. The blast radius of one mistake grows with the scope of the policy. There is a quieter cost as well. A broad policy hides the real dependencies of a service. If a function can read every bucket in the account, the policy cannot tell you which bucket it actually uses, so change impact analysis becomes guesswork and incident response gets slower....