Carrier gives enterprise teams a cleaner way to express service architecture, but it does not prevent poor design by itself. The most common pitfalls come from carrying old habits into a more structured tool.
Overusing Routes
Routes are necessary, but they should not become the center of the architecture. A common mistake is putting too much logic in routes. When route bodies contain workflow decisions, validation rules, persistence sequences, authorization details, and transaction behavior, the service becomes harder to test and govern. A better pattern is route → action → transaction.
Underusing Actions
Underusing actions usually happens when teams treat Carrier as a thin API declaration layer and keep the real behavior in route code or ad hoc helper functions. That loses one of Carrier's main architectural benefits. Use actions for operations that are transactional, auth-aware, reused by multiple routes, meaningful as business workflows, lock-heavy or consistency-sensitive, or important enough to audit or govern.
Weak Module Boundaries
Modules are powerful, but imports create dependencies. Weak boundaries show up as shared modules full of unrelated declarations, domain modules importing each other's internals, service-specific models reused as if they were enterprise contracts, and common modules changing constantly because they have no clear owner. A useful test: if a module changes every time one product team changes its workflow, it is probably not a shared module — it is that team's domain logic leaking outward.
Treating Generated Code as Source
Carrier generates artifacts under .carrier/. These files can be useful for diagnostics, but they are outputs, not source. Teams should not hand-edit generated files to fix behavior. Doing so creates a false state that will be overwritten by the next generation step. The correct workflow is fix Carrier source under src/, run carrier check, then regenerate.
Ignoring Schema Migration Discipline
Schema changes are among the highest-risk changes in enterprise systems. Carrier can generate migrations, but generated does not mean automatically safe. A safer strategy is expand-and-contract: add new schema in a compatible way, deploy code that supports both old and new shapes, backfill, switch, and only then remove the old schema.