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 ...

Saturday, June 17, 2017

27000 violations - that, by all means, is a huge ticket ...

In the previous posting, I gave a first set of example rules for the Archichect tool, with the sad result that almost 27000 of the 35500 or so dependencies read in from Archichect.exe were flagged as bad. I want to find out what's wrong here. In other words, we are now looking at the descriptive architecture, or whatever Archichect considers this to be.

If you are curious how to run Archichect on itself, here is a short how-to: Put the rules in some text file, say ArchichectRules.dep (.dep is the standard extension of Archichect rule files). Then, run the following command on the command line:
Archichect.exe -read Archichect.exe -configure CheckDeps { -rule-defaultfile ArchichectRules.dep } -transform CheckDeps -write RuleViolationWriter { -newline } ArchichectViolations.txt
There are nicer ways to do this (putting the commands into an .arch file is one), but I will explain them somewhen later.

After running the command above, ArchichectViolations.txt contains all those 27-and-something-thousand bad dependencies. Let's take a look at the first (I have replaced some trailing information that we are not yet interested in with ellipses ...):
Bad dependency detected by global rule group:
DOTNETTYPE::<>f__AnonymousType0:Archichect;0.0.0.2;:...
  -- ;;1'_usesmember
  -> DOTNETITEM:System.Runtime.CompilerServices:CompilerGeneratedAttribute:...
  (at archichect.exe/.<>f__AnonymousType0)
Oh—the compiler produces things by itself! This is not an architectural artifact, and not even a design artifact, but an internal artifact of the concrete execution infrastructure—we simply do not want to see such things (typically). Before diving deeply into this complicated area, we simply write a catch-all rule:
** ---> System.Runtime.CompilerServices
Rerunning Archichect now gives us 26281 violations—that's about 800 fewer than before. Slow going. What's the next?
Bad dependency detected by global rule group:
DOTNETTYPE::<>f__AnonymousType0:Archichect;0.0.0.2;:...
  -- ;;1'_usesmember
  -> DOTNETITEM:System.Diagnostics:DebuggerDisplayAttribute:mscorlib;4.0.0.0;:.ctor
  (at archichect.exe/.<>f__AnonymousType0)
A use of System.Diagnostics:DebuggerDisplayAttribute should certainly be allowed:
** ---> System.Diagnostics
Come to think of it, we also should allow using many more things from the System libraries, like int and bool and string. At least the whole System namespace should be ok—but not its sub-namespaces: We might want to control whether our software uses System.Windows.Forms, for example. Let's therefore add all of System:
** ---> System
11207 is the new violation count—more than half of the former "violations" were simply uses of the .Net framework. What else is there? Before stumbling over all these problems one by one, let me show here all the namespace rules that capture useful and certainly allowed dependencies and which I therefore allow everywhereNote 1:
// Dependencies allowed everywhere in .Net applications

** ---> System
** ---> System.Collections
** ---> System.Collections.Generic
** ---> System.Collections.Specialized
** ---> System.Diagnostics
** ---> System.Diagnostics.CodeAnalysis:ExcludeFromCodeCoverageAttribute
** ---> System.Globalization
** ---> System.Linq
** ---> System.Runtime.CompilerServices
        // CompilerGeneratedAttribute, ExtensionAttribute,
        // IteratorStateMachineAttribute, RuntimeHelpers
** ---> System.Text
** ---> System.Text.RegularExpressions
** ---> -:<>f__AnonymousType*
        // Anonymous types, e.g. in Linq expressions
** ---> -:<PrivateImplementationDetails>/*
        // the C# compiler emits such things from time to time

** ---> JetBrains.Annotations
        // If you use ReSharper's [NotNull], [CanBeNull]

(**) ---> \1
        // A namespace can use all of itself
        // (but excluding child and parent namespaces)
And instead of copy-pasting these rules everywhere, it makes sense to put them into a file e.g. named StandardRules.dep, and then include this file from other rule files (this is done with a simple + line). The ArchichectRules.dep rule file now looks as follows:
$ DOTNETITEM ---> DOTNETITEM

+ StandardRules.dep

Archichect.Reading      ---> Archichect
Archichect.Transforming ---> Archichect
Archichect.Rendering    ---> Archichect
Running Archichect a last time for this posting, we are still left with 6155 violations, which I leave for the next posting.

But let me add an important, if obvious remark: In the process above, I fell deep into the claws of the descriptive model, i.e., into "what is".
But of course, with any software, there will always be some more prescriptive rules, i.e. "what we want", or at least "what we expect". It makes very much sense to write down such rules early.

Usually, a large part of the software will follow these rules, or rules that are just a little bit different from them: And during such an "architecture reconstruction", the rules, but soon also the software, can change so that the prescriptive and the descriptive models start to converge. But how do we make sure that they do not start to diverge the next day? I'll think a little bit about this in yet another posting.

Note 1: Of course, there are situations where these "minimal dependency rules" need tuning, e.g. for portable libraries. I trust that you can adapt them yourself in such cases.

No comments:

Post a Comment