Where you might want to start reading ...

Is there something wrong with software architecture - or with us?

I am a software architect (one of a few) for a 20-million LOC business software, with currently a few thousand installations, developed and ...

Showing posts with label architectural documentation. Show all posts
Showing posts with label architectural documentation. Show all posts

Sunday, June 18, 2017

Broad or narrow-spectrum prescriptions - that's the question

In my last posting, I have shown that writing precise rules for dependencies requires formulating a standard set of rules that allow using all the self-evident parts of the chosen runtime environment.

However, after we did that for the Archichect tool itself, there were still more than six thousand dependencies that violate the first, simplistic set of rules that I derived from a rough overview. What are the reasons for these violations? Essentially, there are two:
Here, I will concentrate on the first item, and derive an interesting and important question from it. The next posting will then deal with the second topic.

Before I start with the concrete rules, I would like to know how many of the present violations are of the first kind—i.e., referring external classes—, and how many of the second. For this, I simply add a rule
Archichect.** ---? Archichect.**
This rule allows every item inside Archichect to use any other Archichect item. Of course, I must take care to remove this rule from the final rule set afterwards. That's the reason why I chose the "question mark arrow", which will mark all matching dependencies as "questionable"—hopefully, I will remember to remove this rule later (an additional comment might also be helpful ...).

With this rule added, Archichect's output is Writing 1438 violations and 4717 questionable dependencies. Thus, we will deal in this step with around 1400 dependencies—the rest will be tackled in the next posting.

Now, what about these "violations"? Among other things, Archichect can do the following (I will explain the reasons for these features later, maybe much later—right now, assume that these features are just a "given"):
  1. It can extract dependencies from .Net assemblies (".DLLs" and ".EXEs");
  2. it can watch files (e.g. rule files or source files) and automatically rerun a check in a new thread when a file changes;
  3. it can create some architectural diagrams;
  4. of course, it accesses files and directories at many places;
  5. it provides a small web server for accessing output via a browser;
  6. it can write rule violations in an XML format (but right now not JSON, which is an unforgivable oversight; I will repair this soon);
  7. it can load plugins from assemblies for reading, transforming, writing, and more;
  8. it caches items and strings internally for more efficient memory usage.
These functionalities are located in the following classes:
  1. Reading .Net assemblies: Classes in namespace Archichect.Reading.AssemblyReading;
  2. file watching: Class FileWatcher in namespace Archichect;
  3. diagram creation: Classes in namespace Archichect.Rendering.GraphicsRendering;
  4. file and directory access: many classes need this for reading (of dependencies or configurations) and writing (of violations, logs etc.); so I allow it just for all of Archichect.
  5. webserver: Classes in namespace Archichect.WebServing;
  6. XML writing: Class RuleViolationWriter in namespace Archichect.Rendering.TextWriting;
  7. loading plugins: Classes GlobalContext and ItemAndDependencyFactoryList in namespace Archichect;
  8. Caching of items: Classes in namespace Archichect and child namespaces Archichect.Reading and Archichect.Rendering; in turn, the classes implementing the cache use System.Threading.
The following rules capture the intention that these packages are allowed to use certain namespaces from external libraries:
Archichect.Reading.AssemblyReading     ---> Mono.(Cecil.**|Collections.Generic)
        // For reading .Net assemblies, Archichect uses Mono.Cecil,
        // including some general Mono collection classes
Archichect:FileWatcher             
    ---> System.ComponentModel:Component
        // .Net's FileSystemWatcher derives from Component
Archichect:FileWatcher
                ---> System.Threading
Archichect.Rendering.GraphicsRendering ---> System.Drawing.**
Archichect.**                          ---> System.IO
Archichect.WebServing                  ---> System.Net
Archichect.Rendering.TextWriting:RuleViolationWriter.** ---> System.Xml.**
Archichect(.Reading.**|.Rendering.**)? ---> Gibraltar
        // String caching I copied from the web and generalized for arbitrary objects
Gibraltar                              ---> System.Threading
And now—ta-ta-ta-taa: Running Archichect with these rules leaves us with a mere 40 violations, down from the 1438 just minutes ago! It seems I have described an interesting part of Archichect's architecture almost perfectly (but, well, I invented the tool and wrote its code, so it would be fatal if I couldn't accomplish this, wouldn't it?)

What about these last 40? A quick scan of them exposes the following two reasons:
  • A somewhat widespread use of System.Reflection.MemberInfo, where its Name property is accessed. This results from code like ...GetType().Name, which I find totally ok. On the other hand, I do not want to allow use of all of System.Reflection everywhere, therefore I add the specific rule
  • Archichect.** ---> System.Reflection:MemberInfo::get_Name
  • The GlobalContext class uses System.Threading's CancellationTokenSource and CancellationToken as well as System.Reflection's Assembly and AssemblyName classes. I could now add another two rules going from GlobalContext to ... but wait: Am I still doing architecture here, or is this going into nitty-gritty details that concern no one?
That's an interesting question: Should the prescriptive architecture be
  • "narrow", i.e., allow only access from and to items where we positively need this for current features?
  • Or should it be "broad", i.e., allow the use of large swaths of foreign libraries in most of the product so that future extensions and modifications can use them freely?
I have to confess that I have never managed to keep a "narrow regime" up for a long time: Developers feel or actually are constrained to solve the problems they are supposed to solve. It seems to me that dependency rules should mostly "forbid what has to be forbidden", i.e., prevent definitely problematic design decisions. But they should not restrict something just because "there might be a reason against it, if we only think hard enough". Also, developers will find ways to subvert rules that are not common-sense or simply obstructive: So, my advice is, don't be narrow, be broad.

Still, threads and reflection are not something to use lightly. So, I will restrict the use of these two packages to the cancellation and assembly related classes, respectively; but allow that they be used everywhere in Archichect.

Of course, in your environment, there might be hard and fast rules that must be followed no matter what—so much better so that one can define and check precise dependency rules with tools like Archichect (when and if it is completed).

However, these are still lacking for the more than 4000 open "violations" flagged inside Archichect. The next posting is a stepping stone to tackling them.

Thursday, June 1, 2017

Prescriptive, descriptive and experimenal architectures - what you want, what you have, what you like

In the previous posting, I described a frugal model for architectural descriptions; and ended with the question for which purposes it can be used. This should have been followed by examples—but I decided that before that, I want to explain a very important high-level view concerning architectural descriptions.

The software we have at a concrete point of time during its evolution is often not the software we want. That is true for external attributes, i.e. features and non-functional qualities, but it is also true for the many architectural aspects of a software or system. From this alone it follows that we have to deal with two different sets of architectural descriptions:
  • The prescriptive architecture is the set of rules that we want some software to follow. Typical prescriptive constraints are "the GUI models must not access the database directly", "all event handlers must be aynchronous", "there must be no cycles in the dependencies of modules of type X" and the like.
  • The descriptive architecture is the set of constraints that is actually adhered to in the software. These constraints are typically much more muddy than the prescriptive ones—"the generated GUI models do not access that database, but in the startup module, a model directly reads configuration from the database, and for some plugin modules, we actually do not know whether they access the database or not" might be an honest description of some set of dependencies.
In a well-functioning architectural process,
  • the relevant aspects of the prescriptive architecture are known and documented unambiguously;
  • also the corresponding aspects of the descriptive architecture are routinely extracted from the software and documented;
  • and both are compared to detect when the latter diverges from the former at some critical point.
Maybe it should be noted that there are two categories of reasons why the prescriptive and the descriptive architecture might differ: On the one hand, the prescriptive architecture might be stable, but for whatever reasons actual development does not follow it. On the other hand, the prescriptive architecture might change, because architecture-driving requirements change. This should only highlight that it is not necessarily someone's "fault" if the two do not match.

Are we done with the architectural process?

No, we are not: When a critical difference emerges, something has to happen to align them. This might be a change to the prescriptive architecture, or a change to the actual software (which changes the descriptive architecture), or changes to both of them. In many cases, this alignment will be painful. After all, both the reasons why the prescriptive architecture is as it is, and the reasons why the actual software is as it is are profoundly embedded in the requirements and processes and people building the software. In almost all cases I have seen or taken part, planning for the reconciliation of "what we want" and "what we have" was hard and frustrating. Typically, the consequences of such a "re-architecting effort" were, and are, not at all clear for two very important factors:
  • How much would the modification cost? The potentially recursive ripple effects that one change creates could lead to a nightmare of subsequent changes, and that prospect alone often considerably reduces the possibility to get the "funding" for such a change.
  • How much benefit would the modification yield? Aligning the software with some prescriptive architecture may sound great, but there are typically good (but maybe not well-understood) reasons why the software is as it is; and so "following the rules" might actually make the software worse. The same is also true in the opposite direction: Just changing the prescriptive architecture to "what we have" may result in a set of "rules" that is so large and chaotic that following them is practically impossible.
One important reason for these uncertainties is that we mostly approach such alignment tasks with only two tools:
  • "Dive in": This works by direct modification of the software (where the goal is to keep the prescriptive architecture) or the prescriptive documents (when the software, or some aspects of it, should be kept, but the prescriptive architecture should change). For changes in the software, this is typically (and hopefully) done in a feature branch to shield the productive software from modifications whose adverse and potentially fatal consequences are seen only later in the modification enterprise.
    For changes to the prescriptive architecture, the same should be true—which requires versioned handling of architectural documents, including "branching" and "merging". As I understand it, current tools and notations are not well-prepared for this—I would shudder to find out what an automatic merge of two UML diagrams might produce. But maybe I am too feeble here.
    In sum, irrelevant of which side has to change, "dive in" is an expensive undertaking.
  • "Panorama": This approach works using informal knowledge and notations that try to capture only the essential aspects and consequences of modification variants. From these, often shaky grounds, decisions on how to proceed are derived. Typically, these are very conservative, and often limit themselves to "pilot projects" or some "drill-downs" which are supposed to be fed into another loop of the modification process.
    In sum, "panorama" often requires many iterations to get a useful result, and is therefore also arbitrarily expensive.
Both the "dive in" and the "panorama" approaches are valuable tools. However, they seem to work only with changes of a limited size. For larger systems and changes, their "sort-of-quadratic effort" (practically try out a subset of all interaction pairs between any two components) limits their usefulness and possibilities.

Thus, there should be a third possibility, namely to simulate changes on an abstract representation of the software. I think we would like to do something like the following:
If we move all the controllers into a new package, then we could separately unit-test them. Let's do it ...
... Ah, but now we see that some controllers have dependency loops with their models, and others dont. We do not want a rule 'allow loops between models and controllers'; but cleaning up all these loops right now is no option.
... But wait, it seems that the loops are mainly on trivial controllers that do not have service dependencies; whereas the controllers on top of services are typically cycle-clean ... so let's make two groups, the 'simple controllers' and the 'service controllers'.
Ok, we do it ...
... and now there are only two service controllers with loops. But could we carve out their 'looping' code into a simple controller—let's try it ...
... Ok, so we can agree on new rules for controllers: 'Simple controllers must not access services, but can have looping dependencies with their models' and 'Service controllers may access services, but must not have loops with their models'.
etc.etc.
Such an exploration into the possible changes and their consequences obviously needs to be done on a model of the architecture—a model that can be trusted, i.e., mimics the actual software; is simple, i.e. can be intuitively understood by at least the architects; can be efficiently handled by tools; and what not else (I'll have to come back to these properties later, won't I?).
To distinguish this model from the two introduced at the very beginning, I call it the (or rather, an) experimental architecture.

So, in a nice(r?) world, we end up with three architectural models of a software:
  • The prescriptive architecture—what we want (or believe that we want).
  • The descriptive architecture—what we have (for some interesting abstractions).
  • Experimental architectures—where we try what we might want and have.
But don't we have all these right now, you might ask? After all, the first is in some documents and the minds of all architects and developers, the second is "in the software", the third is on flipcharts during discussions about architecture. Of course, that's true: But I would argue that we need to be able to move information—and this will be loads of information; remember the "telephone directory property"!—between these models, and move it reliably and quickly. That's why a common notation and tooling for all three would appear worthwhile, at least to me.

To coin another term, I will call this the "escalator property" of languages for architectural descriptions: Namely that they can be "escalated" from the descriptive (what we have) to the prescriptive (what we should have) to the experimental (what we might have). And just as an escalator can also go downwards, it should be possible to map the results of an architectural experiment easily to a prescriptive architecture, i.e., to an enhanced set of rules for the system under consideration.

Examples are now really what is needed!

Sunday, May 14, 2017

What made xunit testing successful?

The xunit revolution introduced
  • a very simple notation (actually, two notations);
  • a reasonable benefit for every developer;
  • and, later, a culture that extended "mere xunit testing" to various "development philosophies" like TDD, TDD with baby steps, or BDD.
The notations have a set of important properties:
  1. They define a small language of a few important concepts:
    • At the core, only testcases that run in a predefined test harness framework; and—almost unrelated to that framework—assertions;
    • for scalability, testfixtures and setup and teardown of test cases and fixtures.
  2. The building blocks are very small: A single assertion is atomic; a single testcase can also be made atomic (i.e. just test a very tiny segment of the intended behavior).
  3. There is a simple tool that efficiently does the mundane job of collecting and executing all notated items (test fixtures and test cases).
  4. The tool can be easily run by any developer at any time.
  5. The tool can also be easily integrated into existing automated build processes.
  6. And, finally, the automatic execution can have a drastic feedback on the processes: Tests that do not pass halt the delivery process (by resulting in a "red" build).
The direct benefit for the developer is not that more quality assurance can be done during code development—even though later "xunit philosophies" are, one could argue, roughly founded on this belief (and delivered arguably better processes for direct support of development). On the contrary, more quality assurance (in the sense of "trying to find destructive input to check a program against the limits of a specification") during development would actually be an annoyance, because it disrupts the developer's constructive thought processes necessary for constructing code.

Rather, xunit testing helps to solve the problem of "later regression checks" occurring after code changes, when it is necessary to remember and run the simple as well as the tricky test cases that actually allow a developer (or a team) to hold the belief that the modified piece of code still behaves sanely.

The important experience is that that "later" is not only "much later", when a feature upgrade or bug fix requires changing the code, but that it can be right after the next (well or not so well thought out) modification during the initial development of some piece of code. That really helps developers.

Finally, xunit testing is open in multiple ways—how many tests one writes, how much behavior each one ascertains, when they are run in the development cycle, and when in the build cycle, and, last but not least, how writing and executing of xunit tests feeds back into design and code development. Because all this is not enforced by the tooling in any way, a host of "philosophies" could emerge on top of xunit testing, leading to a lively and sometimes heated debate with a huge effect on wide understanding and on "marketing" of xunit testing.

Great.

Could the same be accomplished for some parts of "architecting"?

We should try, at least, shouldn't we?

So, you and I and everyone should start to invent notations and tools for "architecting" along the lines of what made unit testing successful. I'll leave your ideas to you; in the next posting, I'll start to present mine.

Thursday, May 4, 2017

Two common, and defective, approaches in software architecture

Let me deviate—or actually, approach my target from a different angle—for two more postings before presenting one such "mundane notation" for software architecture documentation (which I have promised in my last posting).

What are the main problems with current (explicit) approaches to software architecture? Very briefly, they might be dubbed
  • the "waterfall approach"; and
  • the "moralistic approach".
The first one, "waterfall thinking", is the old idea that one "starts" with deciding on basic and important architectural aspects, and "then" goes on to design and write software accordingly. Some parts of software engineering might follow this pattern, but there are at least two major scenarios—or maybe forces—that lead to a different process:
  • One is the fact that in almost all cases, a huge software is already in place; and the architectural problem is to modify this software "from inside out". This can and is often be done by small exploratory "experiments" in the software that prove or disprove whether some concept might be worthwhile. And in many cases, this is done implicitly and "under the hood", when some developer starts, on his or her own initiative, to introduce the first RESTful service, a "small NoSQL database on the side", or reuses some executable for production purposes that originally started out as a tool for developers only.
  • The second scenario is brought on us by the typically vast capabilities of commercial and open-source frameworks or tools. When you buy SQL server instead of using Postgres (maybe for external reasons, like having a partner status with Microsoft); or when you take Angular instead of some lesser-known JS framework because some graphics library ties in better with it, you also "buy into" a huge feature set that comes with that tool. Your architectural possibilities are suddenly, and at the same time, extended by the tool's many for-free features, and also limited by the grand architectural and technological lines of it. And like your software, such a toolset is often "just there", without any possibility or even wish to ponder any underlying architectural requirements and decisions.
It is by no means clear that bottom-up approaches, as done by the hypothetical developer above, aren't on par or even better than processes that proceed from "grand architectural analyses" "down" to design and implementation. And, in real life, such bottom-up situations are unavoidable anyway. Thus, "waterfall thinking", while certainly an option, should not be the only and preconceived approach to architectural decisions.

The second problem is the "moralistic approach" to architecture (and design). Architecture and design decisions produce rules: "In our system, code on the GUI layer must access the database via an intermediate DAO layer"—or the other way round; "plugin registration happens explicitly by adding an entry to the configuration, and not implicitly by merely placing the plugin at some location"—or not. And somehow, such rules must be enforced. Most of the time, there are only two enforcement regimes in place:
  • One is the "build-and-install-regime": The build and, later, installation processes of a software require that certain rules are followed. These rules are often implicit, but at least it is hard to violate them. It is also often very hard to change them.
  • The other regime is the "moralistic one"—"you should", or "you must": Without support from tools, it is assumed that developers have the capabilities to follow the current rules. When, later, some disaster happens, one can more or less easily find a person that is the "culprit": "You shouldn't have added that trigger that implicitly calls itself and then fills up the audit table!", "You should not have hardcoded that connection string, but taken it from that (faraway) configuration file to keep database accesses consistent!" But of course, people will only adhere to some sub-100% percentage of rules—and this assumes that the rules are explicitly documented and consistent to begin with. And also of course, culprit-finding does not solve problems well (it might, in some cases, prevent others from violating the same rules in the near future). And finally, we are all versed in putting the fault on the shoulders of the ultimate culprit: "This has grown historically."
Both the "waterfall approach" and the "moralistic approach" are wrong in their fundamentals. But just by saying so, there is no positive alternative in place that replaces them. And, to tread somewhat more carefully, one should certainly not throw out top-down approaches (of which "waterfall" is a special case) and rules-of-thumb (an essentially "human-compatible" method for solving problems, just like "moral") from the portfolio of process building blocks for "doing software architecture": These are worthwhile at the, well, right places.

But some alternative view on "doing it" should be possible.

Wednesday, May 3, 2017

Purposes of architectural documentation disentangled

I have been a little unfair in my last posting: The eight pages on UML 2.0 in Gorton's "Essential Software Architecture" are more than a mere advertisement for that (then) new UML version 2.0—they do actually contain some core advice about how to document architectural aspects of a program. I'll try to extract a compact view of what architecture documentation is, in Gorton's and, I think, the mainstream architecture's textbooks' view, from these pages and the case study in chapter 7.

First of all, architecture documentation is a collection of artifacts for human beings only. This is in contrast to code, which is targeted both at the "machine" and at human readers. In the background, there looms the idea of model-driven architecture, where an architecture model is used to create code—essentially, a compiler for a new language on some "higher" level than standard programming languages. However, like the book, I will disregard this aspect right now and return to it somewhat later.

The clear target of providing information to humans has lead most of us to the use of informal diagrams and standard prose to describe the architectural aspects of a software—"simple box-and-arrow diagrams", as Gorton calls them. He claims that there is "an appropriate diagram key to give a clear meaning to the notation used" in his examples, but most diagrams in his chapters 1 to 5 don't have such a key, and in any case, most people drawing such diagrams don't include one. The problem with this is that any plan to derive hard facts from such diagrams is then doomed.

Now, one purpose of architecture documentation is to give someone a "feeling of the interplay of things", and for this purpose, informal diagrams with textual or oral explanations are perfectly fine and, I am quite sure, even preferable: They appeal to our intuitive approach to most problems, which includes working with somewhat unclear terms and their relations in order to limit thinking about tricky consequences, so that our mind is free to "suck in the universe" of the problem area at hand.

Maybe it should be noted that formal clarity, precise meaning and even "simple" (mathematical) consistency entail, in almost all cases, "hard thought work", as the history of mathematics has shown:
  • Geometry in the plane seems like an easy subject, until you start trying to understand its base and algorithms from Euclid's axioms and definitions, well over 2300 years old: There is nothing easy with concepts like parallels or ratios of line segment lengths! And later formalizations, mainly from about the 1800s onwards, are even more intricate.
  • The other, apparently so "simple" basis of mathematics, namely the natural numbers, lost its simplicity also in ancient times with some prime number theory by the Greeks. It was and is by no means obvious what can emerge from simple addition and multiplication, let alone from the algebraic structures and formalizations extracted in the 19th century, leading to Gödel's mind-bending encodings and Turing's work.
Let me state this in my "Axiom 1": Mathematics, by and large, is not what we want in software documentation (and that from me, who majored in theoretical computer science ...).

Still, it seems we all want something more than the informal box-and-arrow-diagrams.

Gorton, like many others, proposes the use of UML. I cannot help the feeling that he is not really happy about it. The summary of chapter 6 has the following two sentences:
I’m a bit of a supporter of using UML-based notations and tools for producing architecture documentation. The UML, especially with version 2.0, makes it pretty straightforward to document various structural and behavioral views of a design.
"A bit of a supporter", "pretty straightforward": This does not really sound like wholehearted endorsement.

So, what is the problem?

The problem is, in my humble opinion, that there is no clear picture of what a notation for architectural documentation should do. The described use-cases typically oscillate between a "better notation" for those informal, easily comprehensible overviews over some aspects of a software system, and a more formal notation that can help derive hard knowledge about a system, with that implied goal of "generating code" in model-driven approaches.

I am, after many years in the field, now certain that we have to structure the use cases for architectural documentation in a threefold classification, with different notations for each area:
  1. Informal documentation, from which humans can learn easily and intuitively gather a common understanding and a useful overview about some aspects of the system. In the best case, such a documentation is part of a common culture about "how we name and see things." However, this documentation is not intended to derive any hard facts: Everything shown can be disputed and discussed and viewed differently, and the notation can be extended at will if it helps with that intuitive understanding. All must agree that formal arguments based on such documentation are futile and hence must be avoided.
  2. Formally sound and precise documentation that can be used to derive invariants and definitive properties of the documented system. If such documentation is used as the basis for a tool-supported model-driven approach, then there is no difference between a descriptive and a prescriptive architectural documentation for the aspects covered by the process. However, such an approach is very expensive in more than one respect:
    • First, especially without full tool support, keeping such a documentation in line with the system is much work, as even tiny changes on one or both sides require precise updates.
    • Second, as software can exhibit very complex behavior, the notation must be capable of describing many and, usually, deep concepts, which makes it hard and "mathematical" to understand and even harder to write. Such documentation therefore blatantly contradicts "Axiom 1".
    • Last, on a conceptual level, it is not really clear that such a documentation is actually "documentation" in the sense of "humanly accessible information relevant for many decisions in the software life-cycle". Rather, it might be more of a formal specification or even—when used in a model-driven process with code generation—part of the implementation, albeit (maybe) on some higher or "more compact" level than standard programming languages.
Thus, rich informal and deep formal notations are not sufficient for documenting and arguing about architectural aspects of a software.
  1. Therefore, we need notations that are somewhere in-between: Not informal, so that they can be used to derive and ensure hard facts. But equally, they must be easily usable so that they can be read and written by the average software engineer under average project circumstances. It should be obvious that this type of notation cannot be very rich and also not very abstract. Only then, it can on the one hand avoid requiring an extensive semantics for formal derivations, and on the other hand being too esoteric to be used for understandable documents. In other words, it must be a quite mundane notation. I'll show my preferred notation for this, and its uses, in later postings—just in case you think that this looks a little like the search for the holy grail.
UML, incidentally and unfortunately, does not work really well for any of these purposes if its complex semantics is taken seriously:
  1. For an informal notation, it carries a too heavy backpack of that formal semantics which no-one wants to remember when drawing informative diagrams in a running text (as, e.g., in the case study in Gorton's book).
  2. For a formal notation, it is too indirect: One needs to map UML propositions back to the underlying semantic model (like Petri nets or state machines), and only then one can formally draw conclusions; as far as I can oversee it, the number of publications that use UML as a formal base has declined quite a bit over the last years.
  3. Finally, as a simple but yet strict notation, UML is much too baroque, because it was lobbied to include every useful diagram and icon. This large notational size would recommend it for many different informal diagrams—if it weren't for that formal semantics ballast ...
But even if  you think that UML does work well (or well enough) for one area, there is the danger of misinterpreting UML diagrams: Is a diagram which your team uses as a basis for a decision a "type 1." diagram?—then it conveys informal concepts, but does not limit the decision strictly or formally. A "type 2." or "type 3." diagram, on the other hand, would narrowly limit some choices you can make—and definitely require a formally (for "type 2.") or at least collectively (for "type 3.") approved update of the diagram for any change in the software or the architecture. But most diagrams do not spell out explicitly their "conformance level".

Nonetheless, our analysts and some of our developers and architects (including me) are happy enough to use UML as a pool of symbols for sketching explanatory diagrams that help us to keep our complex machinery at least somewhat documented. So yes, I am, and we are also "a bit of a supporter of using UML-based notations and tools", as Ian Gorton puts it.

But now, I feel, I am starting to owe you an explanation how to do architectural documentation better. The next posting ... well, after I wrote it, it turned out to still consider some general observations about software architecture and how we deal with it.

Tuesday, April 25, 2017

How I did not (yet) learn how to write architectural documentation


As I promised in the last posting, here are some thoughts on architectural documentation. They crept out of my mind while I read chapters 6 and 7 of Ian Gorton's "Essential Software Architecture". For such a central topic as the documentation of an architecture, the chapters are astonishingly short. To be fair, the book is not only about the documentation of software architecture, but about all of architecture. Yet, we would expect a solid foundation of how to arrive at a good documentation. Do we get this?

Essentially, chapter 6 of the book is structured like this:
  • One and a half pages of introduction
  • One page about what to document
  • Eight pages of introduction to UML 2.0
  • Half a page on having an "Architecture Documentation Template", and a extremely high-level example of one
  • Finally, a one-page summary
As I and all my architects and all our analysts know UML 2.0, what remains are four pages of information.

Let's try to dissect the information in them:
  1. The first two paragraphs tell the true story that both feeble and massive documentation can be, and "sometimes"(?) is, "out-of-date, inappropriate and not very useful."
  2. "But there are many good reasons why we want to document our architectures, for example" so that others, "most commonly members of the design and development team" "can understand and evaluate the design"; "we [whoever we are, in contrast to the development team] can understand the design" later; others can "learn from the architecture by digesting the thinking behind the design" [I do not understand this; isn't that "evaluating the design"?]; "we can do analysis on the design, perhaps to assess its likely performance, or to generate standard metrics like coupling and cohesion."
  3. But it's hard to document, and the predominant tools are Word, and Visio and PowerPoint ("along with their non-Microsoft equivalents"), and the notation is "informal 'block and arrow' diagrams". "We should be able to do better."
  4. The second section, "What to Document", starts with an example that shows, in my opinion, a huge misunderstanding of what software architecture is:
    A two-tier client server application with complex business logic may actually be quite simple architecturally. It might require no more than an overall “marketeture” diagram describing the main components, and a perhaps a structural view of the major components (maybe it uses a model-view-controller architecture) and a description of the database schema, no doubt generated automatically by database tools. This level of documentation is quick to produce and routine to describe.
  5. I'll show, a few postings down my ramblings, that all the standard books and texts on software architecture notation were, up to now, not able to provide and explain a notation that can even capture this "simple architecture". At the moment, let's just take it that some high-level diagrams seem to be the goal of an archtectural documentation. Why? Because we are told so?
  6. However, the section tries to sum up the reasons for more extensive documentation: Complexity ["in the eye of the beholder"? measured somehow? agreed by a team based on gut feeling?]; longevity [again: how evaluated?]; needs of stakeholders. However, this still does not tell us "what" to document; only that it must be "more".
  7. Final sentence: "It’s therefore important to think carefully about what documentation is going to be most useful within the project context". I thought and think carefully, and three of us discuss—sometimes more heated than carefully—what documentation is going to be the most useful. We are not very successful at this, I have to admit.
  8. The short template, after the UML 2.0 section gets its meat from the case study in chapter 7—an interesting approach dating back to the Babylonians (teach by example, not by abstraction) which, for me, does not really work, because the key point of learning, namely how to apply it in one's own environment, is lacking.
  9. The summary again tries to morally uplift us:
    Generating architecture documentation is nearly always a good idea. The trick is to spend just enough effort to produce only documentation that will be useful for the project’s various stakeholders. This takes some upfront planning and thinking. Once a documentation plan is established, team members should commit to keeping the documentation reasonably current, accurate and accessible.
    I remain sceptical and am unable to heed this advice in our team, I have to admit.
  10. Finally, there is a summary with two main ideas: First, and I agree wholeheartedly for—right now—lack of something better, it favors UML 2.0 as the notation to use, for sketches, for "closely" modelling components and objects, and for "exact" modelling for model-driven development.
  11. The other idea in the summary is to have a repository for documentation, with some sort of "automatic documentation production", for whatever purpose and with an unknown functionality.
10 items of information about how to document a software architecture. I do not want to dispute most of them. I have indicated above where I think the text is wrong (and I hope to explain this in more detailed later postings). But here, I want to find out how to proceed with our documentation: And for this, I have not gotten many clues. Why? See my next posting.

Sunday, April 23, 2017

Is there something wrong with software architecture - or with us?

I am a software architect (one of a few) for a 20-million LOC business software, with currently a few thousand installations, developed and delivered in a Scrum process with monthly iterations, a development team of more than 40 developers, analysts, and testers. The software is in a reasonable state—we have no problems delivering new features, we have no problems integrating new team members, we envision major overhauls of the software (ranging from virtualization to running the hitherto GUI-driven application server as a SaaS system and integrating more DWH functionality into a database designed for a pure—and partly time-critical—OLTP load).

We do not have a valid software architecture documentation.

We have, I think, a solid software architecture: We are able to immerse new team members into our culture in a way that keeps our main architectural constraints in place—layers, tiers, synchronous and async calls, normalized and denormalized data, communication with central systems about licences and failures. The performance, the usability, the stability of the whole system has never been a fundamental problem: Standard design and implementation activities have kept everything in place more or less nicely: Adding covering indexes to critical database tables, rewriting small as well as large chunks of business objects—and their underlying database structures—or swapping out complete workflows with their GUIs, even when they were referenced from other parts of the system, was "work to be done", never a "crisis".

But we do not have a valid software architecture documentation.

And we are not happy about it, because we would like to base some upcoming decisions on both
  • what actually goes on in our software (might it be that the application server actually calls, at some obscure place, some client-side software directly? or can we rely on the fact that this does not happen?)—what is commonly called the descriptive architecture
  • and on our rules of our software architecture (did we actually agree, 10 years ago, that we will forever only access a single database behind the application server?)—what is sometimes called the prescriptive architecture.
At one time, we had a project Wiki. It worked somewhat nicely for maybe 3 or 4 year of our 12 year product history, then one of the main contributors left the company, and the other one—me—got more productive, i.e., wrote more code. The Wiki, which was designed in a somewhat hierarchical fashion (it always had a large catalogue of "TBD" documents), was more and more referenced as "don't read it, because it's uncontrollably wrong as well as right". We tried to revive it half a year ago, with let's say small success, even though we still remain hopeful—but this is a separate story.

What should we do?

I started reading about software architecture documentation, and software architecture in general. And I found the problem. But the solution will be hard, and most probably require serious out-of-the-box thinking. I will tell you about the problem in the next postings.

Let me start with what I learnt from "the books"—two of them, actually, and a host of articles I found on the web.

The first book I started to read was "Essential Software Architecture" by Ian Gorton, which was published in 2006. It is a good book—read it (it is somewhere on the web, whether legally or not I do not know), and take everything away with you that you didn't know, or saw differently before.
  • I agree with everything it says in chapters 1 to 3.
  • Chapter 4, a guide to middleware technologies, is already outdated—not so much because the described technologies have evolved so much, but rather because they have been superseded, mostly by the cloud and the REST revolution.
  • With chapter 5, I disagree: This is a disguised waterfall process. Unfortunately, this is still the mainstream view in the software architecture literature, sometimes a little beautified by talk about agile cycles, but without real depth. Writers in the field will probably disagree. I will make my case in later postings.
  • Then comes chapter 6: "Documenting a Software Architecture". This would be it. But it wasn't. And then there is a "case study" in chapter 7: Interesting—we should expect that if we do as is done there, we would be perfect, or at least very good. I'll look at this shortly—but let me go quickly over the rest of the chapters, to find whether there is something interesting in there.
  • With Chapter 8, a second part is introduced which collects contributions for various topics from different authors. From my (probably limited) perspective, they are not relevant: Product lines in chapter 9 may be interesting, but are, in my humble opinion, just a special case of restructuring software in the long run. Aspect orientation, in chapter 10, has not fulfilled its promises—or it has been integrated in all sort of frameworks so seamlessly that we didn't notice that it has already arrived; anyway, it is, as of today, not relevant as a major architectural concept. Model driven architecture, in chapter 11, is equally absent from industrial practice. SOA, in chapter 12, is in some sense here: With web services, in the cloud, and then with micro-services, we have services everywhere. The "web service standards" shown on p.226, on the other hand, seem to lose ground: XML (still strong) declines against JSON; messaging and especially reliable messaging is nowhere seen, as are (distributed) transactions; on metadata, I do not dare say anything; but WSDL support for typical B2B services is sketchy at best. The semantic web, from chapter 13, has not arrived. And also chapter 14's software agents are a niche architectural style, at least in all IT systems I know.
  • The fascinating future of chapter 15, at least, has arrived: But, when viewed from a decade after the publication, more in the sense that on the one hand, many concrete technologies and the architectural patterns and styles they were based on or promoted, have been defeated by completely new ones, for which the old diagrams do not really work. But on the other hand, this fascinating future has, as far as I can see, not brought much progress in the wide-spread use of distinctly architectural techniques and methods in many many software projects ...
... which brings me back to my original question: Is there something wrong with software architecture—or with us? ... and to chapters 6 and 7 of Gorton's book, about documentation of an architecture, and a case study.

Let me tackle these in my next posting.