Skip to content
All posts

Most Architecture Advice Assumes You Have a Team. You Don’t.

June 5, 2026·Read on Medium·

The design patterns that actually hold up when it’s just you running production.

Every system design article you will read this week was written for teams. Not small teams. Real teams, with dedicated DevOps engineers, separate frontend and backend squads, a platform group that maintains shared infrastructure and someone whose actual job is to think about service boundaries.

If that is your reality, great. Most of those articles are probably useful.

If you are a solo technical lead or one of three engineers holding up a production system, that advice is quietly setting you up for years of maintenance pain.

I have spent the last several years running systems where the ratio of engineers to moving parts in production is not encouraging. What I keep finding is that the patterns most celebrated in architecture discourse are exactly the ones that hurt you worst when you are the only person who understands the system end to end. The elaborate setups that earn applause in architecture reviews become debt that no one can afford to pay down.

This article is not for the team with a dozen engineers and a healthy DevOps practice. It is for everyone else.

The Team-Size Assumption Nobody Talks About

Here is the assumption buried in 90% of architecture advice: the problem it is solving is coordination between humans, not technical problems.

Microservices exist because keeping fifty engineers from stepping on each other’s code is genuinely hard. Bounded contexts in domain-driven design exist because when your team is large enough, you want clear ownership lines. CQRS with separate read and write models exists partly because you can have dedicated teams owning each side of the model. Event sourcing exists because you want different teams to subscribe to state changes without tight coupling.

These are real solutions to real coordination problems. None of those coordination problems exist when two people own the entire system.

What is left after you strip out the coordination value? Operational complexity. You still have to maintain separate deployments, manage inter-service communication, handle distributed tracing, deal with failures across service boundaries and ensure schema consistency across multiple data stores. All the overhead is still there. The benefit, the human coordination piece, is gone.

This is the silent tax that kills solo and small-team codebases. You adopted a pattern designed for a forty-person team. You are paying the overhead of a forty-person team. You have two people.

Gartner projects that by 2030, 80% of organizations will have moved toward smaller engineering teams enhanced by AI tools. That means the “tiny team” is not a transitional phase you are supposed to escape. It may just be the default state for a generation of engineers. Understanding what architecture actually works at that scale is not a secondary concern.

Three Patterns That Break Specifically in Small Teams

Microservices with Separate Repos and Deployments

The pitch is clean: independent deployability, bounded context ownership, isolated failures. In practice, for a small team, this means waking up at 2am and spending forty-five minutes figuring out whether the bug is in Service A, Service B or the network layer between them, then discovering it was a config drift.

The cognitive overhead of context-switching between multiple services is real and cumulative. Engineers in flow working on a feature should not have to track five repositories, three Dockerfile variations and two different CI/CD configurations for a change that touches a single domain concept. Every context switch is a tax on focus, and small teams cannot afford that tax the way large teams can absorb it.

Small teams also do not have the time to properly maintain service boundaries under pressure. In practice, under deadline stress, the boundary becomes fiction. Teams start making direct database calls across service lines, introduce chatty HTTP dependencies between services or copy logic across codebases to avoid touching the “other” service. The microservices architecture gives you the illusion of separation while actual coupling grows invisibly underneath. You get the worst of both worlds: the constraints of a distributed system with the coupling of a monolith.

Event-Driven Everything with a Heavy Message Broker

Async event systems are powerful. Kafka, RabbitMQ and AWS SQS are genuinely useful tools when you have the operational maturity to run them. The problem is that event-driven architectures require serious operational discipline to work safely in production. Dead-letter queues need monitoring. Event schema changes need coordination. Consumer lag needs alerting. Message ordering guarantees need ongoing verification.

None of this is magic. Someone has to own it. In a large team, that might be an entire platform group. In a three-person team, that is your Friday evening, every sprint.

When everything is async and event-driven, debugging a production issue becomes an archaeology project. You are reconstructing what happened from event logs across multiple consumers and producers, tracing causality through a system that was designed to decouple exactly that causality. For experienced teams with mature tooling, this is manageable. For a small team that also has to ship features, it is a permanent drag that never quite gets resolved.

Aggressive CQRS Without Dedicated Ownership

Command Query Responsibility Segregation has legitimate uses, particularly when read and write loads are wildly asymmetric and you can justify the synchronization complexity. The pattern genuinely helps when you have separate teams who can own each side and the scale to make the tradeoff worthwhile.

For small teams, it usually means doubling the code surface area, maintaining two models of the same domain concept and explaining to every new hire why the read side and write side have diverged slightly since the last quarter. The synchronization logic between models is rarely trivial, and it tends to be the least-tested part of the system because it is the part everyone trusts implicitly.

In practice, most small-team systems do not have the read/write asymmetry that justifies this pattern. What they have is an architecture astronaut who read the right book at the wrong time.

What Actually Works (and Nobody Posts About It)

The pattern that holds up best for small teams is the one that architecture influencers treat as a stepping stone you are supposed to outgrow: the modular monolith.

This is not a monolith in the pejorative sense. It is not a giant ball of mud where everything depends on everything else. A well-built modular monolith has clear module boundaries enforced at the code level, explicit interfaces between modules and shared-nothing conventions within each module. It looks like microservices on the inside but deploys as one unit.

In Laravel terms, this means structuring your application into feature modules with their own service providers, their own models and repositories and strict rules about cross-module access. Each module can theoretically be extracted into a separate service later if you actually need to. In practice, most teams never need to.

# Laravel modular monolith directory structure
app/
Modules/
Billing/
BillingServiceProvider.php
Models/
Services/
Http/
Notifications/
NotificationServiceProvider.php
Models/
Services/
Orders/
OrderServiceProvider.php
Models/
Services/
Http/

The cognitive benefit is real. You get the clarity of service boundaries without the operational overhead of distributed systems. When something breaks, it is still one application. Your logs are in one place. Your deploy is one command. A new team member can understand the structure in a morning, not a week.

For async work, a database-backed queue handles most of what small teams need without introducing a separate broker that has to be operated and monitored. If you are on Laravel, the built-in queue system backed by Redis or a database driver covers the vast majority of async processing needs at a fraction of the operational complexity of a full message broker. The queue is part of your application. The failure modes are already known. Your alerting is already configured.

This is not a permanent ceiling. If you hit genuine scale problems, you can introduce a real message broker at that point, with the context of actual measured bottlenecks rather than anticipated ones. The modular monolith is not a trap. It is a foundation you can build from rather than one you have to refactor away from.

The Database Question Small Teams Usually Get Wrong

One area where small-team architecture goes sideways fast is database design, specifically the temptation to go polyglot too early.

You see it in startups and solo projects: Postgres for relational data, Redis for caching, MongoDB for documents, Elasticsearch for search, all running by the time the product has fifty users. Each of these tools is legitimate. Running all of them simultaneously on a team of two is an infrastructure tax that compounds with every deploy, every incident and every onboarding.

For small teams, the right question is not “which database is best for this data shape” but “how many databases can we actually operate well.” In most cases, the answer is one, with Redis as a second only when you genuinely need caching or queuing at a scale your primary database cannot handle.

Postgres, in particular, is remarkably capable before it becomes a bottleneck. Full-text search, JSON storage, time-series data with partitioning, queue-like patterns with SKIP LOCKED: the list of use cases that Postgres covers before you need another system is longer than most teams realize. The operational simplicity of a single well-tuned database is worth more to a small team than the theoretical elegance of purpose-built storage for every data type.

What AI Augmentation Changes About This Calculus

Here is where the picture is shifting. As Gartner’s projection noted, small teams enhanced by AI tools are becoming the norm rather than the exception. What does that mean for architecture?

AI coding assistance reduces one of the biggest constraints of small teams: context retention. An AI assistant with access to your full codebase can help a solo engineer navigate a modular monolith without the cognitive cost of remembering every module’s conventions. This makes it more feasible for a small team to manage more surface area than before.

But this does not make poorly structured complexity manageable. It makes well-structured simplicity more powerful. An AI assistant is dramatically more effective on a codebase with clear, consistent conventions than on one where every module has its own idiom, where service boundaries are documented nowhere and where the async patterns are inconsistent between teams.

If AI tools are going to be deeply involved in your development workflow, you have more reason to keep your architecture simple and explicit, not less. The complexity tax compounds on both humans and models.

One thing AI augmentation does not solve: operational incidents at 3am. The architecture that fails at 3am is not the one you will debug with a coding assistant. It is the one you will debug half-awake, reading logs and checking dashboards. The simpler your architecture, the faster that investigation goes. That calculus does not change with AI.

The Twenty-Minute Test

Here is the filter worth applying before adopting any architectural pattern:

“Can a developer who has never seen this codebase understand this component’s purpose and failure modes in twenty minutes, working alone?”

If the answer is no, the pattern is probably wrong for your team size. Not wrong forever. Wrong now.

This is not about avoiding ambition or pretending scale problems do not exist. It is about being honest that the biggest risk in most small-team systems is not insufficient architecture. It is insufficient people to maintain the architecture you already have.

Senior engineers have a specific failure mode here. We know about the advanced patterns. We have seen them work at scale. We want to do the interesting technical things, and the interesting technical things are usually distributed systems problems. That is how you end up with a three-person startup running eight microservices, a Kafka cluster and a service mesh that nobody fully understands.

The boring correct answer is to start with the simplest architecture that could possibly work, add complexity only when you have a specific measured problem that simpler architectures can no longer solve and document what you add and why.

That is the whole model for small-team system design. Everything else is either a distraction or a future problem.

The patterns that scale are not always the ones that scale up. The ones that work for small teams scale with the team’s actual capacity to understand and maintain what is running in production. That is a different axis entirely, and most architecture content will never tell you that because the content is not written for your team. It was written for theirs.

Found this helpful?

If this article saved you time or solved a problem, consider supporting — it helps keep the writing going.

Originally published on Medium.

View on Medium
Most Architecture Advice Assumes You Have a Team. You Don’t. — Hafiq Iqmal — Hafiq Iqmal