The Serverless Mindset Before You Draw a Single Box
The nine principles I now run every serverless design through, starting with the one nobody puts first: does this even need to exist?
The first time I looked at a real serverless architecture diagram, my reaction was: this is absurd.
A dozen boxes. Arrows crossing everywhere. Events firing into queues that fire into more functions that fire into a state machine. My instinct was the same one most engineers have looking at it cold: just put this on one server and call it a day. One codebase, one deploy, one thing to reason about.
That instinct is wrong, and understanding why it's wrong is the whole mindset shift.
Complexity doesn't disappear, it moves
Every one of those connected boxes represents a piece of logic that has to exist somewhere. Put it all on one server and the logic doesn't vanish, it just moves inside your code. One function now branches into charge-the-card, notify-the-warehouse, refund-on-failure, retry-on-timeout, all tangled together in a single call stack.
You've traded a complex diagram for a complex function. You didn't remove complexity, you just hid it somewhere harder to see.
ExpandComplexity doesn't disappear, it moves from the architecture into the code or back
Split the same logic into small, single-purpose functions instead, and each one gets simple: validate the order, charge the card, notify the warehouse. The connections between them, the retries, the ordering, the failure paths, move out of your function bodies and into the architecture, where you can actually see them.
This is the real design decision every time you draw a system: do you want the complexity in your code, or in your architecture? Serverless is a bet that architecture-level complexity is easier to manage than code-level complexity, because you can observe it, test each piece in isolation, and scale each piece independently. That bet only pays off if you follow it through with the rest of these principles, not just the first one.
The eight rules that make the trade pay off
Once you accept that complexity moves instead of disappearing, the rest of the mindset is about making sure it moves somewhere manageable.
-
Start from the business requirement, not the service catalog.
- Opening the AWS console and picking the interesting-looking services first is building a resume, not a system.
- The architecture has to satisfy the real requirement first, both what the system must do and how well it must do it.
- A great-looking pipeline that misses your latency budget or skips a business rule is still a failed design, no matter how clean the diagram looks.
-
Reach for serverless first, not by default.
- Once the requirement is clear, ask one real question: can this be solved with a serverless service?
- Serverless services don't charge for idle time, and nobody has to patch a server fleet at 2 AM.
- That's the difference between a team that ships features and a team that babysits servers.
- If a pure serverless answer genuinely doesn't fit, a hybrid with containers is a fallback, not the starting point.
-
Use managed services instead of rebuilding them.
- Every major cloud provider ships managed, serverless offerings for compute, storage, content delivery, and APIs.
- These already plug into your logging, permissions, and monitoring, so you don't wire that up yourself.
- Rebuilding a queue or an auth layer from scratch when a managed version already exists just means more code your team has to maintain forever.
-
Decouple components, don't just distribute them.
- The rule: high cohesion inside a component, loose coupling between components.
- Inside one component, everything should be closely related and do one clear job.
- Between components, talk through events or APIs, never by reaching straight into each other's internals.
- Get this right and you can scale the checkout function without scaling the whole system.
- Get it wrong and you've built a distributed monolith: all the network overhead of microservices, none of the independence.
-
Build in observability from day one.
- A single server has one log file. A serverless system can have a dozen components, each with its own logs and traces.
- When something breaks, you're looking for one bad request among millions of good ones.
- Tracing that follows a single request across every service it touches is not optional here.
- Without it, the same architecture that gave you clean, simple functions gives you almost no way to debug them when they fail together.
-
Design for extensibility, and room to experiment.
- Real projects have real constraints: deadlines that can't move, budgets already spent, requirements that changed last week.
- What you can control is whether the architecture bends or breaks when the next requirement shows up.
- A new event type should mean a new listener on an existing event bus, not a rewrite of the producer.
- The same openness should make trying new ideas cheap. If adding one new service means touching five existing functions, nobody will ever propose the idea.
-
Treat security as a layer, not a checkbox.
- Companies have gone from thriving to explaining a data breach in a single news cycle.
- Regulations like GDPR turn a security gap into a legal bill, not just a technical one.
- Security has to be enforced at every layer on its own: the API boundary, the function's own permissions, the data store, the network path between them.
- One strong layer covering for three weak ones doesn't make you secure. It just means an attacker has one weak layer left to find.
-
Match the data store to the job.
- Cloud platforms offer document stores, relational databases, graph databases, search-optimized stores, and in-memory caches.
- Forcing every workload through one relational database is an old habit, and purpose-built stores exist so you don't have to keep it.
- Full-text search belongs in something like Elasticsearch, not bolted onto a relational database with a
LIKE '%query%'search. - Data that should disappear in milliseconds belongs in an in-memory store, not a table you clean up with a scheduled job.
- One application using several databases, each picked for what it's good at, is normal here, not over-engineering.
None of these eight rules is exotic on its own. What turns them into a mindset instead of a checklist is applying all of them together, before the first Lambda function gets written, on a system built for a real business problem instead of a toy example.
The Essentials
- Complexity never disappears, it relocates. Serverless is a bet that architecture-level complexity is easier to manage than code-level complexity.
- Business requirements come before service selection. A clever architecture that misses the actual requirement is still a failed design.
- Ask "can this be serverless?" first. The payoff is no idle-time billing and no infrastructure babysitting, not architectural fashion.
- Prefer managed services over rebuilding them. They already integrate with your logging, auth, and monitoring.
- Decouple with high cohesion, low coupling. Components should scale independently, not just live in separate repos.
- Observability is not optional in a distributed system. Without tracing, one bad request is a needle in a haystack.
- Design the connection points before you need them. Real projects rarely get the perfect first design, and the same openness makes new ideas cheap to try, not just changes cheap to absorb.
- Enforce security at every layer on its own. One strong layer covering for three weak ones just means an attacker has one weak layer to find.
- Pick the data store for the job, not out of habit. One application can and should use multiple purpose-built stores.
Further Reading and Watching
Keep reading