Every architect’s favorite interview question: “Should we use REST or gRPC?”

Wrong question. It’s like asking Neo if he should take the red pill or the blue pill before asking what the Matrix even is.

The right question: Do your services even need to talk right now?


Synchronous - “I’ll Be Back” (And I Need an Answer)

REST - The John McClane

Shows up in a dirty tank top, gets the job done. Your grandma can debug it with curl. Not sexy, but it starts every morning.

Use it when: User clicks, needs response. Simple. Stop overthinking.

gRPC - The Terminator

Fast. Efficient. Speaks binary, doesn’t do small talk. Perfect for service-to-service when milliseconds matter.

Use it when: Internal calls, high throughput, you hate JSON parsing overhead.

Skip it when: Your team doesn’t want to learn protobuf. Debugging it is like interrogating the T-1000.

The FastAPI Question

“DI or raw Python?”

FastAPI’s dependency injection for HTTP concerns (auth, DB sessions). Raw Python for domain logic. Don’t let your framework leak into business code like the Blob consuming a small town.


Asynchronous - “Life Moves Pretty Fast” (Ferris Bueller)

The Polling Pattern - REST/gRPC Gone Async

You can fake async with sync calls. Service A calls Service B, gets a task ID, polls until done.

POST /tasks → 202 Accepted, task_id: "abc123"
GET /tasks/abc123 → { status: "processing" }
GET /tasks/abc123 → { status: "done", result: {...} }

The problem? Now YOU handle:

  • Retries (how many? backoff?)
  • Rate limiting (don’t DDoS yourself)
  • Circuit breakers (when to give up)
  • Error states (timeout? failure? partial?)

Building IKEA furniture without instructions. Possible, but why?

Queues handle this for you. Dapr makes it trivial.

Message Queues (SQS, Service Bus)

The postal service. Boring. Reliable. Your parents trust it.

Dead Letter Queues: where messages go after being rejected too many times. Like that kid left at the altar in Sixteen Candles.

Use it when: Guaranteed delivery matters. Someone will get the message. Eventually.

PubSub / Event Streaming - The Town Crier

HERE’S WHAT NOBODY TELLS YOU:

EventBridge, Kafka, Kinesis, Event Hubs - they’re broadcasters, not delivery services. They yell “HEAR YE!” and don’t care if you were in the bathroom.

The Pattern That Actually Works:

[Service] → [EventBridge/Kafka] → [SQS/Queue] → [Your Service]

Event bus fans out. Queue buffers and guarantees. Like having a secretary who takes messages instead of people shouting through your door.

Or just use Dapr.


Dapr - “Wax On, Wax Off”

Daniel-san didn’t understand why he was waxing cars. Then suddenly he was blocking punches.

That’s Dapr. You configure YAML, then suddenly you’ve swapped RabbitMQ for Kafka without changing code.

The Magic:

  • Service Invocation - Call by name, not URL. Dapr handles discovery, retries, mTLS.
  • State Management - Same API for Redis, PostgreSQL, whatever. Your code doesn’t care.
  • PubSub - The sidecar handles queue-in-front-of-service FOR YOU. No SQS plumbing. It just works. Like that ’89 Honda Civic still running.

When it’s overkill: Single service, single cloud, you already know AWS by heart.


The Non-Negotiables (3 AM Survival Kit)

Distributed Tracing

“It worked on my machine” doesn’t scale.

Correlation IDs: Every request gets an ID at the edge. It follows through every service, queue, database. Without this? Debugging blindfolded in a maze while someone throws things at you.

OpenTelemetry: The standard that actually became standard. Like VHS winning over Betamax.

Observability

Structured logging: JSON or go home. It’s not 1995. Stop grepping through access.log like Indiana Jones.

Metrics that matter:

  • ✅ Latency p95, p99
  • ✅ Error rates by endpoint
  • ❌ Total requests ever (cool story, bro)

Error Reporting

“Game over, man! GAME OVER!” - Hudson knew about production incidents.

Circuit breakers: Teach services when to give up. If Service B is down, Service A shouldn’t keep calling like a desperate ex. Fail fast, try later.

Health Checks

Liveness: “Are you alive?” → No? Restart.

Readiness: “Can you handle traffic?” → No? Stop sending, don’t kill.

Get these wrong and enjoy the Kubernetes restart loop from hell.


The Decision Matrix

Need Use Why
Instant response REST Stop overthinking
Internal, high throughput gRPC Fast, typed
Async but hate yourself Polling DIY retries, errors, rate limits
Async without pain Queue (SQS/Service Bus) Built-in retry, DLQ, backoff
Fan-out to many services EventBridge + Queue Broadcast + guarantee
All of above, no lock-in Dapr Future-you sends thanks

The Uncomfortable Truth

“Your scientists were so preoccupied with whether they could, they didn’t stop to think if they should.” - Dr. Ian Malcolm

Most startups don’t need microservices.

A well-structured monolith beats a distributed mess. Every. Single. Time.

Signs you’re overengineering:

  • More YAML than features
  • “Let me check which service handles that”
  • Debugging requires 5 terminal windows
  • You have a service that just… forwards requests?

The Napkin Test: Can you explain your architecture on a napkin? Not a tablecloth. A napkin. No? Simplify.

As we say in Greece: "Ό,τι λάμπει δεν είναι χρυσός" - All that glitters is not gold. Microservices included.


Closing

Your services will talk. They’ll gossip, miscommunicate, ghost each other at 3 AM.

Use REST when simple. gRPC when fast. Queues when reliable. Events when broadcasting. Dapr when sleeping matters.

Add tracing, logs, and health checks BEFORE you need them. Not after the incident.

“Now I have a machine gun. Ho ho ho.” - John McClane

Make sure that machine gun is observability. Not a frantic SSH into production.