<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://ewolff.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://ewolff.com/" rel="alternate" type="text/html" /><updated>2026-09-07T12:34:16+00:00</updated><id>https://ewolff.com/feed.xml</id><title type="html">ewolff.com</title><subtitle>Eberhard Wolff&apos;s Homepage</subtitle><author><name>Eberhard Wolff</name></author><entry><title type="html">What Really Drives Software Architecture?</title><link href="https://ewolff.com/2026/07/31/what-really-drives-software-architecture.html" rel="alternate" type="text/html" title="What Really Drives Software Architecture?" /><published>2026-07-31T00:00:00+00:00</published><updated>2026-07-31T00:00:00+00:00</updated><id>https://ewolff.com/2026/07/31/what-really-drives-software-architecture</id><content type="html" xml:base="https://ewolff.com/2026/07/31/what-really-drives-software-architecture.html"><![CDATA[<p>It starts with terminology. Some people speak of <em>non-functional
requirements</em>, referring to the technical characteristics of a system,
in contrast to <em>functional requirements</em>, which describe the business
functionality the system implements. Today, the term <em>quality</em> is more
commonly used. It is somewhat more abstract than specific
requirements, but perhaps better captures the underlying
idea. Individual requirements can be expressed as <em>quality scenarios</em>,
which describe in concrete terms how the system should behave in
particular situations.</p>

<h2 id="typical-quality-attributes">Typical Quality Attributes</h2>

<p>Every software system requires a different set of qualities because
every system solves a different problem. Nevertheless, quality models
such as ISO 25010 identify common quality dimensions. Typical examples
include:</p>

<ul>
  <li>
    <p><strong>Maintainability</strong> is particularly important because poor
maintainability can eventually make a system impossible to change,
forcing a complete rewrite — a total loss of the original
investment. The consequences primarily affect developers: working on
an unmaintainable system is frustrating and can become
demoralizing. Unsurprisingly, technical teams often value
maintainability highly, even if other stakeholders do not.</p>
  </li>
  <li>
    <p><strong>Security</strong> is usually about managing risk. Security
vulnerabilities only become a problem if they are exploited or
publicly disclosed. They can be introduced by only a few careless
lines of code — or even by relying on a library with a
vulnerability. The real question is therefore how much risk an
organization is willing to accept.</p>
  </li>
  <li>
    <p><strong>Performance</strong> can be important, but in many situations
<strong>scalability</strong> matters more. A scalable system can improve
performance by adding more resources, such as additional or more
powerful servers.</p>
  </li>
</ul>

<h2 id="achieving-scalability">Achieving Scalability</h2>

<p>Many architects believe that maintainability and scalability are the
most important qualities of a software system. Fortunately, software
architecture can significantly influence both.</p>

<p>Scalability can be supported by selecting appropriate technologies —
for example, cloud platforms — and by designing systems that scale
horizontally without obvious bottlenecks. If scalability later proves
insufficient, changing the system’s structure or technology stack may
solve the problem through architectural changes.</p>

<p>However, architecture alone cannot solve scalability. A scalable
system still requires additional resources when demand
increases. Operations therefore play a crucial role. Besides providing
the necessary infrastructure, operations can also influence other
qualities such as security.</p>

<p class="callout">Scalability is a luxury problem.</p>

<p>Furthermore, scalability is essentially a luxury problem. It only
becomes relevant when a system already has many users — in other
words, after the product has already succeeded in the market. Before
scalability matters, other qualities such as usability or valuable
features must attract users in the first place.</p>

<p>Usability, however, cannot really be achieved through traditional
architectural mechanisms. Almost any frontend technology can be used
to build either an excellent or a terrible user experience. Choosing
React over Angular — or vice versa — has little impact on
usability. Instead, UX practices determine whether users find a system
pleasant to use.</p>

<h2 id="what-about-maintainability">What About Maintainability?</h2>

<p>Maintainability also appears to be largely a technical
concern. Automated tests, a well-structured architecture, and high
code quality certainly help.</p>

<p class="callout">It is unrealistic to make every part of every system to the highly
changeable.</p>

<p>Yet maintainability only matters if the software actually needs to
change. To invest wisely, developers need to know <em>which</em> parts of the
system must be adaptable. If flexible pricing is central to a
company’s competitive advantage, then the pricing logic deserves
exceptional maintainability. It is unrealistic to build every part of
every system to the highest maintainability standard. Prioritization
is therefore essential.</p>

<p>Moreover, code quality is not the only way to achieve
adaptability. Pricing rules, for example, might be made configurable
or implemented in a rule engine, allowing business users to change
behavior without modifying code at all. In such cases, code quality
becomes largely irrelevant for adaptability.</p>

<h2 id="do-technology-and-architecture-matter-at-all">Do Technology and Architecture Matter at All?</h2>

<p>Architecture can only create the prerequisites for
scalability. Whether scalability becomes relevant depends on the
product’s success, which itself depends on qualities such as
usability — qualities that architecture can influence only indirectly.</p>

<p>Similarly, maintainability only becomes valuable when architects
understand where change is expected. Technical measures such as clean
code and testing help, but only in those strategically important
areas. If adaptability can instead be achieved through configuration
or business rules, code quality becomes much less important.</p>

<p class="callout">Architecture is only one part of ensuring quality.</p>

<p>Architecture is therefore only one part of achieving the required
quality attributes.</p>

<p>Even fundamental technology choices may not be driven primarily by
quality requirements. Consider the choice between Java and
JavaScript. Does it determine maintainability? Other factors, such as
clean code, are often more influential. Does it determine performance?
Perhaps — but databases or other infrastructure components are
usually the real bottlenecks. Many performance problems do not
require switching programming languages, and scalable systems can
often compensate simply by adding resources.</p>

<p>Technology choices remain important for another reason: the team
itself. A team with years of Java experience will usually produce
better results with Java than with JavaScript. The determining factor
is therefore not necessarily the system’s required qualities but the
composition and experience of the development team.</p>

<h2 id="what-should-we-do">What Should We Do?</h2>

<p>If software quality cannot simply be achieved through architectural
decisions, how should organizations respond?</p>

<p>One common reaction is for architects to focus on technical qualities
such as maintainability and scalability. This makes sense because
architecture has the greatest influence on these characteristics. Yet
this focus alone may not be sufficient. A system capable of supporting
millions of users is of little value if its poor usability prevents
anyone from adopting it.</p>

<p class="callout">Ensure <em>all</em> relevant quality attributes are addressed sufficiently!</p>

<p>Another approach is to consider <em>all</em> relevant quality attributes and
ensure they are addressed sufficiently. This extends beyond the
traditional responsibilities of software architects, since achieving
many qualities requires collaboration with UX specialists, operations
teams, and business stakeholders. Architects would need to coordinate
these efforts rather than focus solely on technical design.</p>

<p>Perhaps, in the future, quality requirements will be managed in the
same structured way as functional requirements—for example, by Product
Owners or similar roles. At least then, responsibility for quality
would no longer rest solely with architects.</p>

<h2 id="conclusion">Conclusion</h2>

<p>The common claim that quality attributes are architectural drivers is
only partially true. Many qualities depend on factors beyond software
architecture, and in many cases those other factors are actually more
influential. Conversely, even fundamental technical decisions — such as
choosing a programming language — cannot always be derived directly from
quality requirements.</p>

<p>Nevertheless, understanding and achieving the right quality attributes
remains essential to the success of any software
project. Unfortunately, many projects still lack a structured process
for identifying and managing quality. This stands in sharp contrast to
the disciplined treatment of functional requirements, which typically
receive much more systematic attention.</p>

<p><em>This is a translation of my <a href="https://entwickler.de/reader/reading/java-magazin/9.2025/ef60864f86694a400730b97c">German article at Java
Magazin</a>.</em></p>]]></content><author><name>Eberhard Wolff</name></author><category term="Quality" /><summary type="html"><![CDATA[Quality — or non-functional requirements — is often said to drive software architecture, meaning the structure of the software and the technical decisions behind it. While this sounds plausible at first glance, a closer look reveals that the situation is far more complicated.]]></summary></entry><entry><title type="html">Getting Architecture Right: Not Bounded Context but Models and Modularization</title><link href="https://ewolff.com/2026/07/09/not-bounded-context-but-models-and-modularization.html" rel="alternate" type="text/html" title="Getting Architecture Right: Not Bounded Context but Models and Modularization" /><published>2026-07-09T00:00:00+00:00</published><updated>2026-07-09T00:00:00+00:00</updated><id>https://ewolff.com/2026/07/09/not-bounded-context-but-models-and-modularization</id><content type="html" xml:base="https://ewolff.com/2026/07/09/not-bounded-context-but-models-and-modularization.html"><![CDATA[<p>Bounded Contexts are often regarded as a silver bullet for the
architecture of the domain logic. But in practice they are rarely
sufficient. Inside a Bounded Context a Ubiquitous Language is defined
— a shared language between developers and domain experts. Every
project has its own language. And terms are often defined differently
in different contexts.</p>

<p>For example, during hotel check-in, the customer is the human checking
in. However, when invoicing the same stay, for a business trip the
customer might be the employer of the person checking in. This is a
different customer and a company rather than a human. So check-in and
invoicing are probably two Bounded Context with different
languages. Each language ahas a different definition of the term
“customer”. Such distinctions are valuable for analyzing and
understanding the business domain. But are they also a good starting
point for architecture?</p>

<p class="callout">As architects, what we really want is a decomposition of the domain
logic into modules.</p>

<p>As architects, what we really want is a decomposition of the domain
logic into modules. A Bounded Context defines the scope of such a
module — for example, check-in or invoicing. At the same time,
Domain-Driven Design views a Bounded Context also as a part of the
software for which a team is responsible.</p>

<p>As a result, Bounded Contexts are defined as:</p>

<ul>
  <li>a boundaries for <strong>modules</strong> of the domain logic,</li>
  <li>the scope of a <strong>Ubiquitous Language</strong>, and</li>
  <li>the area of responsibility of a <strong>development team</strong>.</li>
</ul>

<p>These three meanings are problematic. They overload the concept and
make it harder to understand (<a href="https://software-architektur.tv/2025/04/25/folge261.html">German stream /
podcast</a>
discussing the three definitions.)</p>

<p>In some situations they even lead to questionable restrictions. Why
should teams be allowed to work only on Bounded Context? In reality,
quite some teams work on UI or entirely different things like
infrastructure automation. Why should collaboration between multiple
teams on the same Bounded Context be ruled out? Certainly, such
collaboration may introduce friction, but that alone is not a reason
to prohibit it. And why couldn’t an area governed by a single
Ubiquitous Language contain multiple modules?</p>

<h2 id="models">Models</h2>

<p>At the core of what we as software architects actually want to achieve
with Bounded Contexts is the creation of models.</p>

<p>In fact, the original <a href="">Domain-Driven Design book</a> begins by discussing models, using the example of a map of China. The book defines a model as follows:</p>

<blockquote>
  <p>“A model is a simplification. It is an interpretation of reality
that abstracts the aspects relevant to solving the problem at hand
and ignores extraneous details.”</p>
</blockquote>

<p>The famous quote by George Box, <em>“All models are wrong, but some are
useful,”</em> makes the point clear: the goal is not a perfect
representation of reality but a useful simplification. Which aspects
are relevant depends on perspective—which brings us back to Bounded
Contexts.</p>

<h2 id="the-real-problem-organizing-business-logic">The Real Problem: Organizing Business Logic</h2>

<p>The goal of the entire exercise is a sensible decomposition of
business logic that enables maintainability and extensibility.</p>

<p>The team-related questions that Bounded Contexts also address are
secondary. A different Ubiquitous Language may indicate that a
separate module makes sense, but the concept of multiple Ubiquitous
Languages is not always easy to grasp.</p>

<p>From an architect’s perspective, it may be easier to think in terms of
<strong>data</strong> and its structure. The example of the ambiguous definition of
“customer” in different Bounded Contexts is directly reflected in data
modeling. Focusing on data modeling might be easier for architects
than focusing on the multiple definitions of a term.</p>

<p class="callout">Bounded Contexts do not solve the problem of organizing business
logic.</p>

<p>Bounded Contexts therefore do not solve the problem of organizing
business logic. They merely tell us that we need multiple models — not
what those models are or how to find them.</p>

<p>Of course, collaborative modeling approaches such as <a href="https://www.eventstorming.com/">Event
Storming</a> can help identify models,
and they are very valuable. Nevertheless, it is useful to have
alternatives. Moreover, we must be able to evaluate and refine the
results of such design exercises. To do that, we need to understand
what makes a model suitable for software.</p>

<p class="callout">Models are simplifications created for a specific purpose.</p>

<p>Models are simplifications created for a specific purpose — in this
case, representing a business domain in software. Since many different
models are possible, understanding the characteristics of a good model
becomes critically important.</p>

<h2 id="information-hiding">Information Hiding</h2>

<p>Information Hiding is a fundamental concept for developing large
systems.</p>

<p>The foundation is the observation that developers will use every piece
of information they can get about a module. Once they depend on that
information, it can no longer be changed easily. As a result, the
system becomes difficult to modify.</p>

<p>An example: Consider a class intended to model a bank account. It
exposes an instance variable representing the account balance. We know
that allowing direct access to this variable without getters and
setters is a bad idea — but why?</p>

<p>If direct access is permitted, we lose the ability to change the
model. Instead of storing the balance directly, we might later decide
to calculate it from the transaction history. In that case, the
internal implementation would be entirely different.</p>

<p class="callout">If consumers know only the interface, we can swap the implementation
for another without affecting other modules.</p>

<p>If consumers know only the interface, we can swap the implementation
for another without affecting other modules. Hiding the implementation
is therefore an important aspect of Information Hiding.</p>

<p>This leads to another important insight: data such as the account
balance is not a good foundation for modularization. Functionality —
such as retrieving the account balance — is the better starting
point. Certain data is needed to provide that functionality, but the
data itself should not define the module.</p>

<h2 id="coarse-grained-modules">Coarse-grained Modules</h2>

<p>What we discussed at the class level also applies to coarse-grained
modules, such as those that often form the basis of Bounded Contexts
or Microservices.</p>

<p>A coarse-grained module responsible for bank accounts will likely have
a database. If other parts of the system access that database directly
instead of going through the module’s interface — for example, to read
account balances — then changing the module becomes much more difficult.</p>

<p>Of course, some information cannot be hidden. For example, calculating
a balance from transactions will generally take longer than returning
a stored value. This runtime behavior can be observed even if only the
interface is exposed. It might be important if a consumer of the
interface has some performance constraints that limit how the
implementation can be changed.</p>

<p>Information Hiding therefore suggests that we should focus on the
interfaces of modules and models. However, that alone is not enough to
design good modules.</p>

<h2 id="abstract-data-types">Abstract Data Types</h2>

<p>The concept of <a href="https://en.wikipedia.org/wiki/Abstract_data_type"><strong>Abstract Data Types
(ADTs)</strong></a> can help
us further refine the definition of an interface. An ADT defines the
behavior of a data type from the perspective of its users. Besides the
set of values the data type can represent, its defining
characteristics are primarily its operations — that is, its interface.</p>

<p>A classic example of an ADT is the
<a href="https://en.wikipedia.org/wiki/Stack_(abstract_data_type)"><strong>stack</strong></a>. The
data structure is modeled after a physical stack, such as a stack of
plates. A stack provides the following operations:</p>

<ul>
  <li><strong>Push</strong> places an item onto the stack.</li>
  <li><strong>Pop</strong> removes and returns the most recently added item.</li>
  <li>Optionally, an operation can test whether the stack is empty,
although this is not an essential operation.</li>
</ul>

<p>If we first push the number 23 and then 42 onto the stack, a
subsequent pop returns 42, and the next pop returns 23. Thus, <strong>Pop</strong>
not only returns the element but also removes it from the stack. A
stack is therefore a <strong>Last In – First Out (LIFO)</strong> data structure.</p>

<p>At the interface level, we can define additional semantics using
<strong>preconditions</strong>, <strong>postconditions</strong>, and <strong>invariants</strong>.</p>

<p>The precondition of <strong>Pop</strong> is that the stack must not be empty. After
executing <strong>Push</strong>, the stack is guaranteed not to be empty, making
this a postcondition of <strong>Push</strong>. There may also be invariants that
always hold.</p>

<p class="callout">ADTs can become almost a specification of a module.</p>

<p>ADTs therefore extend the concept of Information Hiding by recognizing
that an interface can be more than just a list of operations — it can
become almost a specification of a module.</p>

<p>Nevertheless, different implementations of the same ADT are
possible. For example, a stack can be implemented using an array and
an index pointing to the top element. Alternatively, it can be
implemented as a linked list, where each stack element points to the
one below it. These implementations may differ in performance or
memory consumption, but both satisfy the semantics of a stack.</p>

<p>Using the ideas behind ADTs, we can define the behavior of a module
solely at the interface level without prescribing its
implementation. Focusing on the interface therefore achieves more than
Information Hiding alone.</p>

<p>This emphasis on specifying a module through its interface is also the
central idea behind <strong>Test-Driven Development (TDD)</strong>. A test is
written before the implementation exists. To write the test, the
interface must first be defined. Only after the test has been written
is the implementation created. Here, too, <a href="https://bsky.app/profile/jasongorman.bsky.social/post/3lnhjxke4vc22">the design is
driven</a>
by the perspective of how the module will be used.</p>

<h2 id="autonomy">Autonomy</h2>

<p>Ideally, modules should be <strong>autonomous</strong>: every piece of
functionality should be implementable without relying on other modules
too much. Then, a change to a particular feature can typically be made
within a single module without affecting others.</p>

<p>In practice, however, not every change can be confined to one
module. Modules together form the overall system, and some changes
inevitably cross module boundaries. Nevertheless, autonomy remains an
important design goal.</p>

<p class="callout">Autonomy leads to additional heuristics for creating good modules.</p>

<p>If we consider autonomy as a design objective, it leads to additional
heuristics for creating good modules.</p>

<p>Take the hotel example discussed earlier. We could implement the
system using three modules:</p>

<ul>
  <li>Check-In</li>
  <li>Invoicing</li>
  <li>Customer</li>
</ul>

<p>The Check-In and Invoicing modules contain the business logic for their
respective processes. The Customer module provides customer data.</p>

<p>Such a design is certainly feasible, but the Customer module may
become problematic for two reasons.</p>

<ol>
  <li>
    <p><strong>Invoicing</strong> and <strong>Check-In</strong> require different information about
a customer. In fact, they deal with different concepts both called
“customer”. As discussed earlier, the customer in Check-In must be
a human, whereas in Invoicing this is not necessarily the case. Other
parts of the system may introduce yet more interpretations. During
booking, for example, customers might be travel agents who book
trips on behalf of companies. These correspond to several customers
in the Check-In context but perhaps only one customer in
Invoicing. Trying to unify all these concepts within a single
Customer module may make that module quite complex.</p>
  </li>
  <li>
    <p>Many hotel functions require <strong>customer</strong> information: Check-In,
Invoicing, Booking, and so on. This is hardly surprising, since a
hotel provides services to customers. So quite a lot of business
logic will deal with customers. If customer functionality is
concentrated in a single module, that module will be used by many
others. It becomes a dependency hotspot, reducing the autonomy of
the remaining modules.</p>
  </li>
</ol>

<p>Autonomy therefore highlights a particular weakness of modules that
merely model data. Such modules reduce autonomy and tend to have many
dependencies.</p>

<p>On the other hand, they may offer advantages. For example, storing a
customer’s name in one place makes it easier to handle name changes
consistently throughout the system. Whether this benefit justifies the
reduced autonomy is a trade-off that must be evaluated — but at least
the trade-off becomes explicit. Quite honestly valuing autonomy lower
than supporting a name change would be surprising. But the trade-off
might be different in other cases.</p>

<p>Moreover, a module that merely models data is also likely to violate
the principles of <strong>Information Hiding</strong>, since data is typically an
implementation detail. Nevertheless, many systems are designed around
such data-centric modules. When improving these architectures, it is
important to articulate their disadvantages clearly before deciding
whether restructuring is worthwhile.</p>

<p>The decision is not simply black or white. Some customer information,
such as the customer’s name, may be placed in a shared module, while
more specialized information—such as booking preferences—can remain in
the Booking module that actually requires it.</p>

<h1 id="three-heuristics">Three Heuristics</h1>

<p>We now have three heuristics for designing good models or modules:</p>

<ul>
  <li><strong>Information Hiding</strong> suggests that modules should be defined in
terms of their interfaces — and thus their functionality — rather
than their data.</li>
  <li><strong>Abstract Data Types (ADTs)</strong> demonstrate that an interface can
define the meaning of functionality. Preconditions, postconditions,
and invariants are also part of the interface. Much like Test-Driven
Development, adopting the external perspective of a module’s users
helps achieve a sound decomposition.</li>
  <li><strong>Autonomy</strong> serves as another design goal, making modules that
merely model shared data appear less attractive.</li>
</ul>

<h1 id="examples">Examples</h1>

<p>Let us apply these heuristics to design decisions commonly encountered
in practice.</p>

<p>Should an <strong>ordering process</strong> implement not only the execution of an
order but also the decision whether a customer is allowed to place an
order?</p>

<p>From the perspective of ADTs, this seems reasonable. Determining
whether ordering is permitted is a <strong>precondition</strong> for the ordering
operation itself.</p>

<p>However, this is only one heuristic. If authorizing an order
requires entirely different data and business logic from processing
the order, then authorization represents a different model and should
probably be implemented separately.</p>

<p>Should a <strong>payment process</strong> know about products?</p>

<p>Looking only at the interface, a payment service should probably
receive nothing more than a monetary amount and return whether the
payment succeeded.</p>

<p>On the other hand, there may be situations where products must be
known. For example, a credit card company may provide insurance for
certain products and therefore require information about what was
purchased.</p>

<p>Finally, the autonomy heuristic can guide the decision of whether
modules should contain shared data models or maintain their own
specialized representations.</p>

<p>Thus, these heuristics point us in a particular direction, but they do
not determine the correct solution. What the best design is, still
needs to be evaluated in each specific situation.</p>

<h1 id="conclusion">Conclusion</h1>

<p>Creating models and modules for business logic is the central challenge
of business architecture.</p>

<p>A Bounded Context merely tells us that multiple models may exist. It
does not explain what those models should be or how to identify them.</p>

<p>Information Hiding, Abstract Data Types, and Autonomy provide useful
heuristics for discovering and evaluating such models. Other
approaches, such as Event Storming, also contribute to this
process. Even there, however, these heuristics remain valuable,
because Event Storming merely produces candidate models that must
subsequently be evaluated and refined.</p>

<p>More generally, these heuristics are useful whenever architectural
designs need to be assessed.</p>

<p><em>This is a translation of my <a href="https://entwickler.de/reader/reading/java-magazin/8.2025/807160beec78709947ca8a04">German article at Java
Magazin</a>.</em></p>]]></content><author><name>Eberhard Wolff</name></author><category term="Modularization" /><category term="Domain-driven Design" /><summary type="html"><![CDATA[A good decomposition of the domain logic is crucial for making systems maintainable in the long term. Discussions often focus on Bounded Contexts — but models are far more important.]]></summary></entry><entry><title type="html">Independent? Impossible! Why Dependencies Matter So Much</title><link href="https://ewolff.com/2026/06/24/independent-impossible-why-dependencies-matter-so-much.html" rel="alternate" type="text/html" title="Independent? Impossible! Why Dependencies Matter So Much" /><published>2026-06-24T00:00:00+00:00</published><updated>2026-06-24T00:00:00+00:00</updated><id>https://ewolff.com/2026/06/24/independent-impossible-why-dependencies-matter-so-much</id><content type="html" xml:base="https://ewolff.com/2026/06/24/independent-impossible-why-dependencies-matter-so-much.html"><![CDATA[<h1 id="independent-impossible-why-dependencies-matter-so-much">Independent? Impossible! Why Dependencies Matter So Much</h1>

<p>Dependencies are so important in architecture because they make it
difficult to predict the impact of changes. If everything depends on
everything else, a change can have unforeseen consequences,
potentially affecting entirely different parts of the
system.</p>

<p>Therefore, the goal of dependency management is <a href="/2025/09/15/loose-coupling.html">loose
coupling</a>: a change should have as
little impact as possible on other parts of the system. This applies
at every level. Changes to classes should ideally have minimal impact
on other classes, changes to methods should have minimal impact on
other methods, and the same principle applies to coarse-grained
structures such as packages (collection of classes in Java) or
microservices.</p>

<h2 id="independence">Independence?</h2>

<p>Many people see the goal of good architecture not as loose coupling
but as complete independence of individual elements. However, that is
impossible. The individual elements form a whole: methods form
classes, classes form packages, packages form microservices, and
microservices ultimately form systems. Components are
connected. Therefore, the objective can only be to ensure that changes
have as little impact as possible — but there will always be changes
that ripple through to other components. Every change has a kind of
<strong>blast radius</strong>, an area affected by that change, and that radius
should be kept as small as possible. This is where dependencies come
into play.</p>

<p class="callout">Every change has <strong>blast radius</strong>, an area affected by that
change. that radius should be kept as small as possible.</p>

<p>Dependencies can take different forms. In object-oriented systems, for
example, there is the <em>uses</em> relationship: one class uses another
class. It holds a reference to instances of that class. In this case,
the class’s interface is known, meaning only methods marked as
<code class="language-plaintext highlighter-rouge">public</code> can be used. There is also the inheritance
relationship. Here, a more specialized class is derived and can access
<code class="language-plaintext highlighter-rouge">protected</code> methods or variables as well. Inheritance therefore
creates a stronger dependency because more of the class can be used,
increasing the number of aspects that may be affected by changes.</p>

<p>This is often described using the concepts of <strong>black boxes</strong> and
<strong>white boxes</strong>. With a black box, only externally visible elements
can be accessed — for example, the interface formed by public
methods. A white box should more accurately be called a <em>transparent
box</em>, because it also allows access to internal structures, such as
protected methods. Compared to a black box, a white box exposes a
larger set of potentially usable methods, making the dependency
stronger. Thus, an inheritance relationship typically leads to
a stronger dependency than a uses relationship.</p>

<h2 id="confusing-dependencies">Confusing Dependencies</h2>

<p>Once you dig a little deeper, the topic of dependencies quickly
becomes confusing, as several examples demonstrate: There is an
interesting
<a href="https://www.pathsensitive.com/2022/09/bet-you-cant-solve-these-9-dependency.html">paper</a>
by Jimmy Koppel and a German <a href="https://software-architektur.tv/2023/09/01/folge179.html">stream /
podcast</a>
that discusses it.</p>

<p>Suppose we build an application that uses Google Analytics. At first
glance, the application is obviously dependent on Google
Analytics. Yet in a certain sense, it is also independent of it. After
all, the application can be built — and more importantly deployed —
without Google Analytics. It merely sends messages over the network to
Google Analytics. Independent build and deployment would not be
possible if Google Analytics were not an Internet service but instead
a part of the application itself, such as a library. Libraries are
required both when building and deploying the application.</p>

<p>However, the application is dependent on Google Analytics in another
sense. It must send messages that Google Analytics understands — and
only Google Analytics understands them. Yet this dependency can be
reduced by introducing an abstraction layer, making it possible to use
a different analytics service.</p>

<p>Would the application then be independent of Google Analytics? Not
entirely. If Google Analytics fails, the application is affected
because it can no longer report metrics. In the case of an analytics
service, this probably would not cause the application itself to fail,
but with other services it might. Conversely, if Google Analytics is
improved, the application benefits. It might become faster if requests
to Google Analytics become faster, or it might provide users with
richer analytics if Google Analytics gains new capabilities.</p>

<h2 id="dependent-or-independent">Dependent or Independent?</h2>

<p>So is the application dependent on Google Analytics or not?</p>

<p>The fundamental problem is that the term <strong>dependency</strong> is not precise
enough. Jimmy Koppel proposes to distinguish three categories of
dependency:</p>

<p class="callout">The fundamental problem is that the term dependency is not precise
enough.</p>

<ul>
  <li>
    <p><strong>Static semantics:</strong> This concerns building the application. In
this regard, the application is independent of Google Analytics
because it can be built and deployed without it.</p>
  </li>
  <li>
    <p><strong>Dynamic semantics:</strong> This concerns runtime behavior. Here, the
application is dependent because if Google Analytics is unavailable
at runtime, metrics can no longer be collected.</p>
  </li>
  <li>
    <p><strong>Program logic or functional requirements:</strong> In this sense, the
application is dependent because improvements to Google Analytics
can directly benefit the application. An abstraction layer also
affects this category, creating greater independence by abstracting
different implementations of the same requirements.</p>
  </li>
</ul>

<p>Thus, in each category, the question is not whether the application is
dependent or independent. Rather, there are different types
of dependency. In terms of functional requirements, new Google
Analytics features may directly influence the application, while at
the same time Google Analytics remains replaceable if an abstraction
layer is introduced and the functional requirements continue to be
satisfied.</p>

<h2 id="even-more-confusion">Even More Confusion</h2>

<p>There are many more examples that illustrate the complexity of
dependencies.</p>

<p>If one application writes a file in a specific format and another
reads that file, there is clearly a dependency. But what depends on
what? Jimmy Koppel suggests one interpretation: serialization and
deserialization depend on each other because the data written during
serialization must be readable during deserialization. Another
interpretation is that both implement a common specification. In that
case, both depend on the specification.</p>

<p>However, this perspective is purely technical. If the concern is
ensuring that the correct and meaningful business data is written,
then the two applications depend directly on each other. The
communication medium is irrelevant. Writing files creates essentially
the same challenges as sending and receiving messages.</p>

<p class="callout">A message bus does not automatically create decoupling.</p>

<p>This is why a message bus does not automatically create
decoupling. Data formats and the semantics of the data must still be
coordinated. In addition, there is now a dependency on the message bus
itself in terms of availability. Components can no longer communicate
if the message bus fails, which can have consequences ranging all the
way to the failure of the entire system.</p>

<h2 id="conclusion">Conclusion</h2>

<p>The concept of <strong>dependency</strong> is central to software architecture, but
unfortunately it is not particularly well defined. Software
architecture typically focuses on dependencies in relation to change,
and concepts such as black-box and white-box dependencies can be
useful in that context.</p>

<p>In reality, however, dependencies have many different dimensions. It
is worth examining the concept carefully precisely because it is so
important.</p>

<p><em>This is a translation of my <a href="https://entwickler.de/reader/reading/java-magazin/7.2025/05f865d3dbb38a8904cdf802">German article at Java
Magazin</a>.</em></p>]]></content><author><name>Eberhard Wolff</name></author><category term="Fundamentals" /><summary type="html"><![CDATA[Software architecture is about managing dependencies, and independence is the goal — or so it seems. In reality, however, it is not even entirely clear what dependencies actually are.]]></summary></entry><entry><title type="html">The Myth of Objective Effort Estimation in Software Development</title><link href="https://ewolff.com/2026/01/22/the-myth-of-objective-effort-estimation-in-software.html" rel="alternate" type="text/html" title="The Myth of Objective Effort Estimation in Software Development" /><published>2026-01-22T00:00:00+00:00</published><updated>2026-01-22T00:00:00+00:00</updated><id>https://ewolff.com/2026/01/22/the-myth-of-objective-effort-estimation-in-software</id><content type="html" xml:base="https://ewolff.com/2026/01/22/the-myth-of-objective-effort-estimation-in-software.html"><![CDATA[<p>An objectively estimate would clarify how long it takes to implement a
feature and how much it costs. This could help e.g. when outsourcing
development services.</p>

<p>In agile methods, the effort of a feature is estimated as a story
during planning meetings to decide whether a story should be
implemented. This is optional: NoEstimates describes projects that
work entirely without effort estimates. The advantage: The effort
spent on estimating is eliminated and can instead be used to develop
stories. It is enough prioritization if everyone on the team works on the
currently most valuable feature. <a href="https://software-architektur.tv/2025/02/24/episode252.html">Woody
Zuill</a> is
a pioneer in this area and has never worked any other way.</p>

<p class="callout">Is it enough prioritization if everyone on the team works on the
currently most valuable feature?</p>

<p>Estimating stories is usually based on a reference story or
functionality that is assigned a size of one. Other stories are
estimated relative to this reference, by and for the team that is
doing the estimating. In addition, there is the team’s velocity
(speed). It is measured in implemented story points per iteration and
can change over the course of a project.</p>

<h2 id="objective-estimates">Objective estimates?</h2>

<p>So the estimate is not objective, because it involves the specific
team and because velocity changes over time. However, the estimation
methodology achieves its goal: it allows teams to plan their
work. Through estimation relative to a reference story and through
velocity, it is also possible to make a reasonably good estimate of
what a team can deliver.</p>

<p>Of course, one can try to make these measurements more objective. For
example, a uniform reference story could be chosen. There are
approaches that go much further. The <a href="https://en.wikipedia.org/wiki/Function_point">Function Point
method</a> attempts to
measure the objective complexity of a business requirement in function
points. But these methods also show variability in estimates. In
addition, they require experience in estimating and are
labor-intensive. Methods such as
<a href="https://en.wikipedia.org/wiki/COCOMO">COCOMO</a> even warn that their
results provide only rough numbers, not precise estimates.</p>

<p>The usefulness of such estimates is questionable: projects always
change their scope. When software is used in practice, new
requirements emerge. Software development is a process in which
engineers and users learn together how best to support business
processes with software. When you learn something new, you change the
scope. Then precise estimates for the original scope are no longer
worth much, and the effort spent on additional precision is not well
invested. Perhaps this is why agile estimation is used in practice,
but more sophisticated methods rarely are.</p>

<p class="callout">When software is used in practice, new requirements emerge.</p>

<p>Overall, the focus on effort in projects is often exaggerated. Like
any investment, an investment in software must generate value that
exceeds the investment — ideally by a large margin. Estimating value,
however, often seems to be treated as a secondary concern compared to
estimating effort.</p>

<h2 id="start-ups">Start-ups</h2>

<p>There are very small teams that have developed extremely successful
software. Such outliers raise the question of whether objective
estimation is even possible. Before its acquisition,
<a href="https://www.theverge.com/2012/4/13/2946785/facebook-instagram-acquisition">Instagram</a>
was valued at one billion dollars, operated a globally scaled system,
and did so with only 13 employees. Before its acquisition,
<a href="https://www.wired.com/2015/09/whatsapp-serves-900-million-users-50-engineers/">WhatsApp</a>
had 900 million users; 50 engineers developed and operated this
system. That is a fraction of the staff involved in many IT projects
at established companies.</p>

<p>These applications appear to be “just” a simple photo-sharing app or a
messaging app. Does that explain the low staffing numbers? Insurance
products or savings plans seem also simple: you pay money and under
certain conditions, you get money back. Search engines are also
simple: there is even off-the-shelf software to index and search
documents.</p>

<p>On a superficial level, everything appears simple. The complexity of
a problem only becomes clear when you look at the details and truly
understand the problem.</p>

<p class="callout">The complexity of a problem only becomes clear when you look at the
details and truly understand the problem.</p>

<p>Regardless of complexity, the economic success of a project is the
most important outcome. In this respect, Instagram and WhatsApp are
truly convincing: they created billions in value. That is almost only
possible for successful start-ups. But for every successful start-up,
there are many that are not successful.</p>

<h2 id="established-companies">Established companies</h2>

<p>Let’s assume an established company were to build a photo-sharing app
or a messaging app. It would build it using its typical mechanisms:
potentially hundreds of developers, sophisticated project management,
and detailed project plans. Thirteen people developing a central
system for a market breakthrough would more likely be perceived as a
risk than great value for the money. Large teams and projects also
bring a lot of <a href="https://www.youtube.com/watch?v=3MP-4UcAYJU">prestige</a>
— for everyone involved, for managers, but also for
engineers. Finally, established companies usually have many software
projects, and the survival of the company rarely depends on any single
one — unlike a start-up. The pressure is therefore objectively lower
at established companies.</p>

<p>There are thus lots of mechanisms that lead to a completely different
level of effort. An established company will develop a software system
using the mechanisms it typically employs and is in a different
competitive situation than a start-up. In a start-up, on the other
hand, the economic conditions are such that something has to go live
as quickly as possible, maybe even at all costs — with corresponding
consequences.</p>

<h2 id="solving-problems-differently">Solving problems differently</h2>

<p>It is also unlikely that an established company would build exactly
the same application for a problem such as photo sharing or
messaging. The IT landscape into which the application must integrate
is different. Not really necessary requirements might be added —
something a start-up often cannot afford due to its economic
constraints.</p>

<p>A start-up would therefore likely develop a much simpler solution even
in areas that are traditionally implemented with complex software
systems — simply because it needs to get to market quickly and has a
very limited budget.</p>

<p>So trying to assess an objective effort makes even less sense:
software solves a problem that an organization has. For example, the
organization wants to establish a new product. To do so, it relies on
the social processes embedded in the organization. For a start-up, the
approach of an established company is just as alien as the approach
of a start-up is to an established company. This includes not only the
development process, but also the product itself.</p>

<p>In the end, the objective effort should not really concern us too
much. What matters is how we can develop better software in a given
situation. And for that, there are always many exciting possibilities.</p>

<h2 id="tldr">tl;dr</h2>

<p>Determining the objective effort for a feature may be possible, but
only with great effort. Since it is unnecessary and costly for
planning, most projects do not use such techniques at all. Software is
also “just” an implementation of the solution to a business
problem. Organizations approach problems differently, so the solution
and the effort will differ for that reason alone. Nevertheless, one
can and should ask questions about
<a href="https://www.youtube.com/watch?v=zLN_apxG8PM">productivity</a> and better
ways of working.</p>

<p><em>This is a translation of my <a href="https://www.heise.de/blog/Warum-objektive-Schaetzungen-in-der-Softwareentwicklung-nicht-funktionieren-11074323.html">German blog post at heise
Developer</a>.</em></p>]]></content><author><name>Eberhard Wolff</name></author><category term="Fundamentals" /><summary type="html"><![CDATA[The estimated effort of a software project usually refers to the specific team involved. But can effort be estimated objectively and independently of the team?]]></summary></entry><entry><title type="html">Cohesion, Modules, and Hierarchies</title><link href="https://ewolff.com/2026/01/08/cohesion-modules-hierachies.html" rel="alternate" type="text/html" title="Cohesion, Modules, and Hierarchies" /><published>2026-01-08T00:00:00+00:00</published><updated>2026-01-08T00:00:00+00:00</updated><id>https://ewolff.com/2026/01/08/cohesion-modules-hierachies</id><content type="html" xml:base="https://ewolff.com/2026/01/08/cohesion-modules-hierachies.html"><![CDATA[<p>Cohesion means something like “togetherness”. The cohesion of a module
is good when the individual parts of the module somehow belong
together. In fact, it is possible to develop good architectural
designs by simply implementing functionalities that intuitively belong
together in a single module such as a microservice.</p>

<p class="callout">Cohesion and loose coupling are therefore related and
mutually dependent.</p>

<p>For example, assume that a specific product can be reserved, then
ordered, and finally delivered. We are talking about a concrete
product — for instance, a specific used laptop that exists only once
in stock with exactly this configuration. The functionalities
ordering, reserving, and delivering are candidates for a shared
implementation in one microservice. They could <strong>share a data model</strong> in
which the state of a product (ordered, reserved, delivered) is
stored. If one of these functionalities is implemented somewhere else,
this model is torn apart. The status would then have to be available
in two places. As a result, the modules would have to interact a lot,
because the product’s state would have to be kept consistent in both
places. In addition, changes might require modifying both places—and
then the two microservices would no longer be loosely
coupled. Cohesion and loose coupling are therefore related and
mutually dependent.</p>

<p class="callout">This way of thinking in terms of models and shared functionality is
central to good modularization.</p>

<p>This way of thinking in terms of models and shared functionality is
central to good modularization. Unfortunately, this does not seem to
be clear to all software developers and architects, so in practice
models that actually belong together are sometimes split apart,
resulting in poor modularizations.</p>

<p>As so often, there are gray areas here. The concrete approach depends
primarily on the specific domain. That ordering, reserving, and
delivering are so closely related that they must share a data model is
a result of the specific domain. If, for example, not a specific
laptop is reserved and ordered, but merely some laptop that meets
certain specifications, the modeling can look different. Then it may
be sufficient to reconcile inventory levels to avoid reserving and
ordering too many laptops with certain specifications. This difference
is a consequence of the business model: for used laptops there may be
only one specific item with a given configuration, while for new
laptops there may be a stock of identical products.</p>

<p>Delivering the laptop means that no other customer can reserve or
order it. But delivery must also is have logic to actually ship the
laptop, which may be better placed in another module. In other words,
the domain concept of “delivery” can be represented in different
models across different modules: one module that models the state of
the product (reserved, delivered …), and another that actually does
the actually shipment process.</p>

<h2 id="cohesion-code-duplication-and-dry">Cohesion, Code Duplication, and DRY</h2>

<p><strong>Code duplication</strong> might be an indicator of weak cohesion. This makes
sense: when code is duplicated, the same or almost the same logic is
implemented in two places, even though it should really exist in only
one place. Something has been “torn apart” that probably belongs
together. <strong>DRY (Don’t Repeat Yourself)</strong> can be a countermeasure. DRY
says code should never be duplicated; instead, code should always be
adapted to be flexible enough to cover multiple cases. For example,
duplication of a class can be avoided through a superclass and
inheritance, or duplication of a method through an additional
parameter or a conditional branch.</p>

<p>But the example of data modeling for laptops is a case where DRY and
avoiding code duplication would not have solved the problem. One can
implement ordering and reserving in two separate microservices and
thus achieve no cohesion, while still adhering to DRY. DRY alone is
therefore not sufficient as a concept for achieving cohesion.</p>

<p class="callout">Code duplication violates cohesion but can create loose coupling.</p>

<p>Interestingly, code duplication violates cohesion but can create loose
coupling: When code is duplicated, the two copies can be evolved
independently. For example, if a specific software solution for one
customer is to be turned into an industry solution, one can copy the
specific solution for each customer and adapt it. This is very easy to
implement and enables completely separate development, making
customer-specific changes easy. The changes are thus completely
decoupled — more than just loosely coupled. On the other hand, copying
code also means that a separate, specific solution emerges for each
customer, making it nearly impossible to implement features for all
customers, because these features would have to be implemented in all
customer-specific code bases.</p>

<p class="callout">Implement a generic core only after the third similar implementation?</p>

<p>It seems obvious that it is better to use a single code base for all
customers — there are reasons why DRY an important concept. But then
one has to design a reusable code base of domain logic. This is not
easy at all, which is why
<a href="https://software-architektur.tv/2023/10/13/episode184.html">some</a>
actually recommend implementing a generic core only after the third
similar implementation. This suggests that up to a certain point,
one benefits more from strict separation and the resulting loose
coupling than from reuse and the tight coupling of a shared code base.</p>

<h2 id="hierarchies">Hierarchies</h2>

<p>In the laptop example, the discussion revolved around functionalities
such as ordering products. With the proposed decomposition, changes to
this functionality would affect only one microservice. But other
criteria for cohesion can also be defined: doesn’t all business logic
code belong together? And the user interface code? When changes are
made to the ordering process, both parts of the system are affected: a
change in business logic can only be used after corresponding changes
to the user interface. Nevertheless, business logic belongs together
and should be separated from user interface code. So there is another
level of modules and cohesion here.</p>

<p class="callout">Modules form a hierarchy.</p>

<p>In fact, modules form a hierarchy: microservices, for example, are
divided into e.g. Java packages that may contain user interface or
business logic. Below that are classes that contain specific parts of
the business logic or user interface, and finally methods. At all
these levels, things that belong together should be implemented
together.</p>

<p class="callout">At a coarse-grained level, decomposition should follow domain concerns.</p>

<p>At a coarse-grained level such as microservices, decomposition should
follow domain concerns; at a fine-grained level, it should follow
technical aspects such as business logic and user interface. Cohesion
applies to all sizes of modules such as microservices, Java packages,
or classes.</p>

<h2 id="size-and-cohesion">Size and Cohesion</h2>

<p>There is a trivial way to always ensure that everything that belongs
together is in one module: just have a single module for the complete
system. For example, one microservice for an entire system, or one
class for a microservice. Obviously, this is a bad idea, because the
systems then become very hard to understand. So size plays a role:
microservices, classes, and methods should be small enough to understand.</p>

<p class="callout">Cohesion also means separating what does not belong together.</p>

<p>So cohesion does not only mean grouping together what belongs
together, but also separating what does not belong together. If
unrelated domain concerns are implemented together in one
microservice, it not only becomes too large, but there are also too
many domain reasons to change it, turning it into a change hotspot.</p>

<h2 id="conclusion">Conclusion</h2>

<p>Cohesion provides a good way to identify modules: if you implement
things that belong together in the same module, you created at good
designs. Like all other rules for designing architectures, also this one
depends on the domain and the specific use case. This makes it
difficult to apply in practice. However, it applies at all levels:
microservices, packages, classes, and even methods. That is why it is
so important to truly understand these concepts and their effects.</p>

<p><em>This is a translation of my <a href="https://entwickler.de/reader/reading/java-magazin/5.2025/c089adccc63144b27b9cfcd0">German article at Java
Magazin</a>.</em></p>]]></content><author><name>Eberhard Wolff</name></author><category term="Fundamentals" /><summary type="html"><![CDATA[Good modules have high cohesion — alongside loose coupling from previous post, this is the most important property of modules. But why is cohesion so important? And how does it influence the design of software systems?]]></summary></entry><entry><title type="html">How Architects Create Real Impact</title><link href="https://ewolff.com/2025/10/20/how-architects-create-real-impact.html" rel="alternate" type="text/html" title="How Architects Create Real Impact" /><published>2025-10-20T00:00:00+00:00</published><updated>2025-10-20T00:00:00+00:00</updated><id>https://ewolff.com/2025/10/20/how-architects-create-real-impact</id><content type="html" xml:base="https://ewolff.com/2025/10/20/how-architects-create-real-impact.html"><![CDATA[<p>First of all: of course, architects need technical expertise. They
must design architectures — and that involves structuring the system
into <strong>modules</strong>, classes, etc., to enable long-term
maintainability. Another aspect of architecture work involves making
<strong>technical decisions</strong> about certain technologies or
approaches. These decisions influence qualities (also known as
non-functional requirements) such as security, performance, or
usability. Making such decisions is at the core of architecture
work. Knowledge of technologies and architectural approaches is
therefore essential. But understanding the specific <strong>requirements</strong>
and desired <strong>qualities</strong> is also necessary to work effectively on the
architecture.</p>

<p>These aspects of architectural work are the focus of most articles,
books, and talks on the subject. However, this article takes a
different angle — because even a perfect architecture is useless if it
is not implemented. Only then can architects make an <strong>impact</strong>. This
aspect is extremely important in practice, yet there is significantly
less material available on the topic.</p>

<h2 id="writing-code-yourself">Writing Code Yourself?</h2>

<p>Architectural decisions affect the code: the code’s structure and the
technologies used within it. So, one might simply sit down at the
computer and implement the architectural decisions directly in
code. That approach obviously works if you’re implementing a project
alone. But most projects are developed by <strong>teams</strong>. You can still
implement your architectural ideas yourself — but that alone isn’t
enough if the other developers implement things that don’t align with
your design. And since there are more of “them” than “you,” your
architectural ideas will never be fully reflected in the code unless
they get on board.</p>

<p class="callout">The real challenge is influencing the other developers.</p>

<p>So, the real challenge is <strong>influencing</strong> the other
developers. Writing code yourself can help with that. It allows you to
better understand the challenges others face, gives you firsthand
experience with at least part of the technical context, and increases
your credibility — since you’re not just talking abstractly about
concepts, but actually applying them. Pair programming with other
developers can also help bring architectural ideas to life within a
project.</p>

<h2 id="communication">Communication!</h2>

<p>At this point, it should be clear what this is really about:
<strong>communicating</strong> ideas and <strong>convincing</strong> developers. Some people
expect there to be a kind of magic that allows them to persuade others
of their views. Even if such magic existed — would you really want to
convince everyone of the superiority of your own architectural ideas?</p>

<p>In software architecture, there are many possible options for every
decision. Depending on the specific context, one or another option
might make sense. Sure, you can debate endlessly about which framework
is better, how APIs should be structured, or which architectural
paradigms to use. But in the end, projects often fail for entirely
different reasons — misunderstood requirements, communication
breakdowns, or organizational challenges are common causes. And the
“correct” architecture always depends on the context. In other words:
many options can work; the differences between them might not be huge,
and the supposedly better option can turn out to be suboptimal. Making
compromises requires being willing to compromise — not clinging to a
single approach in a quasi-religious way.</p>

<p>Therefore, an <strong>open dialogue</strong> is better. Other developers may have
ideas or knowledge that lead to better decisions. It’s not about
persuasion — it’s about dialogue that leads to shared, and ultimately
better, decisions.</p>

<p class="callout">Other developers may have ideas or knowledge that lead to better
decisions.</p>

<p>That requires everyone involved to be willing to engage in such a
dialogue — not just push their own egos. Architects need to <strong>foster
such conversations</strong> and deal constructively with disagreement or
resistance, rather than ignoring it.</p>

<p>What applies to architects also applies to other technical
professionals: at a certain seniority level, they must engage in
discussions with others and share their knowledge and ideas. Only then
can they have an impact beyond their direct personal
involvement. Simply opposing others is not enough.</p>

<h2 id="management">Management?</h2>

<p>At first glance, architects seem to primarily talk to developers and
other technical people. But software projects involve many
stakeholders who define requirements and provide valuable input — so
they must also be included in the dialogue.</p>

<p class="callout">Software projects involve many stakeholders - they must also be
included in the dialogue.</p>

<p>In addition, management and other stakeholders often make decisions
that affect projects. This includes direct <strong>architectural decisions</strong>
— such as which technology to use or whether to adopt an approach like
microservices. Ideally, such decisions should be made by architects,
since they are the experts. Management’s involvement in these
decisions is therefore somewhat contradictory — they are not the
experts and should defer to those who are.</p>

<p>Management also makes decisions about <strong>team setup</strong> and <strong>team
responsibilities</strong>. Since Conway’s Law, we know that organizational
structure affects architecture. Yet such organizational decisions are
often made by management without considering architectural
implications.</p>

<p>In practice, many architects treat these decisions as fixed and
unchangeable. Sure, they are made by people higher up the hierarchy —
but you can still talk to those people and influence their
decisions. Just as architects should engage in <strong>dialogue</strong> with
developers, they should do the same with management.</p>

<p class="callout">Just as architects should engage in dialogue with developers, they
should do the same with management.</p>

<p>Of course, it can be convenient to let others take responsibility for
decisions. But at the very least, architects should provide
feedback — especially when they have unique insight. That feedback helps
improve decisions overall.</p>

<p>Sometimes, decisions need to be “translated”: management may not
understand how organizational structures influence
architecture. Likewise, management doesn’t care about the “beauty” of
code, but they do care about development efficiency. As an architect,
you need to perform this translation if you want your ideas to be
heard.</p>

<h2 id="dimensions-of-effectiveness">Dimensions of Effectiveness</h2>

<p>There are thus two dimensions of impact: based on your knowledge and
skills, you can design better or worse <strong>architectures</strong> — but whether
they are actually implemented depends on how effectively you operate
within the <strong>organization</strong>. Both dimensions must be considered when
making decisions.</p>

<p>It can even make sense to choose the option preferred by the majority,
even if it’s technically inferior to the one you would choose
yourself. That option will likely receive more support. And in many
cases, the different options don’t lead to dramatically better or
worse outcomes. It might even be that your preferred option only seems
better to you but is in fact worse. Software architecture is complex,
and the consequences of decisions are often impossible to fully
predict.</p>

<p>On the other hand, when decisions could have catastrophic
consequences, you must speak up and escalate if necessary. It would be
completely irresponsible to let such decisions go unchallenged. The
first step is not to treat decisions — especially those made by
management—as immutable truths.</p>

<p class="callout">It’s crucial to prioritize which decisions to “push through” or firmly
resist.</p>

<p>Thus, it’s crucial to prioritize which decisions to “push through” or
firmly resist, and which to approach by focusing on communication and
collaboration.</p>

<h2 id="conclusion">Conclusion</h2>

<p>Designing architectures and selecting technologies are prerequisites
for successful architectural work — but they’re not enough. To make an
impact, architects must communicate with developers. The goal isn’t
necessarily to impose one’s own ideas but to engage in dialogue,
question those ideas, and revise them if needed. Writing code can
help, but communication skills are far more important.</p>

<p>The same applies when dealing with management and other
stakeholders—these skills are essential for influence. Without the
ability to create impact, even the best architectural designs will
never be realized. That’s why <a href="https://software-architektur.tv/2024/08/16/episode228.html">communication is so important in
IT</a>. And
there are, of course, <a href="https://software-architektur.tv/tags.html#Kommunikation">many
ways</a> to
improve these skills — for example, through <a href="https://software-architektur.tv/2024/10/04/episode234.html">facilitation
techniques</a>.</p>

<p><em>This is a translation of my <a href="https://entwickler.de/reader/reading/java-magazin/4.2025/2f30e08fa9bd5efa002644f3">German article at Java
Magazin</a>.</em></p>]]></content><author><name>Eberhard Wolff</name></author><category term="Fundamentals" /><summary type="html"><![CDATA[Designing architectures is one thing — but how can you ensure that the architecture you’ve designed is actually implemented? Every architect faces this challenge if they want to make an impact in a project.]]></summary></entry><entry><title type="html">The Problem with Loose Coupling and Why It Is Important</title><link href="https://ewolff.com/2025/09/15/loose-coupling.html" rel="alternate" type="text/html" title="The Problem with Loose Coupling and Why It Is Important" /><published>2025-09-15T00:00:00+00:00</published><updated>2025-09-15T00:00:00+00:00</updated><id>https://ewolff.com/2025/09/15/loose-coupling</id><content type="html" xml:base="https://ewolff.com/2025/09/15/loose-coupling.html"><![CDATA[<p>As so often in software architecture, loose coupling is about
<strong>dependencies</strong>. An architecture divides software into different
parts such as packages, microservices, or classes. Ideally, this
allows you to change and understand just one part instead of the whole
system. There must, of course, be dependencies between these parts
because they are supposed to form a complete system. Therefore, truly
“independent” components are impossible. The question then becomes:
what kinds of dependencies should we aim for?</p>

<p>This is where loose coupling comes in: if changes do not ripple
through to dependent components, we speak of <strong>loose coupling</strong>. The
two components may depend on each other, but a change typically
affects only one component and does not touch its dependents. Some
<strong>changes</strong> may still involve both components, but under loose
coupling, this is the exception.</p>

<p class="callout">If changes do not ripple through to dependent components, we speak of
<strong>loose coupling</strong>.</p>

<p>This property plays a crucial role: it ensures that we can modify
<strong>large systems</strong> by changing just one component while largely
ignoring the rest. Without loose coupling, changes become difficult
because dependencies may cause effects throughout the system. In the
worst case, those effects become unpredictable, and the software is
practically unchangeable because the risk of unintended side effects
is too great.</p>

<h2 id="how-to-achieve-loose-coupling">How to Achieve Loose Coupling</h2>

<p>If loose coupling is so important, the obvious question is how to
achieve it. A key concept is modularization: components expose an
<strong>interface</strong> and hide their <strong>implementation</strong>. For classes, for
example, the interface consists of public methods, while their
implementation and private methods remain hidden. This guarantees that
other classes can only depend on the interface. A change that keeps
the interface stable does not technically propagate to other
classes. Of course, a change in behavior may still force updates to
other classes.</p>

<p class="callout">A key concept is modularization: components expose an <strong>interface</strong>
and hide their <strong>implementation</strong>.</p>

<p>Modularization and loose coupling are also why you should avoid
exposing <strong>instance variables</strong> directly: if you change how data is
modeled in instance variables but those variables are accessed outside
the class, changes will ripple outward, making the coupling less
loose. An even more extreme case is sharing a database directly. At
worst, you may not even know which applications are reading from or
writing to the database. If you want to change the database schema,
all these applications could be affected. This tight coupling is why
database schemas often become practically unchangeable. The remedy is
to hide the data model and allow access only through an interface.</p>

<p>There are many other potential dependency hotspots where changes can
ripple through the system — shared data structures, for example.</p>

<p>So, loose coupling is achieved through <strong>modularization</strong> — the
fundamental concept of software architecture. But in practice, loose
coupling is so critical that we often wish for additional ways to
improve it.</p>

<h2 id="adapter-layers-not-a-good-idea">Adapter Layers: Not a Good Idea!</h2>

<p>Some architectures introduce an extra layer to further decouple
interface and implementation. In this layer, data is copied into
<strong>Data Transfer Objects (DTOs)</strong>, and an adapter with its own
interface is implemented to hide the interface of the underlying
layer. The idea is that changes to the external interface won’t “bleed
through” but can be addressed by updating only the DTO and
adapter. This should in theory create looser coupling since
changes typically affect only this layer.</p>

<p class="callout">The idea is that changes to the external interface won’t “bleed
through” but can be addressed by updating only the DTO and
adapter.</p>

<p>In reality, these adapter layers often get in the way. When a change
does propagate, you have to update the adapter layer in addition to
the implementation. Adding a new feature to the interface always
requires updating the underlying implementation as well as the adapter
layer.</p>

<p>Subjectively, such adapter layers tend to hinder more than they help
because some changes become more cumbersome. The <strong>overhead</strong> rarely
seems justified by the savings in other cases. This situation occurs
whenever changes commonly pass through the adapter layer — which is not
unusual: new features and changes affecting more than just the
interface are frequent, and these always pass through the
adapter. Furthermore, the system now has an extra layer, making it
harder to understand and thus harder to modify.</p>

<p class="callout">Subjectively, such adapter layers tend to hinder more than they help
because some changes become more cumbersome.</p>

<p>This points to another issue with loose coupling: it is easy to look
back and see whether a system was loosely coupled for past
changes. But <strong>designing</strong> a system to remain loosely coupled for
future changes is much harder. Good modularization will likely result
in looser coupling; adapter layers, on the other hand, rarely will.</p>

<p>Therefore, it’s worth examining loose coupling to improve it. An
important input for this is <a href="https://software-architektur.tv/2023/06/07/folge168.html">Behavioral Code
Analysis</a>. It
looks at how a team interacts with and changes the software. This
approach reveals which parts of the code are often changed
together. You can then take steps to restructure the code so that
similar changes in the future affect only a small part of the system,
making them easier to implement.</p>

<p>In general, <strong>iterative architecture evolution</strong> is a good idea. As
the project progresses, you learn which approaches work well or poorly
and can adjust the architecture accordingly. Only through such
adjustments do truly good architectures emerge. If you neglect
iterative improvement, the system’s quality will inevitably decline.</p>

<h2 id="domain-logic">Domain Logic</h2>

<p>Still, it would be nice to design a system upfront to achieve as much
loose coupling as possible. As mentioned, modularization can help — but
it’s also worth considering completely different strategies. Up to
this point, we have mainly looked at technical measures like
modularization or adapter layers. But software is usually changed or
extended because of the domain.</p>

<p class="callout">But software is usually changed or extended because of the
domain. Therefore, you shouldn’t rely solely on technical measures —
you should strive to reflect the <strong>domain concepts</strong> accurately in
code.</p>

<p>Therefore, you shouldn’t rely solely on technical measures — you
should strive to reflect the <strong>domain concepts</strong> accurately in
code. Then, domain-related changes become easy to
implement. Misrepresentations are common here. For example, if a
payment module knows too much about products, it will need to be
updated whenever a new type of product is introduced. That leads to
tight coupling: every change for a new product type affects payment,
in addition to other modules that configure products. But this is also
probably a domain modeling mistake: conceptually, payment should
simply ensure a specific amount of money is received. Why should this
part of the system know which product that amount applies to? That’s a
question about the domain — but it can lead to loose or tight
coupling, especially for critical changes to the domain logic. No
technical measure — neither modularization nor adapter layers — can solve
this problem. On the contrary, an adapter layer could make changes for
a new product even more complicated.</p>

<p>Thus, a sensible separation of domain logic is a key factor
for loose coupling — and this can be addressed during system design, not
just afterward.</p>

<h2 id="conclusion">Conclusion</h2>

<p>Loose coupling is rightly considered an essential property of good
architecture. Only with this property are large systems truly
maintainable, as changes have minimal impact and are therefore lower
risk. To achieve it:</p>

<ul>
  <li>Focus on modularization and domain modeling</li>
  <li>Be skeptic about technical measures that introduce extra code — like
adapter layers</li>
  <li>Examine how your team interacts with the software. This can teach
you valuable lessons about your architecture and help simplify typical
changes.</li>
</ul>

<p><em>This is a translation of my <a href="https://entwickler.de/reader/reading/entwickler.de-blog/3.2025/8ac0fd017dcfe238f917b41d">German article at Java
Magazin</a>.</em></p>]]></content><author><name>Eberhard Wolff</name></author><category term="Fundamentals" /><summary type="html"><![CDATA[Loose coupling is a very well-known concept and is regarded as an important quality of an architecture—perhaps even the most important. But how can loose coupling actually be achieved? And why exactly should an architecture have this property?]]></summary></entry><entry><title type="html">Designing Models Together - Collaborative Modeling</title><link href="https://ewolff.com/2025/08/26/designing-models-together-collaborative-modelling.html" rel="alternate" type="text/html" title="Designing Models Together - Collaborative Modeling" /><published>2025-08-26T00:00:00+00:00</published><updated>2025-08-26T00:00:00+00:00</updated><id>https://ewolff.com/2025/08/26/designing-models-together-collaborative-modelling</id><content type="html" xml:base="https://ewolff.com/2025/08/26/designing-models-together-collaborative-modelling.html"><![CDATA[<p>Software development is, at its core, the creation of models: Code
models reality and business logic. If an insurance premium needs to be
calculated, the code performs this calculation and thus models the
insurance’s business logic. Besides code, there are various other
models. These include formal models such as UML (Unified Modeling
Language), which can be used to model many different domains. UML is
formal because every element has precise, fixed semantics. This has
been leveraged, for example, in generating code from UML. Naturally,
there are also informal models, which we use spontaneously to discuss
software collaboratively. These models are particularly useful for
informal communication.</p>

<p>Ultimately, informal models must at some point be transformed into
formal models. For software to run, at least one formal model must
exist — the code. Like UML, code has a clear semantics, defined by the
programming language used. Creating code is the responsibility of
technical people. Typically, they can also express themselves well with
other formal models, since they can transfer their programming
knowledge to these.</p>

<p>But in order to write code and create other formal models, they need an
understanding of the business logic. That’s where domain experts come
in. They know how the domain is structured and which domain logic must
be implemented. However, they are usually not able to write code, and
other formal models may also be difficult to understand for them.</p>

<h2 id="collaboration">Collaboration</h2>

<p>Therefore, technicians and domain experts must agree on a shared model
to express their understanding of the domain. This allows knowledge to
move from the heads of domain experts into those of technicians, and
eventually into formal models such as code.</p>

<p>In this context, communication becomes the primary focus of
models. Event Storming has established itself as a technique here:
Domain experts write events that happen in the domain on orange
Post-its and arrange them on a timeline, often on a wall. The benefit
of this technique is its low barrier to entry. No complex formal
models need to be understood — simply writing Post-its suffices to
informally express logic.</p>

<p>Other techniques work in a similar way:</p>

<ul>
  <li>
    <p>Specification by Example captures example processes or values from
the domain. This concept is closely related to Behavior-Driven
Design (BDD). One describes what happens in the domain under certain
conditions. These scenarios can be expressed in natural language
while following a defined structure, making them executable as
automated tests. Tools such as <a href="https://cucumber.io/">Cucumber</a>
support this approach.</p>
  </li>
  <li>
    <p>Spreadsheets can also serve as inputs for such modeling. For
example, a spreadsheets might calculate the interest rate for a loan
depending on credit rating, term, and other parameters. Such
spreadsheets can be created with familiar tools like Excel, then
read and parsed by tests to verify the implemented code against
these requirements. This way, domain experts can use tools they
already know to express logic.</p>
  </li>
</ul>

<p>These models are not informal. They are clearly formal enough to be
executed as tests — just like code.</p>

<p>It is not uncommon for domain experts to work with formal
languages. Machine operators program CNC machines. Office workers use
Excel, often with complex macros. BPMN (Business Process Model and
Notation) can also be used by domain experts. Business processes are
modeled in this formal language and thus made executable by
computers. While domain experts may not be able to create BPMN
diagrams themselves, they can often understand them and collaborate
with technical people to create them.</p>

<p>Domain experts can even be provided with DSLs (Domain-Specific
Languages) to express their logic. For instance, a DSL could be
created for modeling tax calculation rules, enabling the development
of systems for tax filing and consulting.</p>

<p>The way collaboration around models is organized defines the
development process: With Event Storming, technical people learn about
the domain logic and then implement it in code. With DSLs, developers
create tools that let domain experts express the logic in code
themselves. This is a sociotechnical perspective: Depending on the
division of work, systems can be implemented in very different ways.</p>

<h2 id="sociotechnical-influences">Sociotechnical Influences</h2>

<p>There is another sociotechnical dimension: These models primarily
facilitate communication. Event Storming, for instance, has many
advantages here. Writing Post-its and sticking them on a wall is
something even introverted people can do. Multiple people can work on
the model in parallel. This enables a different style of collaboration
than a classic meeting, where one person — or only extroverts — are active
while others remain passive. Model quality and shared understanding
benefit greatly from the active involvement of as many participants as
possible. Because these models explicitly foster collaboration, we
speak of collaborative modeling.</p>

<p>Practices from this approach can be carried over into other
meetings. Post-its, for example, can also play a role outside Event
Storming. Of course, there are many other ways to structure group
collaboration so that more people actively contribute their
knowledge. One example is Liberating Structures. Collaborative
modeling is just one concept among many to strengthen collaboration.</p>

<h2 id="social-relationships">Social Relationships</h2>

<p>In such meetings and design sessions, something else becomes visible
beyond the artifacts themselves: How people collaborate in context,
where knowledge silos exist, and what social interactions in this
environment look like. Since collaboration is often at the core of
project challenges, these insights can be very valuable for project
success.</p>

<p>It is especially important to look not just at the artifacts but also
at the process of creating them. Certainly, artifacts contain
knowledge about the domain and thus important information. But perhaps
we overvalue them compared to the insights gained into social
structures, the practice of collaboration and communication, and the
knowledge exchange that happens during artifact creation. For example,
collaborative modeling reveals who understands which part of the logic
or which people enjoy working together.</p>

<p>Thus, models have more than just the dimension of formal
or informal. They can also either support or hinder collaboration and
communication. Event Storming, for instance, enables parallel,
interactive work with minimal effort. Many people can simultaneously
contribute to different parts of the model and discuss their
ideas. Textual models may not be as strong in this regard. But even
with textual models like code, collaboration is possible — for example
through pair programming. Whole groups can even work on code together,
with one person at the keyboard and the others discussing the next
steps — this is called ensemble or mob programming. Different
collaboration models can therefore be implemented with different
artifacts like code, or requirements.</p>

<p>Modeling thus has not only a technical aspect but also helps establish
social systems such as pairs or ensembles. These systems foster
knowledge exchange and joint work. In fact, whether a specific piece
of information is captured on a Post-it may be less important than
knowing who wrote it and who to approach for further details. Perhaps
that conversation already happened during the modeling session, making
further collaboration easier. In that case, the social impact of the
modeling session is crucial. Modeling must therefore be seen as a
sociotechnical activity.</p>

<p>Agile software development has long followed a similar principle: The
goal of a story was never to document every detail but to enable later
conversations about functionality. Collaborative modeling can likewise
be used to establish and prepare communication pathways.</p>

<h2 id="conclusion">Conclusion</h2>

<p>Collaborative work on models — including code — is a central
activity in software development. There are many different models
intended to support developers in coding. But working on models
collaboratively ensures that everyone is involved in shaping
them. This not only improves the quality of the models but also
fosters collaboration. This aspect is what makes collaborative
modeling special, while other approaches tend to focus only on
artifacts and the knowledge captured in them.</p>

<p><em>This is a translation of my <a href="https://entwickler.de/reader/reading/java-magazin/3.2025/6f24108c52d9b4fbb0fe809a">German article at Java
Magazin</a>.</em></p>]]></content><author><name>Eberhard Wolff</name></author><category term="Domain-driven Design" /><category term="Collaborative Modeling" /><summary type="html"><![CDATA[At first glance, designing a software system seems like a purely technical task. In reality, however, it requires collaboration among different roles to build a shared understanding. This involves not only shaping technical artifacts but also social processes.]]></summary></entry><entry><title type="html">Data Is the New Uranium: Dangerous and Hard to Secure</title><link href="https://ewolff.com/2025/07/30/data-new-uranium-dangerous-hard-to-secure.html" rel="alternate" type="text/html" title="Data Is the New Uranium: Dangerous and Hard to Secure" /><published>2025-07-30T00:00:00+00:00</published><updated>2025-07-30T00:00:00+00:00</updated><id>https://ewolff.com/2025/07/30/data-new-uranium-dangerous-hard-to-secure</id><content type="html" xml:base="https://ewolff.com/2025/07/30/data-new-uranium-dangerous-hard-to-secure.html"><![CDATA[<p>At the end of last year, a <a href="https://www.theregister.com/2025/01/06/volkswagen_ev_data_exposed/">data
scandal</a>
involving Volkswagen (VW) made headlines: the VW Group collects and
stores location data from many of its vehicles. Due to a
misconfiguration in Spring Boot, it was possible to generate a heap
dump of an application via a specific link. This application had
access to the cloud storage containing the location data. The heap
dump included the secrets needed to access the data — enabling
attackers to download it.</p>

<p>What can we learn from this? The obvious conclusion is: public-facing
applications must be properly secured. While true, the
<a href="https://media.ccc.de/v/38c3-wir-wissen-wo-dein-auto-steht-volksdaten-von-volkswagen">presentation</a>
at the Chaos Communication Congress taught me something even more
important: knowing a car’s past locations can reveal very sensitive
information. Suppose a car regularly parks at an intelligence agency
like the German BND, then spends nights at a specific residential
location, and occasionally appears at a brothel’s parking lot. That’s
valuable intelligence. It could likely be used to blackmail an easily
identifiable intelligence officer. In total, 800,000 vehicles were
affected in this scandal, and around one terabyte of data was exposed
— plenty of opportunity to extract highly valuable information.</p>

<p>These problems are older than digital computers. The Netherlands built
a population registration system. After invading, the Nazis used that
<a href="https://digital.kenyon.edu/bulmash/1406/">data</a> to deport all Jewish
people.</p>

<h2 id="protecting-data-alone-isnt-enough">Protecting Data Alone Isn’t Enough</h2>

<p>Data that has the potential to blackmail intelligence personnel are so
valuable that intelligence agencies will go to great lengths to
acquire them. And IT systems can’t be fully protected against such
adversaries. A prime example:
<a href="https://en.wikipedia.org/wiki/Stuxnet">Stuxnet</a>, the cyberattack
targeting Iranian gas centrifuges used in uranium enrichment. It
exploited several unknown Windows vulnerabilities (so-called zero-day
exploits). You can’t defend against such attacks — because the
vulnerabilities are unknown, there are no countermeasures. So even
nuclear facility systems are vulnerable, which are probably not
connected to the internet and have tightly controlled physical access.</p>

<p>Even the data of the German parliament hasn’t been safe from <a href="https://cyberlaw.ccdcoe.org/wiki/Bundestag_Hack_(2015)">Russian
hackers</a>.</p>

<p>VW didn’t secure their data properly. But even if they had, it would
only have made access more difficult — not impossible. If an
intelligence agency wants that data, they’ll get it.</p>

<p>And let’s be honest: VW is unlikely to be the only company collecting
such data. Tesla, for example, <a href="https://apnews.com/article/tesla-las-vegas-explosion-cybertruck-elon-musk-789dc864a0c138fd7c36ca8c94b0fbfd">gathers telemetry and video
data</a>. This
data is accessible to a person some regard as the <a href="https://www.wired.com/story/far-right-new-leader-elon-musk/">new hero of the
far-right</a>. Other
manufacturers store data in authoritarian countries — which is hardly
ideal either.</p>

<h2 id="you-cant-fully-protect-data">You Can’t Fully Protect Data</h2>

<p>But let’s assume the data isn’t in bad hands already, and the only
issue is keeping it safe. The example of cryptocurrencies shows how
difficult that is. Anyone with a private crypto key can access the
associated funds — whether they have a legitimate claim or are
stealing them. That’s why such keys must be very well protected. Yet
there’s a <a href="https://www.web3isgoinggreat.com/">website</a> that
continuously reports massive crypto losses — often in the millions,
and once even <a href="https://www.web3isgoinggreat.com/?id=bybit-hack">$1.5
billion</a>. Even when
real money is at stake, data can’t be adequately secured. Intelligence
agencies are active here too: North Korea <a href="https://edition.cnn.com/2025/02/24/politics/north-korean-hackers-crypto-hack/index.html">finances its
dictatorship</a>
and its <a href="https://apnews.com/article/technology-crime-business-hacking-south-korea-967763dc88e422232da54115bb13f4dc">nuclear weapons
program</a>
through crypto theft.</p>

<h2 id="privacy-by-design-and-datensparsamkeit">Privacy by Design and Datensparsamkeit</h2>

<p>So, securing data isn’t the solution. That leaves only one option:
don’t collect or store the data in the first place. This is where
<a href="https://en.wikipedia.org/wiki/Privacy_by_design">privacy by design</a>
and
<a href="https://martinfowler.com/bliki/Datensparsamkeit.html">datensparsamkeit</a>
come in: before storing data, we must ask which features require it
and only store what’s truly necessary. For example, if you want to
locate your car, all you need is its current location. There’s no need
to store historical location data. You could even ping the car only
when requested, retrieve the current location and never store any
data at all. At first glance, it’s unclear why a company would need to
store a car’s entire movement history.</p>

<p>Users could also be asked whether they want certain features enabled
in the first place. An intelligence agency like BND, for example,
might prefer to forego convenience features rather than risk exposing
their agents. Others may choose differently. But if companies never
ask clearly, store the data by default and hide the opt-out, users
are stripped of the ability to make meaningful choices.</p>

<p>Above all, we need to abandon the idea that “data is the new oil.”
That mindset makes stockpiling data for later analysis seem
logical — and leads directly to scandals like VW’s.</p>

<p>We see similar risks elsewhere: do we really want to collect all
Germans’ health data and make it centrally accessible? How valuable is
that data — and <a href="https://www.heise.de/en/news/38C3-Major-security-flaws-uncovered-in-electronic-patient-file-3-0-10221396.html">can we actually protect
it</a>?</p>

<p>There are, however, positive examples: the <a href="https://en.wikipedia.org/wiki/Corona-Warn-App">German
Corona-Warn-App</a>
focused only on contact data and used a decentralized
architecture. Even the German hacker organization Chaos Computer Club
rated it <a href="https://www.tagesschau.de/inland/coronavirus-app-107.html">“very
good”</a>.</p>

<h2 id="so-what-now">So What Now?</h2>

<p>Software developers and architects need to consider the implications
of the data their software collects. Before the VW hack, I hadn’t
realized how valuable such data could be to interested parties. And
that’s despite the fact that <a href="https://www.newsweek.com/fitness-app-strava-reveals-location-secret-military-bases-around-world-793442">smartwatches have already revealed the
locations of military
bases</a>
in the past.</p>

<p>Development teams must always ask themselves: do we need to collect
this data at all?</p>

<p>In the U.S., DOGE (Department of Government Efficiency) has gained
<a href="https://ash.harvard.edu/resources/understanding-doge-and-your-data/">access to large quantities of
data</a>. The
public is reassured with claims that it’s “read-only” access. But this
reveals a stunning naivety about the value of data. DOGE’s <a href="https://arstechnica.com/tech-policy/2025/02/doges-gov-site-lampooned-as-coders-quickly-realize-it-can-be-edited-by-anyone/">own
website</a>
was extremely insecure. Its staff fail to protect <a href="https://www.businessinsider.com/doge-nasa-google-calendar-public-2025-3">even their own
data</a>. So
it is not clear whether the plethora of data DOGE has access to is
actually safe.</p>

<p>Also it is worth asking what happens when stored data becomes publicly
accessible online — or falls into the hands of extremists or an
undemocratic government.</p>

<h2 id="tldr">tl;dr</h2>

<p>Data is only truly secure if it’s never collected or
stored. Development teams should therefore store only the data that is
absolutely necessary.</p>

<p><em>This is a translation of my <a href="https://www.heise.de/blog/Daten-sind-das-neue-Uran-Gefaehrlich-und-schwer-zu-sichern-10318961.html">German blog post at heise
Developer</a>.</em></p>]]></content><author><name>Eberhard Wolff</name></author><category term="Data" /><category term="Security" /><summary type="html"><![CDATA[Software developers and software architects must handle data with great care. Otherwise, they create risks no one can fully assess.]]></summary></entry><entry><title type="html">Bringing Ideas into an Organization - Fearless Change Patterns and the Fearless Journey Game</title><link href="https://ewolff.com/2025/06/05/fearless-change-fearless-journey.html" rel="alternate" type="text/html" title="Bringing Ideas into an Organization - Fearless Change Patterns and the Fearless Journey Game" /><published>2025-06-05T00:00:00+00:00</published><updated>2025-06-05T00:00:00+00:00</updated><id>https://ewolff.com/2025/06/05/fearless-change-fearless-journey</id><content type="html" xml:base="https://ewolff.com/2025/06/05/fearless-change-fearless-journey.html"><![CDATA[<p>If you work in IT projects, sooner or later you’ll realize that many
problems can be solved informally – just like in other areas of
life. If you ask nicely, people will help you. It’s not uncommon for
someone to solve a critical project issue while chatting at the coffee
machine. When things are unclear, people tend to talk directly – in
person, via video call, or on the phone – rather than writing an email
and risking misunderstandings. And a private one-on-one conversation
often helps when someone’s behavior in a large meeting is hard to
understand. As helpful as these informal approaches are, they’re not
enough to systematically introduce an idea into a company – that
requires more than just one successful conversation.</p>

<p>In a <a href="/2025/03/25/whos-in-control-the-hidden-influence-of-software-developers-and-architects.html">previous
post</a>,
we discussed how formal decisions can be undermined. We also know many
ways how this happens. This leads to two consequences:</p>

<ul>
  <li>
    <p>Even when someone makes a formal decision, they depend on the
organization not to sabotage it informally. In other words:
decision-makers can’t work against the organization, even if they
appear to have the authority to make arbitrary choices.</p>
  </li>
  <li>
    <p>Informal mechanisms can also be used not to sabotage, but to support
decisions. People who understand how to use these mechanisms can
effectively drive decisions in the organization – even if they
aren’t formally allowed to make them.</p>
  </li>
</ul>

<h2 id="decisions-ideas-and-architecture">Decisions, Ideas, and Architecture</h2>

<p>For technical staff, understanding how an organization makes decisions
is essential – because software architecture involves decisions about
technologies and code structure. These decisions involve developers,
architects, and management alike. Being able to contribute to these
processes helps increase your own effectiveness. These decisions
especially impact developers and architects, who must live with them
and may later be held accountable for the success or failure of a
project that depended on them. That’s another strong reason to
influence those decisions.</p>

<p>So the ability to moderate or influence decisions is key for technical
staff. It’s not enough to recognize the best option – you have to make
it happen. And informal approaches are especially valuable in that
effort.</p>

<h2 id="fearless-change-structuring-informal-action">Fearless Change: Structuring Informal Action</h2>

<p>What’s needed is a structured approach to using informal mechanisms to
bring ideas into an organization. One-on-one conversations and chats
at the coffee machine are certainly useful, but they’re not a complete
solution.</p>

<p><a href="https://fearlesschangepatterns.com/"><em>Fearless Change</em></a> offers a
collection of patterns for exactly this. It’s based on two books by
Mary Lynn Manns and Linda Rising: <em>“Fearless Change – Patterns for
Introducing Ideas”</em> and <em>“More Fearless Change – Strategies for Making
Your Ideas Happen”</em>.</p>

<p>One such pattern is <strong>“Token”</strong>: “To keep a new idea alive in a
person’s memory, hand out tokens that can be identified with the topic
being introduced.” I believe Linda Rising once used this pattern on
me. Over fifteen years ago, I attended a Fearless Change workshop with
her, where she handed out index cards with the patterns on them. I
still have those cards – sometimes I see them lying around and they
remind me of the patterns. Someone else once gave me wooden chips
labeled <a href="https://boringtechnology.club/">“Innovation Tokens”</a>. I still
remember that idea too. Clearly, the “Token” pattern helps anchor an
idea – and it’s a completely different approach than chatting by the
coffee machine.</p>

<p>Another pattern is <strong>“Study Group”</strong>: “Form a small group of
colleagues who are interested in exploring or continuing to learn
about a specific topic.” This can be useful for introducing techniques
like Domain-driven Design or Microservices. Beyond learning the
content itself, you also form a group with a shared interest – people
who care enough to engage with the idea. That group can be helpful for
future initiatives too.</p>

<p>Following the pattern <strong>“Step by Step,”</strong> a Study Group might be a
first (or second?) step, which could then lead to a prototype
implementation and eventually a <strong>“Hometown Story”</strong> – a story about
an internal success with the idea, which can help spread it to other
teams.</p>

<p>There are also communication patterns:</p>
<ul>
  <li><strong>“Whisper in the General’s Ear”</strong> means convincing a manager in a
confidential setting.</li>
  <li><strong>“Royal Audience”</strong> means organizing a meeting between management
and employees with a well-known figure who embodies the idea and can
promote it.</li>
</ul>

<h2 id="fearless-journey-a-game-for-innovation">Fearless Journey: A Game for Innovation</h2>

<p>You could just buy the books, study the patterns, and create a plan
for applying them to your situation. But there’s a playful
alternative: <a href="https://fearlessjourney.info/">Fearless Journey</a>, a card
game that helps a group plan Fearless Change together.</p>

<p>First, you define a goal – like introducing Domain-driven Design in
your organization. The goal should be a real challenge. Then, players
collect the obstacles and blockers standing in the way. Each player
receives a set of strategy cards from Fearless Change. During the
game, players solve the gathered problems using these strategy
cards. One or more players can play one or several strategies to solve
each problem – unless someone vetoes the solution, arguing the
strategies are unsuitable.</p>

<p>The result of the game is a plan for solving the problems involved in
introducing the idea – using Fearless Change patterns. These patterns
can be used by anyone in the organization. In other words: you don’t
need to rely on management – you can drive change yourself. Of course,
management can also use the game to develop strategies with others.</p>

<p>Even more important is the social outcome: a group has formed and
already started working together on introducing the idea. That group
will likely remain engaged in the initiative. The game fosters
cooperation – everyone solves problems with their strategies, which
emphasizes collaboration. Because problems are only unresolved if
someone vetoes the proposed solution, vetoes are rare – this leads to
a constructive and positive atmosphere. By the end, most problems have
a solution, making it easier to take the next steps. If some problems
remain, that’s also useful – because it makes clear which issues still
need attention.</p>

<p>Politics doesn’t have to be boring – it can be fun and useful.
Interested in the game? You can download it for free or
order it from the <a href="https://fearlessjourney.info/">website</a>, a card
game that helps a group plan Fearless Change together.</p>

<p>First, you define a goal – like introducing Domain-driven Design in
your organization. The goal should be a real challenge. Then, players
collect the obstacles and blockers standing in the way. Each player
receives a set of strategy cards from Fearless Change. During the
game, players solve the gathered problems using these strategy
cards. One or more players can play one or several strategies to solve
each problem – unless someone vetoes the solution, arguing the
strategies are unsuitable.</p>

<p>The result of the game is a plan for solving the problems involved in
introducing the idea – using Fearless Change patterns. These patterns
can be used by anyone in the organization. In other words: you don’t
need to rely on management – you can drive change yourself. Of course,
management can also use the game to develop strategies with others.</p>

<p>Even more important is the social outcome: a group has formed and
already started working together on introducing the idea. That group
will likely remain engaged in the initiative. The game fosters
cooperation – everyone solves problems with their strategies, which
emphasizes collaboration. Because problems are only unresolved if
someone vetoes the proposed solution, vetoes are rare – this leads to
a constructive and positive atmosphere. By the end, most problems have
a solution, making it easier to take the next steps. If some problems
remain, that’s also useful – because it makes clear which issues still
need attention.</p>

<p>Politics doesn’t have to be boring – it can be fun and useful.
Interested in the game? You can download it for free or order it from
the <a href="https://fearlessjourney.info/">website</a>. And you can watch
<a href="https://software-architektur.tv/2024/09/27/episode233.html">Software Architektur im
Stream</a> to
see how Tanja Friedel, Ralf D. Müller, and I play Fearless Journey and
reflect on the experience (only in German, sorry!).</p>

<h2 id="conclusion">Conclusion</h2>

<p>The Fearless Change patterns give technical staff a toolkit to
influence decisions – such as software architecture – through informal
means, without needing formal authority. That can make their work
easier. But it also means that you don’t have to passively accept
decisions just because someone higher up made them. You can – and
should – advocate for your ideas, especially if you believe that bad
decisions could cause serious harm. In that case, technical staff
can’t just hide behind management. They must actively work to
implement better ideas within the organization.</p>

<p><em>This is a translation of my <a href="https://entwickler.de/reader/reading/java-magazin/2.2025/de2d846e2b6dfd9128476b1f">German article at Java
Magazin</a>.</em></p>]]></content><author><name>Eberhard Wolff</name></author><category term="Software Development" /><category term="Sociotechnical" /><category term="Fearless Change" /><category term="Fearless Journey" /><summary type="html"><![CDATA[Software development happens in teams. That means every idea or change affects many people. So everyone involved – including technical staff – must contribute their ideas to the organization. Fearless Change and Fearless Journey offer the tools to do just that.]]></summary></entry></feed>