Tag: Software Development

  • Why Event-Driven Microservices?

    You’ve heard the pitch for microservices. Small, independent services. Teams that can ship without waiting on six other teams to finish their sprint. No more three-month release cycles because somebody touched a shared library. It sounds great — and honestly, the core idea is great. But here’s the thing: a lot of teams adopt microservices and end up with something worse than the monolith they started with.

    I’ve spent the most recent part of my career working with distributed systems, and I’ve seen some of the ways monolith-to-microservice transitions can go awry. A team takes their monolith, draws some boxes around the major modules, splits them into separate services, deploys them independently, and declares victory. Six months later they’re debugging cascading failures at 2 AM and wondering why everything is harder than it used to be.

    What went wrong? They broke the monolith apart without actually decoupling it. And a distributed monolith — where you have all the operational complexity of microservices with none of the benefits — is arguably the worst of both worlds.

    The Coupling Problem

    Let’s be specific about what tight coupling looks like in a microservices architecture, because it’s not always obvious.

    Synchronous request-response everywhere. Service A calls Service B, which calls Service C, which calls Service D. If any one of those services is slow or down, the whole chain stalls. You haven’t built a resilient distributed system — you’ve built a monolith with network hops. And network hops are the worst kind of function calls, because now you get to deal with latency, partial failure, and timeout tuning on top of everything else.

    Shared databases. Multiple services reading from and writing to the same tables. This is the one that sneaks up on people, because the database feels like shared infrastructure rather than a coupling point. But the moment you need to change a schema, you’re coordinating across every service that touches those tables. You’re right back to “deploy everything together or deploy nothing” — which is exactly what microservices were supposed to fix.

    Data format dependencies. Service A produces a message with a certain structure. Services B, C, and D all parse that structure. Now Service A needs to add a field or change a type. Congratulations, you need buy-in from three other teams before you can ship. That’s not independent deployment — that’s a distributed approval process.

    Temporal coupling. Services that have to be running simultaneously to function. If the downstream service isn’t up right now, the upstream service can’t do its job. Your services aren’t really independent if they can only work when everyone else is awake. (Kind of like a group project where one person has to be physically present for anyone else to make progress. We’ve all been in that group project.)

    If any of this sounds familiar, you’re not alone. And the good news is that these problems are well-understood, and there are well-established patterns for solving them.

    Thinking in Events

    Here’s the mental shift that makes the difference: stop thinking about services calling each other, and start thinking about services reacting to things that happen.

    This is event-driven architecture, and at its core it’s about making your software reflect how the real world actually works. The real world doesn’t operate on synchronous request-response. Things happen — a customer places an order, a sensor reads a temperature, a payment clears — and other parts of the system respond to those events on their own terms, at their own pace.

    When you build systems this way, something interesting happens to those coupling problems:

    Synchronous chains disappear. Service A publishes an event. It doesn’t know or care who’s listening. Services B, C, and D each pick up the event and do their thing independently. If Service C is having a bad day, Services A, B, and D don’t notice — they keep right on working.

    Data ownership becomes clear. Each service owns its data, publishes events about what changed, and subscribes to the events it cares about from others. No shared databases, no schema coordination nightmares.

    Temporal coupling goes away. If a service is down when an event is published, that event waits in the stream until the service recovers and processes it. The system degrades gracefully instead of falling over.

    Now, this isn’t magic — you’ve traded one set of challenges for a different set. Event-driven systems have their own complexities: eventual consistency, event ordering, debugging asynchronous flows. We’ll get into all of that. But at least these are the right problems to have — problems that come from genuinely decoupled services rather than from a distributed monolith pretending to be something it’s not.

    Patterns That Make It Work

    If you start exploring event-driven microservices, you’ll quickly run into a set of well-known patterns that have emerged to address the practical challenges. Chris Richardson’s microservices.io is an excellent catalog of these — I’d recommend bookmarking it.

    Two patterns in particular are going to be central to what we explore in this blog, and I’ll admit it took me a while to appreciate how well they fit together:

    Event Sourcing — instead of storing the current state of your data and updating it in place, you store the sequence of events that led to the current state. Every state change is captured as an immutable event in an append-only log. This gives you a complete, auditable history of everything that happened in your system — not just “the account balance is $500” but “here’s every deposit, withdrawal, and transfer that got it there.”

    If you come from a database background (guilty), this feels deeply wrong at first. You mean I don’t just UPDATE the row? I keep every change? Forever? But once you get past the initial discomfort, the power of it becomes obvious. You can reconstruct any past state. You can answer questions you didn’t think to ask when the data was created. You have a complete audit trail for free.

    The catch is also obvious — if you need the current state, do you really have to replay every event from the beginning of time? For a system that’s been running for years, that’s not just slow, it’s unworkable.

    CQRS (Command Query Responsibility Segregation) — and this is where it gets interesting. You separate the write path (commands that produce events) from the read path (queries that serve up current state). The write side stores events. The read side maintains materialized views — pre-computed projections of whatever the read side needs, kept up to date by consuming the event stream.

    See what happens when you put these two together? Event Sourcing gives you the complete, immutable history. CQRS and materialized views give you fast reads without replaying the entire event log every time someone wants to check a balance. Each pattern solves the other’s biggest problem. It’s one of those combinations where the whole is genuinely greater than the sum of the parts — and as we’ll see in later posts, it maps onto certain technology stacks almost embarrassingly well.

    What’s Ahead

    This blog is going to be a hands-on exploration of these ideas — patterns first, then concrete implementations. I’m genuinely excited about this, because I think there’s a gap between the theoretical literature on event-driven architecture (which is excellent) and the practical “here’s how you actually build one” content (which is thinner than you’d expect). In the posts to come, we’ll dig into:

    • Resilience through decoupling — how event-driven systems degrade gracefully instead of cascading failures
    • Auditability and replay — the power of an event log as a source of truth, not just for debugging but for compliance, analytics, and the ability to answer questions you didn’t think to ask yet
    • Independent scalability — scaling the services under load without scaling everything, because your order processing pipeline doesn’t need to drag your user profile service along for the ride
    • Evolvability — adding new consumers of existing events without touching the producers, so your analytics team can tap into a data stream without filing a ticket with the team that owns it

    We’ll look at the patterns in general terms — what problem each one solves, what trade-offs it introduces, how to think about whether it’s the right fit — and then we’ll get into specific, working implementations that you can pull apart, run, and adapt to your own projects.

    If you’re a developer who’s building or maintaining a microservices architecture and found it harder than expected — or if you’re designing a new system and want to avoid the common pitfalls — this series is for you. The patterns are universal; the implementations will be specific. Let’s see where it takes us.

  • If at first you don’t succeed

    In my previous post I mentioned that I wanted to give a bit of the story of developing The Sorcerer’s Apprentice iPhone application.    But The Sorcerer’s App was not my first crack at writing an iPhone app.    Before we get to the new app, let’s turn the wayback machine to 2009.   The App Store was only about a year old  (it’s easy to forget that at the initial release, third party developers could not write applications for the iPhone).   And I had an idea for what I felt would be a great iPhone application.

    The idea of the app was a baseball scoring application.   This wasn’t a new idea for me — I had originally thought of it as an application I thought would do well for the Apple Newton.   I had even drawn up some screen mock-ups of the Newton app (I still have them in a file around here somewhere).  But the Newton wasn’t a long-lived platform and was gone before I ever got a chance to make any serious attempt at developing an application for it.

    But the idea didn’t die, so when the iPhone opened up for third party developers, I started thinking about it again, and then working on it.   I bought a couple of developer’s guides, and even attended an iOS developers conference in San Jose.   Soon pieces of the app were beginning to take shape … a display across the top of the screen for the line score (inning-by-inning runs scored), a lineup on the left, an area for scoring the current play on the right.

    Background image for the play scoring area
    Background image for the play scoring area

    As it turns out, this was an incredibly complex application, and in hindsight was really too ambitious for a first project — especially for a single developer, working part time.    Things that were uninteresting, but vitally necessary — like handling the roster, lineup, substitutions, etc. — were very time consuming to get right.    The interesting part — scoring the plays — really required skills with graphics that I didn’t possess if I was to give the app the polished look I was looking for.

    I worked on the app pretty steadily for a number of months.    At some point while I was doing this, another baseball scoring app showed up in the app store — but I wasn’t too discouraged, because I looked at it and decided I could do better.    Not too long after that, a second scoring app showed up — much more complete, better thought out.    Well, I thought, I may have lost the first mover advantage, but  I could catch up.    Then the newer, better app was re-branded –it became the ESPN scorecard app.    At that point it really seemed like Game Over.    If I was confident that I was going to turn out an app that was everything I envisioned, perhaps I would have continued at that point — but I was daunted by how long I’d worked on this and how much was still left to do.   I knew it would be several more months before I could possibly have anything to market, and then it might very well be second-best.

    So, my first iPhone development project was shelved.   But I’d learned a lot, and I felt I would return to iPhone development when the right project came along.   I really thought that would be in a matter of months, rather than years — but in the intervening time, there has been nothing that struck me as something I wanted to do badly enough that I’d invest the hours required.    So time marched on, while millions of new apps were developed and shipped.   There had to be an idea that was still out there somewhere, waiting for me to find it.

    That’s where the story will pick up in the next post.