Agile Software Engineering
This podcast explores how craftsmanship, architecture, engineering rigor, and organizational practices come together in modern R&D environments. Each edition refines and deepens my earlier reflections, building a coherent and evolving body of knowledge around Agile Software Engineering
Agile Software Engineering
SAFe Light - Part 2: Evolutionary Architecture
Use Left/Right to seek, Home/End to jump to start or end. Hold shift to jump forward or backward.
In this episode of The Agile Software Engineering Deep Dive, Alessandro Guida continues the discussion on SAFe Light and explores why lightweight Agile scaling needs evolutionary architecture.
SAFe Light is based on preserving team autonomy while making the essential coordination points visible. But that only works if the architecture supports independent change. Without clear boundaries, explicit dependencies, contract-based integration, fitness functions, and continuous feedback, teams may appear autonomous while remaining blocked by hidden coupling and integration surprises.
The episode introduces evolutionary architecture as architecture planned for change: a disciplined way to let systems evolve incrementally without losing coherence. It also explains why strong architecture can reduce the coordination burden in scaled Agile environments.
The central idea is simple: when architecture is weak, process expands to compensate. When architecture is strong, process can remain lighter.
This Podcast is an audio version of the written Agile Software Engineering newsletter. If you want to go deeper, don't forget to subscribe the newsletter too.
Welcome to the Agile Software Engineering Deep Dive, the podcast where we unpack the ideas shaping modern software engineering. My name is Alessandro Guida, and I've spent most of my career building and leading software engineering teams across several industries. And today I want to continue the discussion about safe light. In the previous article, I introduced safe light as a lighter way to scale agile, less ceremony, less centralized control, but still enough structure to make dependencies, integration, architecture, and release readiness visible. But there is an important question behind that idea. What makes lightweight scaling safe? The answer is not simply better planning, it is better architecture. Because if everything depends on everything else, teams cannot really move independently. They may look autonomous on an organization chart, but in practice they are blocked by hidden coupling, unclear ownership, unstable interfaces, and integration surprises. And when that happens, organizations usually respond by adding more process, more meetings, more reporting, more coordination, more governance. But often the real problem is not lack of process. The real problem is that the architecture does not support independent change. In this episode, I introduce evolutionary architecture as the technical foundation of safe light. Architecture planned for change, guided by clear boundaries, explicit dependencies, fitness functions, continuous feedback, and architectural guardrails. Because when architecture is weak, process expands to compensate. When architecture is strong, process can remain lighter. Let's dive in.
SPEAKER_02You know that feeling when you're uh driving on a massive 10-lane highway that was supposedly built for ultimate steed, but you are just completely stuck in gridlock.
SPEAKER_01Oh yeah, the classic phantom traffic channel.
SPEAKER_02Right. You look around and there aren't any accidents. The weather is, you know, perfectly fine.
SPEAKER_01It's just inherently poorly designed.
SPEAKER_02Exactly. Every time someone tries to merge or exit, traffic just backs up for miles. And you're sitting there being told you're agile, you're supposed to be moving fast, but instead you're just sitting in bumper-to-bumper traffic.
SPEAKER_01It's a brilliant illustration, really, of how a system's underlying structure uh dictates how fast you can actually go, regardless of what the speed limit signs claim.
SPEAKER_02Yeah, and that is exactly what we are unpacking in today's deep dive. We're pulling some amazing insights from issue 33 of Alessandra Guida's Agile Software Engineering Newsletter.
SPEAKER_01It's a really fantastic piece.
SPEAKER_02It is. The piece is called Safe Light Part Two, Evolutionary Architecture. And our mission today is to show you, the listener, a counterintuitive truth, which is fixing a broken agile process doesn't require more process.
SPEAKER_01Right. It requires better code architecture.
SPEAKER_02Yes. And before we get fully into the weeds here, I want to say if you want more insights like this delivered straight to you, make sure to go read that full article and hit subscribe on Alessandro's newsletter. And you know, hit subscribe on this deep dive too, so you don't miss our future breakdowns.
SPEAKER_01Yeah, it's highly recommended reading, especially if you uh if you're currently trapped in a cycle of endless alignment meetings. Because that's the core pain point we are dissecting today.
SPEAKER_02The dreaded alignment meeting.
SPEAKER_01Right. Organizations adopt these really heavy scaling frameworks like SAFE, the scaled agile framework, hoping to coordinate dozens of teams.
SPEAKER_02But they quickly realize that teams aren't actually autonomous at all.
SPEAKER_01No, not at all. They get bogged down in dependency boards, release negotiations, escalation paths. It just becomes pure coordination theater.
SPEAKER_02Aaron Powell Coordination Theater. I love that term. I mean, I'll admit, when I look at a calendar full of alignment meetings, my first thought is usually um, okay, we clearly have a project management problem.
SPEAKER_01Aaron Powell Sure. That's the natural assumption.
SPEAKER_02But the argument here is that the calendar is really just a symptom, right?
SPEAKER_01Aaron Powell Exactly. When architecture is weak, process expands to compensate for it. But when architecture is strong, your process can remain so much lighter.
SPEAKER_02It's like putting a massive Formula One racing spoiler on a on a broken down minivan.
SPEAKER_01That is a perfect way to describe it.
SPEAKER_02You can add all the agile ceremony you want. You can have the most colorful dependency boards in the office, the most rigorous daily stand-ups. But if the underlying engine, you know, the code base architecture is tied in knots, you aren't going fast.
SPEAKER_01You're just holding more meetings about why the minivan won't start.
SPEAKER_02Yeah. But I have to push back a bit here because let's say an organization does have a tangled mess of code. Everything depends on everything else. Is a concept like SafeLight or really any lightweight agile scaling, is that even physically possible?
SPEAKER_01The short answer is no. It's really not.
SPEAKER_02Wow. Okay.
SPEAKER_01Because if your team boundaries don't match your architectural boundaries, every single feature you try to build becomes this massive multi-team negotiation.
SPEAKER_02Aaron Powell So it's a structural barrier.
SPEAKER_01Yeah. Let's break down the mechanics of why. Imagine team A wants to add, I don't know, a simple checkout button, but that button touches a database schema that's owned by Team B.
SPEAKER_02Okay. Pretty common.
SPEAKER_01And then to reach that database, it has to route through a legacy API managed by Team C.
SPEAKER_02Right. So you can't just sprint on that. Team A is immediately blocked until Team B and Team C uh adjust their sprint backlogs to accommodate the change.
SPEAKER_01Precisely. The system itself generates the friction. The architecture actively resists independent progress. And that is why any form of lightweight agile absolutely requires evolutionary architecture.
SPEAKER_02Aaron Powell Okay, so if weak architecture is the disease, evolutionary architecture is the cure. But I'll be honest, evolutionary sounds like um, well, one of those buzzwords executives throw around when they don't actually have a plan.
SPEAKER_00Yeah, I hear that a lot.
SPEAKER_02Isn't emergent or evolutionary architecture just a fancy excuse for letting developers code whatever they want and just hoping a cohesive system magically appears? Because to me that sounds like drift, not design.
SPEAKER_01Right. And that is a dangerous, albeit very common misconception. What you are describing is architectural entropy.
SPEAKER_02Entropy. Okay.
SPEAKER_01Drift happens when technical debt becomes normalized. It's when a developer, say, bypasses an API to read directly from a database just because it's faster for their current ticket.
SPEAKER_02Oh yeah. We've all done that.
SPEAKER_01Right. And suddenly data ownership is completely murky. Nobody is quite sure if a change is safe, and every deployment becomes this terrifying gamble.
SPEAKER_02Yeah, you tweak a CSS class for the checkout cart, and somehow the email newsletter system crashes.
SPEAKER_01Exactly. And that happens because of unmanaged drift. Evolutionary architecture, by stark contrast, is intentional. It's heavily guided.
SPEAKER_02Guided how? Exactly.
SPEAKER_01Well, it means the software is explicitly designed for incremental change. It allows teams to learn, adjust, and refactor continuously, but they do so within deliberate constraints.
SPEAKER_02So the system doesn't just collapse into accidental complexity.
SPEAKER_01Right. And the mechanical way we guide this evolution is through something called fitness functions.
SPEAKER_02Okay, fitness functions. Are we talking about standard unit tests here? Because I feel like, you know, every team already runs automated tests to see if the checkout cart totals the right amount.
SPEAKER_01Aaron Powell No, no. Fitness functions are fundamentally different. While a functional test checks user behavior, a fitness function is an automated test for your architectural rules.
SPEAKER_02Oh, interesting.
SPEAKER_01Think of it less like a behavioral check and more like a structural spell checker for your code base.
SPEAKER_02Aaron Powell, a structural spell checker. I really like that. So what exactly is it checking for? Walk me through the actual mechanism of that.
SPEAKER_01Okay, let's say your architectural design dictates that the user interface should never, under any circumstances, communicate directly with the backend database.
SPEAKER_02Makes sense.
SPEAKER_01You write a fitness function, which is basically an automated script that analyzes the code structure itself. If a developer accidentally writes code that creates a direct dependency between the UI layer and the database, the fitness function fails. Whoa. It runs in your integration pipeline and literally blocks the build from being deployed.
SPEAKER_02Oh wow. So it physically prevents structural drift from even entering production.
SPEAKER_01Exactly. And you can write fitness functions for all sorts of things. You can have tests ensuring that API response times stay below a 200 millisecond threshold.
SPEAKER_02Or like security stuff.
SPEAKER_01Yeah, you can have automated checks that guarantee developers aren't importing forbidden, highly vulnerable third-party libraries.
SPEAKER_02And here is why this is so critical for you, the listener, especially if you are tired of being bottlenecked. These fitness functions act as a form of lightweight automated governance.
SPEAKER_01Which is huge.
SPEAKER_02Yeah. Think about the alternative. In a traditional corporate environment, to ensure code meets architectural standards, you have to like submit your design to an architecture review board.
SPEAKER_01Oh, the dreaded ARB.
SPEAKER_02Right. You sit in a room for three hours, arguing with a committee, just waiting for manual approval. Fitness functions take that human bottleneck entirely out of the equation.
SPEAKER_01They do. It makes change inherently safe, which means it makes change fast. It only stops developers when they are about to damage something structurally critical.
SPEAKER_02Okay, so the fitness functions automatically enforce the rules, but what are those rules actually trying to protect? From what I'm gathering, the ultimate goal here is protecting boundaries.
SPEAKER_01That is the core of it. Yes. Evolutionary architecture isn't about drawing the prettiest deployment diagram. Right. It is entirely about the capability to change safely over time. And a massive system cannot evolve safely if a change in module A forces a complete rewrite in module Z.
SPEAKER_02Yeah, that's a nightmare.
SPEAKER_01You need strict, non-negotiable boundaries.
SPEAKER_02Because if you have those boundaries, team A can own their module, completely rewrite their underlying logic, and deploy it to production without ever needing to coordinate with team B.
SPEAKER_01Exactly. As long as the public-facing API doesn't change.
SPEAKER_02And that is true, autonomy. But you know, discussing boundaries immediately brings us to the elephant in the room, microservices. Right. For the last decade, I feel like the industry has treated microservices as the silver bullet for scaling agile teams. Like the conventional wisdom is if your monolith is a tangled mess, just chop it up into 50 microservices, give one to each team, and boom, you have perfect boundaries.
SPEAKER_01Yeah, if only.
SPEAKER_02But I'm guessing it's not that simple.
SPEAKER_01It is absolutely not that simple. And it's a trap so many organizations fall into. Microservices can support evolutionary architecture, but only if they represent true, meaningful domain boundaries.
SPEAKER_02Okay, meaning what exactly?
SPEAKER_01Well, if you take a confused, deeply entangled monolith and simply slice it into microservices, you haven't actually fixed the architectural confusion. You have merely distributed that exact same confusion across a network.
SPEAKER_02Oh, I see where this is going. Instead of a single tangled ball of yarn on one server, you now have 50 tiny tangled balls of yarn.
SPEAKER_01Yes.
SPEAKER_02And worse, they all have to talk to each other over HTTP.
SPEAKER_01And that introduces catastrophic operational complexity. When components communicate inside a monolith, it's just a localized function call in memory. It's instantaneous.
SPEAKER_02Right. It's practically free.
SPEAKER_01But when you distribute a mess into microservices, you are suddenly dealing with network latency, dropped packets, timeouts, massive deployment orchestration.
SPEAKER_02So if a user clicks buy, and that request has to bounce between seven different microservices over a network just to complete the transaction.
SPEAKER_01Your performance will absolutely tank. And debugging an error becomes a total nightmare.
SPEAKER_02So a poorly designed microservice architecture is actually far worse than a monolith.
SPEAKER_01Much worse. Look at the real-world examples. Amazon and Netflix successfully utilized cloud native microservices, but they did it by aligning those services with massive, highly independent domains.
SPEAKER_02And very specific team ownership.
SPEAKER_01Yes, their architecture, their delivery model, and their organizational chart all evolved together. The lesson from Netflix wasn't just use microservices. Right. But look at the flip side. Look at Shopify.
SPEAKER_02Shopify is fascinating here because they completely rejected the microservices trend, right?
SPEAKER_01They did.
SPEAKER_02They handle a staggering amount of global e-commerce traffic, yet they invested heavily in a modular monolith, specifically using Ruby on Rails.
SPEAKER_01Exactly. Shopify proved unequivocally that you do not need microservices to achieve an evolutionary architecture. You simply need clear internal boundaries.
SPEAKER_02So it's still one big code base?
SPEAKER_01Yes. In a modular monolith, the code base is still deployed as a single unit, but the framework strictly enforces logical boundaries. The billing module cannot physically access the inventory module's database tables. It has to go through an internal, clearly defined interface.
SPEAKER_02Okay, so they get the clean team boundaries and the independent development of microservices, but without the terrifying network latency and deployment nightmares.
SPEAKER_01Exactly.
SPEAKER_02It's a brilliant reminder that architecture should evolve toward better changeability, not just toward whatever deployment style is currently trending on tech blogs.
SPEAKER_01Precisely. You optimize for safe change, whatever the deployment vehicle is.
SPEAKER_02All right. This is all incredibly logical if you are starting fresh. Like if you are building a pristine greenfield project today, absolutely draw those beautiful boundaries, set up your fitness functions, choose your modular monolith.
SPEAKER_01Sure, in an ideal world.
SPEAKER_02But let's be real for a second. What does this mean for someone listening right now who is drowning in a 10-year-old legacy code base?
SPEAKER_01Yeah, the reality for most developers.
SPEAKER_02Exactly. I'm talking about a massive system where the original developers left five years ago and nobody actually knows how the core transaction engine works. Do we just have to bite the bullet, brin it all down, and do a massive big bang rewrite?
SPEAKER_01No. Please don't. In fact, large-scale rewrites are incredibly dangerous.
SPEAKER_02But they're so tempting.
SPEAKER_01They are seductive because they promise a clean, beautiful future, completely free of legacy constraints, but mechanically, they almost always fail.
SPEAKER_02Really? Almost always.
SPEAKER_01Yes. They take years longer than projected, they halt the delivery of new business features. And honestly, because the organization hasn't changed its fundamental habits, the rewrite often just recreates the exact same tangled complexity.
SPEAKER_02It's just in a newer programming language.
SPEAKER_01Exactly.
SPEAKER_02So if a big bang rewrite is off the table and we obviously can't just leave it as a tangled mess, how do we actually fix a legacy system?
SPEAKER_01By focusing on a strategy called encapsulating volatility.
SPEAKER_02Encapsulating volatility. Okay.
SPEAKER_01The goal of evolutionary architecture is to identify the specific parts of a system that are chaotic or likely to change frequently and wall them off behind strict abstractions.
SPEAKER_02Okay, so I'm guessing that means taking the unpredictable elements, like say a third-party payment gateway that updates its API every month.
SPEAKER_01Right.
SPEAKER_02Or maybe a highly experimental machine learning model and wrapping them in an internal interface.
SPEAKER_01That's the idea.
SPEAKER_02So when Stripe or PayPal changes their code, our entire tech at system doesn't break. We only have to update that one isolated wrapper.
SPEAKER_01Exactly. You contain the blast radius of change. And you can apply this exact same concept to legacy systems using what Martin Fower famously coined the Strangler Fig application pattern.
SPEAKER_02Ooh, I know the Strangler Fig pattern from nature. It's like a vine that grows around an old tree until it eventually replaces it.
SPEAKER_01Exactly.
SPEAKER_02But mechanically, in software, how does that actually work? Like if I have a giant legacy database, how do I strangle it without causing a massive data corruption issue where half the system writes to the old database and half writes to the new one?
SPEAKER_01That is the critical challenge.
SPEAKER_02Yep.
SPEAKER_01Mechanically, you don't touch the legacy database first. You start by building a new facade.
SPEAKER_02Like an API gateway.
SPEAKER_01Right, an API gateway or a routing layer right in front of the legacy system. All incoming user traffic hits this new routing layer. And initially, the router just passes 100% of the traffic straight back to the old legacy system.
SPEAKER_02Oh, I see. So to the user, nothing has changed at all.
SPEAKER_01Right. Then you build out a small single piece of functionality in your new clean architecture, let's say the user profile service. Okay. Once it's ready, you configure that routing layer to intercept any requests for user profiles and redirect them to the new service.
SPEAKER_02While the rest of the traffic still goes to the legacy monolith.
SPEAKER_01Exactly.
SPEAKER_02But what about the data? If the new profile service needs to read data from the old legacy billing system, how does that work?
SPEAKER_01That's where you build an anti-corruption layer. The new service communicates with the old system through a strict translator, ensuring that those messy legacy data models don't bleed into your new clean architecture.
SPEAKER_02Oh, that makes sense.
SPEAKER_01And then step by step, endpoint by endpoint, you redirect traffic. The old system slowly shrinks as it receives fewer and fewer requests, and the new system grows.
SPEAKER_02It's just incremental modernization. You completely avoid that terrifying, dramatic cut over weekend where the IT team flips the switch at 2 a.m. and just praise the new system doesn't crash the entire company.
SPEAKER_01Exactly. The architecture changes dynamically while the business continues to operate normally.
SPEAKER_02And this buys your team's independence. You are physically removing the architectural bottlenecks one by one, which naturally reduces the need for all those agile coordination meetings we talked about at the start.
SPEAKER_01Absolutely.
SPEAKER_02Okay, so we've strangled the monolith, we've established clear boundaries, we have automated fitness functions checking our code. It sounds perfect. But um, I see a glaring human flaw here.
SPEAKER_00Oh, what's that?
SPEAKER_02Five years from now, our current engineering team is going to move on to other companies. A new senior developer is going to inherit this code base. They are going to look at our complex routing layer and our anti-corruption wrappers and have absolutely no idea why they were built this way.
SPEAKER_01That happens all the time.
SPEAKER_02Right. So how do we prevent future developers from accidentally ripping out our carefully evolved architecture just because they lack the context?
SPEAKER_01That is exactly why evolutionary architecture cannot survive without architectural memory. If a system is going to evolve over a decade, you need a robust mechanism to document the why. And we do that using architecture decision records or ADRs.
SPEAKER_02Aaron Powell Because honestly, every developer listening has had that moment of looking at five-year-old code, throwing their hands up and screaming, what idiot built it this way?
SPEAKER_01Oh, countless times.
SPEAKER_02Only to realize, you know, a week later that there was actually a really good obscure reason for it.
SPEAKER_01Exactly. Architectural decisions are rarely universally right or wrong. They are right or wrong in their specific context. A database choice that made perfect sense when you had 10,000 users might be a massive bottleneck when you have 10 million.
SPEAKER_02So what actually goes into an ADR to capture that context? Is it some huge wiki page?
SPEAKER_01No, no, it's a very lightweight, standardized markdown document stored right alongside the code.
SPEAKER_02Oh, that's smart.
SPEAKER_01Yeah, it captures the title of the decision, the status, whether it's proposed, accepted, or deprecated, the specific context at the time, the alternatives that were considered, and the agreed upon consequences of the decision.
SPEAKER_02So when that new developer arrives in five years, they don't preserve an outdated routing layer just because they are terrified to touch it. Right. But they also don't blindly delete it without understanding the hidden dependencies. They read the ADR, they understand the original constraints, and they can make a highly informed, confident choice about how to evolve it next.
SPEAKER_01It enables what we call autonomy with alignment. Teams need the freedom to make localized decisions, choose their internal implementations, and own their modules.
SPEAKER_02But they must do this within shared automated rules.
SPEAKER_01Yes, our fitness functions.
SPEAKER_02Yeah.
SPEAKER_01And with shared context, our ADRs. The ultimate goal isn't central, top-down control. It is actively designing a system that makes independent progress physically possible, reducing the need for constant manual coordination.
SPEAKER_02And that leads perfectly back to our starting point about continuous delivery and observability, because you know, theory on a whiteboard is one thing, but production is reality.
SPEAKER_01Precisely. Continuous delivery provides the automated machinery, the deployment pipelines that makes this safe change possible. And observability provides the reality check.
SPEAKER_02Meaning what? Exactly.
SPEAKER_01Well, you might design a service boundary that seems incredibly logical on paper, but when you look at your real-world telemetry, your logs, your latency trends, you realize it's actually creating a massive traffic bottleneck.
SPEAKER_02Ah, I see.
SPEAKER_01Evolutionary architecture demands that you observe those metrics, learn from the friction, and adjust the boundaries accordingly.
SPEAKER_02Which fundamentally changes what it means to be a software architect. To summarize everything we've unpacked today, safe flight, or really any attempt to scale agile gracefully, isn't about just deleting meetings from your calendar and hoping for the best.
SPEAKER_01No, definitely not.
SPEAKER_02It's about keeping only the coordination that genuinely adds value and replacing the rest with architectural integrity.
SPEAKER_01The role of the architect shifts completely. They're no longer this ivory tower dictator defining a perfect static blueprint up front and policing compliance. The architect becomes a steward of changeability.
SPEAKER_02Their job is to ensure the system can bend without breaking. They help define the boundaries. They establish the automated fitness functions to catch drift.
SPEAKER_01And they ensure decisions are captured in ADRs.
SPEAKER_02Right. They remove the structural friction so the development process can finally move as fast as Agile promised it would. It's a profound shift in thinking. It's moving from trying to predict the future to building a system that can gracefully absorb whatever the future throws at it.
SPEAKER_01It really is.
SPEAKER_02Now, if you want to dive even deeper into these concepts, and trust me, the mechanics here are fascinating. We highly encourage you to go read the full article in the Agile Software Engineering newsletter. It's issue 33 by Alessandra Guida.
SPEAKER_01Yes, highly recommended.
SPEAKER_02Be sure to subscribe to his newsletter for a masterclass on engineering leadership.
SPEAKER_01It truly is a central reading for anyone trying to bridge that frustrating gap between agile delivery and long-term technical health.
SPEAKER_02And hey, if this deep dive gave you an aha moment today, or if you finally understand why your microservices are driving you crazy, please subscribe to our deep dive, leave a review, and share this with your team.
SPEAKER_00Yes, please do.
SPEAKER_02Because honestly, the more people who understand these architectural boundaries, the fewer useless alignment meetings we all have to sit through.
SPEAKER_01Which is a win for everybody.
SPEAKER_02Absolutely. Before we go, I want to leave you with a final provocative thought to mull over. We spent this entire time talking about using fitness functions, these automated structural tests to stop our code architecture from degrading into chaos. But it makes you wonder could an organization create organizational fitness functions?
SPEAKER_01Oh, that is a fascinating concept.
SPEAKER_02Think about it. Could you build automated triggers or metrics that test if your meetings, your approval processes, or your agile ceremonies are slowly degrading into useless bureaucracy?
SPEAKER_01Like an automated script for corporate bloat.
SPEAKER_02Exactly. If a meeting regularly produces no actionable outcomes, does a script automatically cancel the recurring calendar invite? Think about how you might systematically test your own calendar for entropy.
SPEAKER_01I love that idea.
SPEAKER_02At the end of the day, you don't want to be sitting on a 10-lane highway wondering why you aren't moving. You want to fix the on-ramps, build better boundaries, and actually get to where you're going. Thanks for joining us, and we'll see you on the next deep dive.
SPEAKER_00A colleague, your team, or your network. You can access all episodes by subscribing to the podcast and find their written counterparts in the Agile Software Engineering newsletter on LinkedIn. And if you have thoughts, ideas, or stories from your own engineering journey, I'd love to hear from you. Your input helps shape what we explore next. Thanks again for tuning in, and see you in the next episode.
Podcasts we love
Check out these other fine podcasts recommended by us, not an algorithm.
Darknet Diaries
Jack Rhysider