I had just learned about microservices.

They were amazing. Scalable. Independent. Deployable separately. The future of architecture.

So naturally, I rewrote my side project as microservices.

The project: A simple todo app. Maybe 100 users.

What I built:

  • 7 separate services
  • Docker containers for each
  • Kubernetes for orchestration
  • API gateway
  • Service mesh
  • Distributed logging
  • Service discovery

Time to build: 3 weeks

Time to build as a monolith: 2 days

My manager saw it and laughed.

Manager: “Why did you use microservices for this?”

Me: “Because microservices are better than monoliths.”

Manager: “Better for what? At what scale? For what team size?”

Me: “…I don’t know.”

That was the problem.

I had learned microservices. But I hadn’t learned when to use them.

I’d learned the hammer. But I thought everything was a nail.

The Context Problem

Most learning focuses on the “what” and “how.”

What is Redux? How do you use it?

But rarely the “when.”

When should you use Redux vs. Context API vs. local state?

This is the skill gap that separates:

  • Beginners: “I know how to use X.”
  • Experts: “I know when to use X vs. Y vs. Z.”
graph TD A[Problem] --> B{Beginner Approach} A --> C{Expert Approach} B --> D[Use the tool I know] C --> E[Analyze context] E --> F[What are the constraints?] E --> G[What are the tradeoffs?] E --> H[What's the scale?] E --> I[What's the team size?] F --> J[Choose appropriate tool] G --> J H --> J I --> J style D fill:#ff6b6b style J fill:#6bcf7f

Beginners have tools. Experts have judgment.

Story: The Two Database Designs

Two developers need to add a feature: user comments.

Developer A (knows tools, not context)

Thinking: “I know MongoDB. I’ll use MongoDB.”

Implementation: Comments in MongoDB.

Problem: The rest of the app uses PostgreSQL. Now there are two databases. The team doesn’t know MongoDB. Queries across databases are complex.

Result: Technical debt. Increased complexity.

Developer B (knows context)

Thinking:

  1. Constraint: “Team only knows PostgreSQL. Adding a new database has training cost.”
  2. Scale: “We’ll have maybe 1000 comments. Not big data.”
  3. Relationships: “Comments reference users and posts, both in PostgreSQL.”
  4. Tradeoff: “MongoDB might be slightly more flexible, but PostgreSQL is simpler for this use case.”

Decision: Use PostgreSQL.

Result: Consistent tech stack. Lower complexity. Team can maintain it.

The difference: Developer B asked “when should I use MongoDB?” not just “can I use MongoDB?”

The Contextual Decision Framework

For every tool or approach you learn, ask:

Question 1: What problem does this solve?

Not: “What can this do?”

But: “What specific problem is this designed for?”

Example: Redux

What can it do: Manage state in React apps.

What problem does it solve: Global state management in large apps with complex data flow.

When to use: When you have:

  • Many components sharing state
  • Complex update logic
  • Need for time-travel debugging
  • Large team needing predictable patterns

When NOT to use: When you have:

  • Mostly local component state
  • Simple data flow
  • Small app
  • Just you or small team

Question 2: What are the tradeoffs?

Every tool has costs and benefits.

Example: TypeScript

Benefits:

  • Catch errors at compile time
  • Better autocomplete
  • Self-documenting code
  • Easier refactoring

Costs:

  • Learning curve
  • More code to write
  • Build step required
  • Can slow down prototyping

The decision depends on context:

Large team, long-term project? TypeScript wins.

Solo dev, weekend hack? Maybe skip it.

Question 3: What are the constraints?

Real-world constraints determine what’s actually viable:

Common constraints:

  • Time: “We need this shipped tomorrow.”
  • Team knowledge: “Nobody on the team knows X.”
  • Budget: “We can’t afford enterprise tools.”
  • Scale: “We have 10 users, not 10 million.”
  • Existing system: “We already use Y, and it’s working.”

Example:

You want to use: Kubernetes

Constraints:

  • Team: 2 developers, neither knows Kubernetes
  • Scale: 50 users
  • Budget: $0 for infrastructure
  • Timeline: 2 weeks to launch

The right choice: Heroku or similar PaaS. Not Kubernetes.

Why: Kubernetes is overkill. The constraints make it a bad fit.

Question 4: What’s the future state?

Think beyond the immediate need.

Ask:

  • Will this scale with growth?
  • Will this be easy to maintain?
  • Will this constraint future decisions?

Example:

Immediate need: Store 100 records.

Bad choice: CSV file (works now, breaks at scale).

Better choice: SQLite (works now, scales to thousands).

Good choice: PostgreSQL (works now, scales to millions).

The right choice depends on expected growth.

The Skill Selection Matrix

Here’s how experts choose:

graph TD A[New Problem] --> B{Is this problem familiar?} B -->|Yes| C[Use proven solution] B -->|No| D{What are the constraints?} D --> E[Time Constraint?] D --> F[Team Constraint?] D --> G[Scale Constraint?] D --> H[Budget Constraint?] E --> I[List viable options] F --> I G --> I H --> I I --> J[Evaluate tradeoffs] J --> K[Choose best fit] K --> L[Document why] style C fill:#6bcf7f style K fill:#4d96ff style L fill:#ffd93d

The key step most people skip: “Document why.”

If you can’t articulate why you chose X over Y, you don’t understand the context.

Real-World Examples

Example 1: State Management

Problem: Manage state in a React app.

Options: Local state, Context API, Redux, Zustand, Jotai, Recoil.

The decision tree:

1. Is the state local to one component?

→ Yes: Use useState. Done.

2. Is it shared between a few nearby components?

→ Yes: Lift state up or use Context API. Done.

3. Is it global state needed everywhere?

→ Yes: Continue…

4. Is the update logic complex?

→ Yes: Use Redux (predictable updates).

→ No: Use Zustand or Context (simpler).

5. Do you need time-travel debugging?

→ Yes: Redux DevTools.

→ No: Simpler solution.

6. What does your team know?

→ They know Redux: Use Redux.

→ They don’t: Use simpler alternative.

The point: The “best” tool depends on 6+ factors, not one.

Example 2: Testing Strategy

Problem: Test a web application.

Options: Unit tests, integration tests, E2E tests, manual testing.

The decision:

Context 1: Startup, 2 devs, MVP

  • 80% manual testing
  • 20% critical path E2E tests
  • 0% unit tests initially

Why: Speed matters most. Comprehensive testing slows iteration.

Context 2: Enterprise, 50 devs, production app

  • 70% unit tests
  • 20% integration tests
  • 10% E2E tests
  • Minimal manual testing

Why: Reliability matters most. Many devs need safety nets.

Same problem. Different context. Different solution.

Example 3: Code Organization

Problem: Structure a JavaScript project.

Options: One file, multiple files, modules, folders by feature, folders by type.

The decision:

Project size: 100 lines

→ One file. Don’t over-engineer.

Project size: 1,000 lines

→ Multiple files by purpose (api.js, utils.js, components.js).

Project size: 10,000 lines

→ Folder structure by feature. Clear modules.

Project size: 100,000 lines

→ Monorepo with packages. Enforce boundaries.

The context (project size) determines the structure.

Building Contextual Judgment

How do experts develop this judgment?

Tactic 1: Learn the Failure Modes

For every tool, learn when it fails.

Example: Microservices

When they work:

  • Large organization
  • Independent teams
  • Different scaling needs per service
  • High throughput requirements

When they fail:

  • Small team (coordination overhead too high)
  • Low traffic (complexity not worth it)
  • Tight coupling between services (lost the benefits)

Learning the failure modes teaches you the boundaries.

Tactic 2: Study Post-Mortems

Read about when things went wrong.

Example post-mortem patterns:

“We chose X, but we should have chosen Y because…”

  • “We chose microservices for a small team. Coordination cost killed us.”
  • “We chose MongoDB for relational data. Query complexity exploded.”
  • “We chose to build our own auth. Security vulnerabilities were inevitable.”

These teach you the context where each tool fails.

Tactic 3: The “Why Now?” Question

Every time you see a tool recommended, ask:

“Why is this being recommended now? What changed?”

Example:

2015: “Use jQuery.”

2025: “Don’t use jQuery.”

What changed: Modern browsers have better APIs. Frameworks handle DOM manipulation better.

Lesson: jQuery was right for 2010 context. Wrong for 2025 context.

Understanding what changed teaches you what context matters.

Tactic 4: Compare and Contrast

Don’t just learn tools. Learn how they compare.

Example comparison: REST vs. GraphQL

REST is better when:

  • Simple CRUD operations
  • Caching is critical
  • Team knows REST well
  • Public API (widely understood)

GraphQL is better when:

  • Complex data requirements
  • Mobile apps (minimize requests)
  • Rapid UI iteration
  • Internal API (you control clients)

This comparison teaches you the decision criteria.

Tactic 5: Track Your Decisions

Keep a decision log.

Every significant technical decision:

  1. What did you choose?
  2. What were the alternatives?
  3. Why did you choose this?
  4. What was the context?

Review it quarterly.

Ask: “Was this the right decision? What would I do differently?”

Example from my log:

Date: 2023-05-12

Decision: Use PostgreSQL instead of MongoDB for user data.

Alternatives: MongoDB, MySQL

Why:

  • Relational data (users, posts, comments all related)
  • Need ACID transactions
  • Team knows SQL
  • Scale is manageable (<1M rows)

6 months later: Still the right call. Relational queries are common. Transactions were crucial.

Learning: Trust relational databases for relational data.

The Anti-Patterns

Anti-Pattern 1: Resume-Driven Development

The trap: Choose tools to pad your resume, not to solve the problem.

Example:

“I’ll use Kubernetes for this project. Not because we need it, but because I want Kubernetes on my resume.”

Result: Over-engineered, hard to maintain.

The fix: Choose tools for the problem, not for your career.

Anti-Pattern 2: Hype-Driven Development

The trap: Use the latest hot technology regardless of fit.

Example:

“Everyone’s talking about Deno. Let’s rewrite everything in Deno!”

Result: Chase every trend, never build expertise.

The fix: Let trends mature. Adopt when there’s proven value.

Anti-Pattern 3: The Golden Hammer

The trap: “I know X, so I’ll use X for everything.”

Example:

“I’m a React expert, so I’ll build the backend in React too… wait, that doesn’t work.”

Result: Force tools into contexts where they don’t fit.

The fix: Learn multiple tools and when to use each.

Anti-Pattern 4: Premature Optimization

The trap: Solve for hypothetical scale you’ll never reach.

Example:

“This app might have 1 million users someday. Let’s architect for that now.”

Current users: 10.

Result: Complex system for a simple problem.

The fix: Solve for current scale. Refactor when you have actual problems.

Anti-Pattern 5: Not Invented Here Syndrome

The trap: Build everything yourself instead of using existing solutions.

Example:

“I’ll build my own authentication system. How hard can it be?”

Result: Security vulnerabilities, wasted time.

The fix: Use established solutions for solved problems.

The Decision Template

Here’s a template I use:

Problem: [What are you trying to solve?]

Constraints:

  • Time: [How long do you have?]
  • Team: [Who’s building this? What do they know?]
  • Scale: [How many users/requests/records?]
  • Budget: [What can you spend?]

Options:

Decision: [What you chose]

Why: [Your reasoning]

Trade-offs: [What you’re giving up]

Example:

Problem: Add real-time chat to app.

Constraints:

  • Time: 2 weeks
  • Team: 2 devs, know React and Node
  • Scale: 500 concurrent users
  • Budget: $100/month

Options:

  1. Build with WebSockets: Full control, but time-consuming.
  2. Use Firebase: Fast, but vendor lock-in.
  3. Use Socket.io: Middle ground, open source.

Decision: Use Socket.io

Why:

  • Team can learn it in 2 days
  • Scales to our needs
  • Open source (no lock-in)
  • Fits timeline and budget

Trade-offs:

  • Not as feature-rich as Firebase
  • More setup than Firebase
  • Less control than raw WebSockets

This process forces clear thinking.

Developing Intuition

Eventually, you don’t run through this checklist every time.

Experts develop intuition.

But intuition is compressed experience.

How to build it:

1. Make Many Decisions

You can’t develop judgment without making decisions.

Even wrong decisions teach you.

My first 10 architectural decisions were mostly wrong.

But each one taught me what to avoid next time.

2. Get Feedback

Did your decision work out?

Track outcomes:

  • Did the project succeed?
  • Was it maintainable?
  • Did it scale?
  • Would you make the same decision again?

This feedback loop calibrates your intuition.

3. Study Experts’ Decisions

Read engineering blogs from successful companies.

Look for:

  • What problem they faced
  • What they chose
  • Why they chose it
  • What happened

Examples:

  • Netflix tech blog
  • Stripe engineering blog
  • Airbnb engineering blog

You’re learning their context-to-decision mappings.

4. Simulate Decisions

When you read about a technical problem, pause.

Ask yourself: “What would I choose?”

Then read what they chose and why.

Compare your reasoning to theirs.

This builds pattern recognition without having to live through every scenario.

Final Thoughts

I used to think expertise was knowing all the tools.

React. TypeScript. GraphQL. Docker. Kubernetes. PostgreSQL. Redis.

But that’s not expertise.

Expertise is knowing:

  • When to use React vs. Vue vs. Svelte
  • When to use TypeScript vs. JavaScript
  • When to use GraphQL vs. REST
  • When to use Docker vs. not
  • When to use Kubernetes vs. simpler solutions
  • When to use PostgreSQL vs. MongoDB vs. Redis

The difference is judgment.

Beginners collect tools.

Experts develop judgment about when to use each tool.

And that judgment comes from:

  • Understanding the problem each tool solves
  • Knowing the tradeoffs
  • Considering the constraints
  • Learning from failures
  • Building pattern recognition

So don’t just learn how to use tools.

Learn when to use them.

That’s the skill that separates good developers from great ones.


What’s a tool you know how to use but aren’t sure when to use? How could you learn its boundaries?