We've inherited enough 2-year-old startup codebases to see the patterns. There's a specific set of decisions that every founding team thinks will save time in month 1, and that every subsequent team curses for the next two years.
This isn't a list of 'use this framework, not that one'. It's about the decision-making anti-patterns that lead to regret — regardless of which specific tools you choose.
Anti-pattern #1: choosing for familiarity, not fit
The most common expensive mistake: a founding engineer uses the stack they know best, without asking whether it's the right fit for the product. A Django monolith is a fine way to ship a B2B SaaS MVP. It's a painful foundation for a real-time collaborative tool that will need WebSockets, event-driven architecture, and horizontal scaling at 50,000 concurrent users.
The question to ask at the start: 'What will this product look like at 10x current scale, and does our stack choice make that migration harder or easier?' You don't need to build for 10x on day one. But you shouldn't be forced into a complete rewrite to get there.
Anti-pattern #2: ORMs that hide the database
Modern ORMs are powerful and developer-friendly. They're also one of the most common sources of catastrophic performance issues at scale. The problem: an ORM that auto-generates SQL is an ORM that can auto-generate terrible SQL as your data model grows complex.
We recommend using an ORM for standard CRUD operations and explicit raw SQL for anything involving multiple JOINs, aggregations, or queries over large datasets. Know what SQL your ORM is generating. If you can't explain a query plan to your team, you won't be able to diagnose it at 3am when it starts causing timeouts in production.
Anti-pattern #3: one database for everything
Relational databases are remarkably versatile. But using PostgreSQL as your transactional store, your audit log, your real-time notification queue, your full-text search engine, and your session store simultaneously is asking it to do too many things at once.
- Use Redis for sessions, rate limiting, and short-lived caches — not Postgres.
- Use a dedicated search index (Typesense, Meilisearch, or Postgres + pgvector for AI) for full-text search — not LIKE queries on a varchar column.
- Use a queue (BullMQ, SQS) for background jobs — not a database polling pattern.
The one thing that actually predicts success
After reviewing dozens of early-stage codebases, the strongest predictor of long-term maintainability isn't the framework, the ORM, or the cloud provider. It's the discipline around one thing: separating your domain logic from your infrastructure.
Teams that put business logic in their API route handlers, their database models, and their UI components simultaneously end up with code that can't be tested, can't be understood, and can't be changed without ripple effects across the system.
The investment: one extra day of architecture work at the project start to define clear boundaries between layers. The return: every subsequent engineer can reason about the codebase confidently, and every new feature takes days instead of weeks.
Want expert help with Strategy?
Tell us about your project — we reply within one business day.
Start a conversationMore articles


