Showing posts with label Architecture. Show all posts
Showing posts with label Architecture. Show all posts

Wednesday, February 25, 2026

Agentic Risk Class Postures

When dealing with Agentic AI, many companies struggle with one basic question: what governance posture should we take? Some say, “Agents are dangerous,” and get labeled naysayers. Others want to “go find out,” and get labeled reckless.

Both impulses have merit. But most agentic implementations require a position in between: contain the danger - and avoid analysis paralysis.

The key differentiator is simple: Can you deal with the consequences?

You don’t want to find out

So here’s the thing: If we don’t experiment, how can we learn?
We may never realize the full benefits of this Agentic AI technology if we refuse to take any risk.

But risks are not created equal. Some risks we can live with. Others, we can't.

And depending on our authority and accountability, some moves are within our control - and others are not even ours to decide. That's why not all risk postures are equal.

It depends on who's taking the risk, why - and whether it is worth it.

Sometimes the predictable consequences are not worth the upside. Other times, the effort required to eliminate possible consequences is prohibitive. That leaves only two honest options: accept risk consciously - or redesign the solution.

To make that decision less emotional and more structural, I've created a simple table. It shows how different agent risk tiers change the way we govern the agent across the Software Lifecycle.

The Agentic Governance Posture

Below is a concise summary of how governance mode, testing depth, nonfunctional requirements, and service operations expectations shift depending on the agent’s risk class.

This model helps you avoid two common failure modes: over-engineering containment for harmless agents, and under-engineering control for agents that can hurt you.

It applies at enterprise scale — and it applies to the agentic tool you just downloaded to make your own life easier.

Ultimately, the choice is yours: risk-positive, risk-conservative, or risk-averse. Just make the choice consciously - before you enroll in the School of Hard Knocks.

Risk Tier Governance Mode Testing Requirements Nonfunctional Requirements Service Operations Requirements
1 - Contained Single accountable owner.
Authority is strictly local.
Worst case: time lost, no external consequence.
Boundary enforcement tests (no unintended writes).
Functional correctness tests.
Basic sanity validation.
Transparent execution.
Predictable behavior.
No hidden side effects.
Operations tied to active use.
Owner directly observes and controls execution.
2 - Operational Delegated authority within explicitly defined scope.
Named accountable authority for outcomes.
Policy-boundary tests.
Reversibility verification.
Negative-case testing.
Threshold and rate-limit tests.
Observability (metrics, logs, traces).
Defined performance expectations.
Traceable execution history.
Known escalation contact.
Kill switch available.
Incident and rollback procedures defined.
3 - Cross-Cutting Authority spans multiple systems or stakeholders.
Formal risk review required.
Active ownership across affected domains.
End-to-end process testing (including negative cases).
Cross-system integration tests.
Drift detection tests.
Exposure and impact simulations.
Full audit trail of decisions and actions.
Decision records retained.
Guardrail monitoring with violation alerts.
Managed change process.
Regular control reviews.
Formal escalation pathways.
4 - Strategic Oversight by the highest accountable authority in the system.
Explicit delegation charter.
Documented risk acceptance.
Scenario simulation before deployment.
Pre-release oversight review.
Segregation-of-authority verification.
Engineered resilience and reliability.
Governance attestations.
Enterprise-grade auditability.
24/7 readiness for high-impact incidents.
Crisis response protocol.
Clear communication pathways.
5 - Sovereign Authority reaches or exceeds the system’s sovereignty boundary.
Multi-step or multi-party authorization required.
Default posture: "air-gapped" control.
Proof that unilateral execution is technically impossible.
Bypass-resistance verification.
Enforced separation of authority.
Non-bypassable controls.
Immutable audit trail.
Highest assurance architecture.
Immediate containment capability.
Escalation to consequence-bearing authority.
Hard shutdown mechanisms.

How to use this table

Start with one uncomfortable question: Whose phone rings when the worst case happened?

Think of Summer Yue's case: She didn't intend for her company correspondence to be deleted. She even explicitly prompted the agent to not do it. But it was still possible. Because for agents, “possible” is enough. They won't hesitate and ask if they got it right: They just do. Rapidly. At scale.

Now work bottom-up through the tiers.

First: is existential impact technically impossible by external control? If you're not certain, be prepared to face it.

Next: could even a short period of unsupervised execution land you in a hole that's hard to get out of? If you can't say how that's prevented - better bring some climbing tools. And no: "I told the agent's prompt ..." - is not prevention. It's hope.

Continue upward through the tiers. Only when you have evidence, not "hope" that authority is constrained, actions are reversible and contained - then you have a predictable risk classification.

And be careful with the word “certainty.” When dealing with Agents, certainty does not mean “it won’t happen.” It means: when the worst case happens, we understand what happened and how to deal with it.

Wednesday, August 10, 2022

TOP Structure - the domain of Architecture

In software, there's a critical intersect between technology, that is - how we turn ideas into working software - and our organization - that is, who is part of development and how they interact.





Architecture is at the crossover point of Technology and Organization


This domain is Architecture, and it exists one way or another - if we don't manage it wisely, the outcome is haphazard architecture, most likely resulting in an inefficient organization delivering a complex, low value solutions in a long time and at a high cost.

Am I trying to advocate for a separate architecture team? No. Take a moment and think about Conway's Law: If we have the wrong organization, the consequence is the wrong architecture, the consequence is the wrong technology - and the consequence of that is a failing business.

Architecture is bi-directional. The right organization depends as much on technical choices as vice versa. We need a closed feedback loop between how we develop, and how we organize ourselves.
In many companies, the architectural feedback loop is utterly broken, hence they're doing with 50 people what could be done with 10.

One of the key organizational failures which lead to the need for "Scaling Agile" is that architecture is either disconnected from workplace reality, or not even considered to be important. By architecting both our organizational system and our technology to minimize handovers, communication chains and process complexity, many of the questions which cause managers to ponder the need for "Scaling Frameworks" are answered - without adding more roles, events or cadences.

This form of architecture doesn't happen in ivory towers, and it doesn't require fancy tools - it happens every day, in every team, and it either brings the organisation in a better direction, or a worse direction.


When was the last time you actively pondered how technical and organizational choices affect one another, and used that to make better choices in the other domain?

Monday, August 8, 2022

Make - or Buy?

Determining which systems, components or modules we should "Make" and which we should "Buy" (in extension: use from Open Source) is a challenging aspect for every IT organization. Even when there's a clear votum by management of developers in favor of one option, that vote is often formed with a myopic perspective: managers prefer to "Buy" whatever they can, whereas hardcore developers prefer to "Make" everything. Neither is wise.
But how do we discern?

There are a few key factors at play here:


FactorDetails
AvailabilityWhen there's an affordable, ready-made solution, then "Buy" to avoid reinventing the Wheel. Be sure that ready means ready and "affordable" has no strings attached.
Uniquenessyou need to "Make" anything that's unique to your business model.
AdaptabilityWhen there's only a small need for change and customization, "Buy" is preferable. Never underestimate "a small change."
Sustainability"Buy" only when both initial cost plus lifecycle cost are lower. Include migration and decommissioning costs.
SkillIf you need specialists that you don't and won't have, "Buy" from someone who has.
DependencyIf your business would have to shut down when the solution becomes unavailable, "Buy" puts you at your vendor's whim.
Write-offYou can "Buy" to gain speed even when all indicators favor "Make," if - and only if - you're willing to write off everything invested into the "Buy" solution.

Choose wisely - the answers are often not as obvious as they seem.

Sunday, January 24, 2021

The importance of testablility

In the article on Black Box Testing, we took a look at the testing nightmare caused by a product that was not designed for testing. This led to a followup point: "Testability", which is also a quality attribute of the ISO:25010. Let us examine a little closer what the value of testability for product development actually is.






What is even a "test"?

In science, every hypothesis needs to be falsifiable, i.e. it must be logically and practically possible to find counter-examples. 

Why are counter-examples so important? Let's use a real-world scenario, and start with a joke.

A sociologist, a physisicst and a mathematician ride on a train across Europe. As they cross the border into Switzerland, they see a black sheep. The sociologist exclaims, "Interesting. Swiss sheep are black!". The physicist corrects, "Almost. In Switzerland, there are black sheep." The mathematician shakes her head, "We only know that in Switzerland, there exists one sheep that is black on at least one side."

  • The first statement is very easy to disprove: they just need to encounter a sheep that isn't black.
  • The second statement is very hard to disprove: because even if the mathematician were right and another angle would reveal the sheep to not be fully black, the statement weren't automatically untrue, because there could be other sheep somewhere in Switzerland that are black.
  • Finally, the third statement holds true, because the reverse claim ("There is no sheep that is black on one side in Switzerland") has already been disproven by evidence.

This leads to a few follow up questions we need to ask about test design.


Test Precision

Imagine that our test setup to verify the above statements looked like this:

  1. Go to the travel agency.
  2. Book a flight to Zurich.
  3. Fly to Zurich.
  4. Take a taxi to the countryside.
  5. Get out at a meadow.
  6. Walk to the nearest sheep.
  7. Inspect the sheep's fur.
  8. If the sheep's fur color is black, then Swiss sheep are black.

Aside from the fact that after running this test, you might be stranded in the Winter Alps at midnight wearing nothing but your pyjamas and you're a couple hundred Euros poorer, this test could go wrong in so many ways.

For example, your travel agency might be closed. Or, you could have insufficient funds to book a flight. You could have forgotten your passport and aren't allowed to exit the airport, you could go to a meadow that has cows and not sheep, and the sheep's fur inspection might yield fleas. Which of these has anything to do with whether Swiss sheep are black?

We see this very often in "black box tests" as well:

  • it's very unclear what we're actually trying to test
  • just because the test failed, that doesn't mean that the thing we wanted to know is untrue
  • there's a huge overhead cost associated with validating our hypothesis
  • we don't return to a "clean slate" after the test
  • Success doesn't provide sufficient evidence to verify the test hypothesis.

Occam's Razor

In the 13th century, a monk by the name of Occam came up with what's known today as Occam's Razor, i.e., "entities should not be multiplied without necessity". Taking a look at our above test, that would mean that every test step that has nothing to do with sheep or fur color should be removed from the design.

The probability to run this test successfully increases by isolating the test object (sheep's fur) as far as possible, and eliminating all variability from the test that isn't directly associated to the hypothesis itself.


Verifyability

We verify a hypothesis by finding a counter-example to what's called the "alternate hypothesis" and assuming that if this one is untrue, then its logical opposite, called the "null hypothesis" is true. Unfortunately, this means we have to think in reverse.

In our example: To prove that all sheep are black, we have to sample all sheep. That's difficult. It's much easier to sample one non-black sheep, and thereby falsify that all sheep are black. If we fail to produce even one, then all sheep must be black.


Repeatability and Reproducibility

A proper test is a systematic, preferrably repeatable and reproducible, way of verifying our hypothesis. That means, it should be highly predictable in its outcome, and we should be able to test as often as we want.

Again, going back to our example, if we design our test like this:

  1. Take a look at a Swiss sheep.
  2. If it is white, then sheep are not black.
  3. If sheep are not - not black, then sheep are black.

This is terrible test design, because of some obvious flaws in each step: 

  1. The setup is an uncontrolled random sample. Since sheep are white or black, running this test on an unknown setup, means we haven't ruled out anything if we picked a black sheep.
  2. The alternate hypothesis is incomplete: Should the sheep be brown, then it is also not white.
  3. Assuming that step 2 didn't trigger, we would conclude that brown = black.

Since the "take a look at a Swiss sheep" is already part of the test design, each time we repeat this test, we get a different outcome, and we can't reproduce anything either, because if I run this test, my outcome will be different from yours.


Reproducibility

A repeatability problem occurs when the same setup can generate different results. In our example, "take a look at" could be fixed, assuming we take the mathematician's advice, by re-phrasing step 1 to: "Look at a Swiss sheep from all angles." This would lead everyone to reach the same conclusion.

We might also have to define what we call "white" and "black", or whether we would classify "brown" as a kind of white.

We increase reproducibility by being precise on how we examine the object under test, and which statements we want to make about our object under test.


Repeatability

Depending on what the purpose of our test is, we are doing well by removing the variation in our object under test. So, if our test objective is to prove or falsify that all sheep are black, we can set up a highly repeatable, highly reproducible test like this:

  1. Get a white Swiss sheep.
  2. Identify the color of the sheep.
  3. If it is not black, then the statement that Swiss sheep are black is false.

This experiment setup is going to produce the same outcome for everyone conducting the test anywhere across the globe, at any point in time.

While there is a risk that we fail in step 1 (if we can't get hold of a Swiss sheep), we could substitute the test object with a picture of a Swiss sheep without affecting the validity of the test itself.


What is testability?

A good test setup has:
  • a verifiable test hypothesis
  • a well-defined test object
  • a precise set of test instructions
  • absolutely minimized test complexity
  • high repeatability and reproducibility
When all these are given, we can say that we have good testability. The more compromises we need to make in any direction, the worse our testability gets. 

A product that has high testability allows us to formulate and verify any relevant test hypothesis with minimal effort.

A product with poor testability has a high difficulty associated with formulating or verifying a test hypothesis. This difficulty might translate into an increase of any or all of the following:
  • complexity
  • effort
  • cost
  • duration
  • validity
  • uncertainty


In conclusion

The more often you want to test a hypothesis, the more valuable high testability becomes.
With increasing change frequency, the need to re-verify a formerly true hypothesis also increases. 

Design your product from day 1 to be highly testable.
By the time you discover that a product's testability is unsustainably low, it's often extremely expensive to notch it up to the level where you need it.

Tuesday, January 19, 2021

The problem of Black Box Tests

One of the most fundamental enablers of agile ways of working is the ability to swiftly and reliably detect problems in your product - that is, to test it efficiently and effectively. Unfortunately, the "traditional" approach of black-box testing a running application is hardly useful for this purpose. 

I have created an executable use case to illustrate the problem. 


Let's take a look at this little service, and assume that it would be your responsibility to test it in a way that you can reliably tell whether it works correctly, or which problems it has:

You can call this service in the mini applet included on this page if you have JavaScript enabled..

Alea iacta est

You need Javascript to run this demo.

Yes, just try it out - roll the dice!
That's about as simple as an application can get.
Can you imagine how much effort it takes to properly test this simple application?

It's not enough to roll the dice once and get a number between 1 and 6 - how do you know that there isn't a possibility that the application might generate results outside that range?

And how would you know that you have fair dice? Call the service a thousand times and assume that you would get an approximately even distribution of values? What would be your thresholds for assuming that the dice are "fair"? What if 5% or fewer, or 25% or more results go to one number, which is statistically still possible with a decent probability?
You see the difficulty already.

But let's make this more difficult:

Hello

You need Javascript to run this demo.

What if I told you that this is a call to the same service?
Yes, exactly - you didn't know everything the service does when you created your test concept before.
There's a different feature hidden in the service: if you pass a user name to the request, it will greet you!

This adds a whole new dimension to the test complexity: you have to test with - and without - a user name. And would you want to try different user names?

  But that's not everything:

You lose!

You need Javascript to run this demo.

Did you even catch that this one behaves different?
What if I told you that this is another call to the same service?
Yes, exactly - you still didn't know everything the service does when you created your test concept.
There's another different feature hidden in the service: you can load the dice and cheat!

If you tell the service to cheat you, you will get unfair dice.

So, now you need to run your entire test set from above twice again - with and without cheating.

And we haven't even looked into whether there are multiple ways of cheating, or whether the cheating function always triggers correctly when the variable is set (hint: it doesn't). Good luck without knowing the application where the malfunction is.

But we're not done yet:

I win  

You need Javascript to run this demo.

Did you catch the difference here?
What if I told you that this is yet another call to yet again same service?

There's yet another different feature hidden in the service: if I use my name to cheat, I will get loaded dice in my advantage!

By now, you're probably doubting whether you understood the application at all when you started testing it.

The code

Now - let me blow your mind and tell you how little source code was required to totally blow your test complexity and effort out of proportion:


That's it. This little snippet of code is entirely sufficient to keep a Black Box tester busy for hours, potentially days, and still remain unable to make a reliable statement on whether they missed anything, and which problems the product may or may not have.

Depending on how your application is designed, a few minutes of development effort can generate a humongous mountain of effort in testing.
 
And that's why you can't possibly hope to ever achieve a decent test coverage on an application without knowing the code.

Testability

There's another problem: this code wasn't written with testing in mind (or, much rather: purposely written with poor testability in mind -- hee hee) so you have no way of ever coming up with an easier way to test this service, until it's rewritten.

And that's why you can't maintain sustainable high quality unless developers and testers actively collaborate to build highly testable software that is easy to work with, both for software changes and testing. 

Think of minimizing sustainable lead time - consider the total effort from request to release, and consider it both for initial creation and future modification. There's no point in optimizing for development speed if you slow down testing more than that, and likewise, there's no point in delivering minimal code if the consequence is totally bloated test scope.

Otherwise, you'll not be very agile.

Friday, January 1, 2021

Low-Code, No-Code, Full-Code - The Testing Challenge

In the Enterprise world, there's a huge market for so-called "Low Code" and "No Code" Solutions, and they do have a certain appeal - you need to do less coding, and as such, need less developers, to achieve your business objectives, because they bring a lot of "out-of-the-box" functionality. 

So why is it even something to talk about - and how does that relate to "Agile" ways of working?


Let's explore this one from a quality perspective.


The Paradigms

No-Code: Configure and Go

"No Code" solutions are especially appealing to organizations that have no IT department and are looking for something that someone without IT knowledge can configure in a way that's immediately useful.

An early implementation of no-code platforms will typically not even include a staging environment where people try out things. Many times, changes are immediately live, on a productive system. That's great for small organizations who know exactly what they're doing because it absolutely minimizes effort and maximizes speed.
It turns into a nightmare when someone, somehow, by pure accident, managed to delete the "Order" object and now you're happy-hunting for a couple thousand unprocessed orders that your angry customers are complaining about - with no way to remedy the system.

And it turns into an even worse nightmare when the system doesn't do what it's supposed to do, and you've got a chance smaller than hell freezing over of figuring out why the black box does what it actually does instead of what it's supposed to do.

When introducing Quality Assurance on a No-Code platform, organizations are often stuck using third-party testing software that uses slow, flaky, difficult-to-maintain, expensive UI-based tests which will eventually get in the way of high speed adaptability. Clean Code practices applied to testing are usually a rare find in such an environment.


Low-Code: Configure and Customize

"Low Code" solutions are especially appealing to managers who are out to deliver standardized software to their organization fast. Many of these systems bring huge chunks of standard capability out-of-the box and "only need customization where your organization doesn't do what everyone else does."

That sounds appealing and is a common route in many organization, who often find out only years after the initial introduction that "you can't sustain your market position by doing what everyone else does" - your business does require a lot of customization to stand out in the market, and the platform often doesn't accommodate for that. 

Most vendor solutions don't provide a suite of functional tests for your organization to validate the standard behaviour, which means you often end up creating duplicate or highly similar code in your customization efforts - or use standard functions that don't do what you think they would. Worse yet, many use proprietary languages that make it very difficult to test close to the code. In combination, that makes it extremely hard to test the customization you're building, and even harder to sustainably keep the platform flexible.



Full-Code: Design, Build, Optimize

"Full Code" solutions sound like the most effort and the slowest way of achieving things. But looks can be deceptive, especially to a non-expert, because a modern stack of standard frameworks like Spring, Vue and Bootstrap, can literally make it a matter of minutes for a developer to produce the same kind of results that a low-code or no-code platform configuration would provide, without any of the quality drawbacks of Low-Code or No-Code.

Your organization has full control over the quality and sustainability of a full-code solution. It depends entirely upon what kind of engineering practices you apply, which technologies you use and which standards for quality you set for yourself.


Quality Control

To sustainably high quality at a rapid pace, you need full quality control:
  • You must be able to quickly validate that a component does what it's supposed to do.
  • You must be able to quickly figure out when something breaks, what it was, why and how.
  • When something breaks, you must be able to control blast radius.
  • You need a systematic way of isolating causes, effects and impact.
The most common approach to maintain these is a CI/CD pipeline that runs a robust test automation in the delivery process. To make it feasible that this control is exercised upon every single change that anyone makes at any point in time, it should not take longer than a few minutes, lest people are tempted to skip it when in a hurry.

The problem with both No-Code and Low-Code solutions is: In many cases, such platforms aren't even built for testability, and that becomes a nightmare for agile development. Instead of running a test where and how it is most efficient to run, you invest a lot of brainpower and time into figuring out how to run the test in a way that fits your technology: You have subjected quality to the technology, instead of the other way around!

In a low-code environment, this can become even more problematic, when custom components start to interfere with standard components in a way that is unknown and uncontrollable in a huge black box.


Non-functional Quality

Although I would not outright suggest to opt for a full-code solution (which potentially is not in the best interests of your organization, and it's entirely implausible without skilled developers), I would like to share a list of non-functional quality attributes that may not be considered when selecting a new system, platform or service.

In order to remain agile - that is, to be able to quickly, effectively and easily implement changes in a sustainable manner - your platform should also accommodate for the following non-functional quality requirements:

Factor Decisions
Testability
How much effort is it to test your business logic?
This must go far beyond having a human check briefly whether something works as intended. It needs to include ongoing execution, maintenance and control of any important tests whenever any change is made. And remember: any function you can't test may cause problems - even when you're not intentionally using it!
Traceability
How closely are cause and effect related?
You don't want a change to X also to affect Y and Z if that wasn't your intent! Are you able to isolate changes you're making - and are you able to isolate the impact of these changes?
This should go for the initial setup as well as for the entire lifecylce of the product.
Extensibility
How much effort does it take to add, change or remove business logic?
Adding a form field to a user interface is a start, not the end. Most of your data has a business purpose, and it may need to be sent to business partners, reported in finance, analyzed in marketing etc. How much effort does it take to verify everything turns out as intended?
Flexibility
How often will you be making changes?
If you're expecting a change a year, you can permit higher test efforts per change, but when you're looking at new stuff in a weekly manner, you could be overwhelmed by high test or change efforts, and cutting corners will become almost inevitable.
Security
Can you really trust your system?
Although every system could have vulnerabilities, and standard softwares tend to have fewer, but how can you test for Zero-Days unless you can fully test the intricate inner workings?
Also, some legislation like GDPR forces you to expose certain data processings, and you may need to provide evidence what your system does in order to do that. This is extremely difficult when some behavioural description of certain aspects are a black box.
Mutability
How much effort would it take to migrate to a new platform and decommission the current platform?
When you introduce a system without having an understanding of how much time, effort and risk is involved in a migration or decommissioning initiative, it might be easier to kill your current company and start another business than to get rid of the current technology. That means you could find yourself in a hostage situation when the day comes that your platform is no longer the best choice for your business, and you have no choice except continuously throwing good money after the bad.

As a general rule of thumb, low-code and no-code platforms tend not to emphasize these, so the value your organization places on these non-functional requirements correlates with the plausibility of selecting this approach.

Conclusion

With a lot of these to be said, if you're in the comfortable situation of introducing a new technology, ensure that you check the non-functional requirements and don't get blinded by the cute bucket of functionality a low-code or no-code solution may offer. If your platform does poorly especially on traceability, testability or mutability, you're going to trade off your agility for some extremely painful workaround that could increase the Cost of Ownership of your solution beyond feasible limits.

It wouldn't be the first time that I'd advise a client to "trash everything and start with a blank folder. Within a few years, you'll be faster, have saved money and made better business."

Tuesday, December 1, 2020

Refactor, Rewrite or Redesign?

Have you ever heard the term "Refactoring Sprint?" There is a lot of confusion around what Refactoring, Rewrite und Redesign are, what they entail, as well as how and when to use them - so here's a small infographic:



Refactoring

Refactoring is all about small changes to the way code has been written to reduce technical debt as the code and our understanding thereof grows. It's a continuous exercise and should always be an integral part of the work. Safe refactoring is a low-risk exercise that happens within the scope of existing unit tests, which should verify that indeed we haven't made any unwanted changes.

Examples of Refactoring are: Extracting a variable, renaming a function, consolidating statements or moving code from one place to another.

It should go without saying that in professional development, Refactoring should never be a separate task and should never be done in big batches. Your Definition of Done should include that no essential refactoring is left to do.


Rewrite

We rewrite code when we discover that a current code segment is no longer the best way to achieve a specific purpose. Depending on your test coverage, rewrites are a high-risk exercise that are the basis for future risk reduction.

Examples of Rewrites are new and better means (e.g. a new technology) to do the same thing, or stumbling upon a legacy that is difficult to work with (e.g. complex methods without tests).

Smaller rewrites (e.g. method rewrites) should be done autonomously as regular part of the work when the need is discovered and when developers have sufficient confidence that they can do this safely.
Larger rewrites should be planned with the team, as this exercise could consume significant portions of time and may need additional attention to failure potential.


Redesign

A redesign must happen when the purpose of an existing component has changed in ways that the current solution is no longer the best possible thing. Redesign is a high-risk and highly time consuming exercise that should be done to get out of a corner. If you have highly malleable code that was well refactored on a continuous basis, redesign should hardly ever be a case unless you have major changes to the business context in which a system is operated.

Examples of Redesign might include moving from batch processing to data streams, changing the database paradigm, or acquiring a new line of business that requires processing different data.

Redesign should always be a planned whole-team exercise that might even break down into a hierarchy of mid- and short term goals. Redesign is a mix of technical and business decision, so it should be aligned with Product.




Friday, April 26, 2019

Agile Architecture

What's should an Architect in an Agile environment do?

Irrespective whether we have a single person as "assigned architect" or have architecture as a shared responsibility of (one or more) teams - there's a massive difference between "Emergent Architecture" and "Architecture by Chance". 
The one thing worse than having the wrong goal is not having a goal at all - and that's true for architecture as well as for the product from a customer's perspective, so let's discuss a bit of what the scope of agile architecture is.

Note: This article is my current, personal view on the topic and I sincerely invite feedback and criticism if I'm off.






Architectural Intent

Architecture may or may not have an intent. Such an intent is usually to create an overall system that minimizes cost or effort for one, more or all involved parties.
This intent can be formulated either in the short, mid or long term. If it isn't formulated at all, the probability quickly diminishes that the optimization goals of the organization are indeed met.

Optimization goals

Typical optimization goals for architecture could be, for example:
  • Minimize Cost of Change
  • Minimize Time to Market
  • Minimize working hours
  • Organizational autonomy
You're free to pick your own, based on your specific circumstance.

Organizational Constraints

Sometimes, we also operate under constraints, which may need to be considered as well. 
Examples might be:
  • Regulatory compliance (e.g. privacy laws)
  • Technological limitations (e.g. company policy)
  • Security constraints (e.g. contractual demands)
These restrict the solution space and should be handled with caution - too many suffocate innovation, while missing any essential ones can kill the product! (as seen in the recent Sixt vs. Accenture case)

Not Strategic Architecture

Although the following activities are indeed part of architecture, these topics are not strategically relevant from an architecture perspective: The more fluently they can shift throughout a product's lifespan, the better other optimization goals can be met.
  1. Formulating, planning and forecasting system capabilities
  2. Defining interfaces, processes, data models and interfaces
  3. Component delivery strategy and component responsibility
  4. Deciding on specific techonologies to solve certain problems
While these things are relevant, agile teams can decide those things on a per-item basis and can indeed evolve and dynamically change when knowledge and skills to do these things are available in the team.


Architectural Direction

Some core questions may be worthwhile answering upfront. These are directional rather than content-wise and can be answered with "Yes" or "No":
  1. Do we want to have Loose Coupling?
  2. Do we want to enable Micro-Releases?
  3. Will we go xAAS?
  4. Do we need scalability?
Note that I'm not talking about big architecture upfront here - even for my company homepage, I have answered these questions by taking a couple minutes to orient myself what I want to achieve before writing the first line of code. 


Architectural vision

A vision that nobody can remember is worthless, as is a vision that doesn't help in orienting activities. Using the above questions, a vision statement could be formulated like:
"To develop a system where changes can be done (and undone) at any time within seconds, we create loosely coupled components that can be independently released as autonomous services which can be flexibly scaled."
Then comes the difficult part: getting there.
And that's more difficult on a "brown field" than it is on a green field.
In order to get there, we need to have some mechanisms in place, which may or may not be helpful depending on where we want to be.



Core paradigms

Where we may need to actively invest effort into enabling and establishing some core paradigms for an equally robust and flexible architecture. 

These paradigms could be, for example:

  • Continuous Integration/Delivery/Deployment
  • Automate Everything
  • Measure Everything
  • Test-Driven Design
  • Impermanent Components
  • You Build it, you run it (“true DevOps”)
This means that every architectural decision made by the teams can be benchmarked by whether (or not) an action, decision or choice brings us closer or distracts us from these paradigms.

As such, we may add to our architectural vision further statements like the following:
Every component uses a Continuous Delivery process, and where possible, even Continuous Deployment. 
Every activity within the pipeline and on the production environment is automated. 
We have real time data on every aspect of the system and therefore full transparency on its behaviour. 
Code that hasn't been designed based on executable tests doesn't go live. 
Replacing components is a low-effort task. 
Developers have both responsibility and authority regarding production.

All of these paradigms are still outside the specific solution space and do not answer the "How".



Architectural roadmap

After we have agreed on where we want to go, we need to actually go and do it. Since our customers don't pay us to do architecture, but to deliver something of value, these actions should be seamlessly integrated with our development efforts, ideally experimenting with practices and introducing tools while we're delivering tangible value.

If we're in a Green Field, setting up a CD pipeline and delivering an automatically tested "Hello World" into a measured, containerized production environment may bloat up the first user story by a day or two, and then speed up everything that comes afterwards.

On a Brown Field, that's an entirely different story - we can't just do this so easily. What if we have 16 million lines of unsustainable code riddled with defects? CD would be insanity. There's no data from Production, nobody knows the current state of the deployment environment and extracting a single feature from the existing monolith could take months.

We then get into an architectural action roadmap that might look like this:


  1. Short term goals
    1. Automate build process & set up CI pipeline.
    2. Agree with stakeholders to accept current "status quo" as baseline quality for CI/CD.
    3. Every new line of code gets a unit test.
    4. Build metric hooks and set up a measurement system.
    5. Build service API for core monolith.
  2. Mid term goals
    1. Introduce Coding Conventions and add measurement to CI pipeline
    2. Stop the current branching process and move towards genuine CD process
    3. Automated deployment to Test Stage
    4. Automate e2e tests for critical user journeys
    5. Decouple frontend and backend.
  3. Long term goals
    1. Build new capabilities as autonomous services.
    2. Containerize services.
    3. Extract service components from existing monolith.
    4. Move towards polyglot development.
    5. Move individual services to Cloud.
    6. Build Security Services (IAM, Secrets-as-Service, etc.)

These items can then be added into the Product Backlog and prioritized as needed. The authority and autonomy on how to do this specifically still rests within the development teams.

Any architectural activity that doesn't trace back to a goal may be questionable. Any architectural goal that doesn't trace back to a paradigm invites scrutiny - and if anything doesn't match intent, another discussion may be in order.

Architectural responsibility

None of the above items mandate having a specific "architect" role, although in larger systems, it may indeed be worthwhile to have at least one person take responsibility to see that there is indeed clarity on the architectural:
  1. Vision
  2. Intent
  3. Direction
  4. Goals
  5. Paradigms
  6. Ongoing Activity