In the last post I described in detail how to use the same document to check the defined against the actual architecture. If you tried it out with the example project, you found some dependencies in the wrong direction. And perhaps also in your own projects. This time I’ll show you how to handle this Technical Debt in a way that both the CI build gets green again and it is also documented in the arc42 architecture documentation.

Documenting the unwanted dependencies

To make it visible and clear that there is Technical Debt regarding building block dependencies, it is a good idea to document the unwanted dependencies just below the description of the building blocks and the desired dependencies. I used for that an Asciidoc table like this:

.Unwantend Module Dependencies
[options="header"]
|===
| From                          | To                          | What should be done
| de.kontext_e.demo.business    | de.kontext_e.demo.core      | Because ...; Todo: ...
| de.kontext_e.demo.exporter    | de.kontext_e.demo.facade    | Because ...; Todo: ...
| de.kontext_e.demo.exporter    | de.kontext_e.demo.processor | Because ...; Todo: ...
| de.kontext_e.demo.importer    | de.kontext_e.demo.parser    | Because ...; Todo: ...
|===

and it gets rendered this way:

rendered Technical Debt table

In a real project I found it useful the have a third column to describe why the wrong dependency is currently there and to propose refactorings.

Add Asciidoc documents to the jQAssistant database

The freshly released version 1.2.0 of the Kontext E jQAssistant plugin suite contains also a Plugin for reading Asciidoc documents. Note that this plugin uses the same version of AsciidoctorJ as jQAssistant does to prevent runtime conflicts. So this Plugin works only with 1.2+ versions of jQAssistant because former versions use a too old AsciidoctorJ version.

To use the Asciidoc plugin, add it as jQAssistant dependency:

Maven

1
2
3
4
5
    <dependency>
        <groupId>de.kontext-e.jqassistant.plugin</groupId>
        <artifactId>jqassistant.plugin.asciidoc</artifactId>
        <version>${jqassistant.kePlugins.version}</version>
    </dependency>

Gradle

1
	jqaRt("de.kontext-e.jqassistant.plugin:jqassistant.plugin.asciidoc:1.2.0")

The documentation folder is already part of the pathes to be scanned. If it works, you should now have lots of :Asciidoc labeled nodes in your database.

Add a jQAssistant Concept for the Technical Debt

Next thing is to mark dependencies between Java packages as Technical Debt. So I first find our Asciidoc table which I titled “Unwantend Module Dependencies” and use the body:

MATCH 
    (a:Asciidoc:Table)-[:BODY]->(body)
WHERE 
    a.title='Unwantend Module Dependencies'
WITH 
    body

In the first two columns I put the source and target package names, so select them:

MATCH 
    (c1:Asciidoc:Cell {colnumber: 0})<-[:CONTAINS_CELLS]-(body)-[:CONTAINS_CELLS]->(c2:Asciidoc:Cell {colnumber: 1})
WITH 
    c1, c2

Now I find the matching Java packages:

MATCH
    (m1:Java:Package), (m2:Java:Package)
WHERE
    m1.fqn = c1.text
AND
    m2.fqn = c2.text

and create a relationship between them:

MERGE
    (m1)-[:TECHNICAL_DEBT]->(m2)
RETURN
    m1, m2;

The resulting Cypher query is this:

MATCH 
    (a:Asciidoc:Table)-[:BODY]->(body)
WHERE 
    a.title='Unwantend Module Dependencies'
WITH 
    body
MATCH 
    (c1:Asciidoc:Cell {colnumber: 0})<-[:CONTAINS_CELLS]-(body)-[:CONTAINS_CELLS]->(c2:Asciidoc:Cell {colnumber: 1})
WITH 
    c1, c2
MATCH
    (m1:Java:Package), (m2:Java:Package)
WHERE
    m1.fqn = c1.text
AND
    m2.fqn = c2.text
MERGE
    (m1)-[:TECHNICAL_DEBT]->(m2)
RETURN
    m1, m2;

Now I can put the query into the jQAssistant rules file. As usual I show examples for Asciidoc as well as for XML.

Asciidoc

[[documented:TechnicalDebt]]
.Creates a relationship between two Packages for Technical Debt.
[source,cypher,role=concept]
----
MATCH 
    (a:Asciidoc:Table)-[:BODY]->(body)
WHERE 
    a.title='Unwantend Module Dependencies'
WITH 
    body
MATCH 
    (c1:Asciidoc:Cell {colnumber: 0})<-[:CONTAINS_CELLS]-(body)-[:CONTAINS_CELLS]->(c2:Asciidoc:Cell {colnumber: 1})
WITH 
    c1, c2
MATCH
    (m1:Java:Package), (m2:Java:Package)
WHERE
    m1.fqn = c1.text
AND
    m2.fqn = c2.text
MERGE
    (m1)-[:TECHNICAL_DEBT]->(m2)
RETURN
    m1, m2;
----

XML

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
    <concept id="documented:TechnicalDebt">
        <description>
            Creates a relationship between two Packages for Technical Debt.
        </description>
        <cypher><![CDATA[
            MATCH 
                (a:Asciidoc:Table)-[:BODY]->(body)
            WHERE 
                a.title='Unwantend Module Dependencies'
            WITH 
                body
            MATCH 
                (c1:Asciidoc:Cell {colnumber: 0})
                <-[:CONTAINS_CELLS]-(body)-[:CONTAINS_CELLS]->
                (c2:Asciidoc:Cell {colnumber: 1})
            WITH 
                c1, c2
            MATCH
                (m1:Java:Package), (m2:Java:Package)
            WHERE
                m1.fqn = c1.text
            AND
                m2.fqn = c2.text
            MERGE
                (m1)-[:TECHNICAL_DEBT]->(m2)
            RETURN
                m1, m2;
        ]]></cypher>
    </concept>

Modify the jQAssistant Constraint WrongDirection

I modified the Constraint WrongDirection in a way that it also requires “documented:TechnicalDebt” and that the Cypher query excludes documented Technical Debt dependencies:

Asciidoc

[[dependency:WrongDirection]]
.Finds package dependencies which are in the wrong direction according to the documentation.
[source,cypher,role=constraint,requiresConcepts="dependency:TransitivePackageDependencies, documented:TechnicalDebt",severity=critical]
----
MATCH
    (p1:PlantUml:Package)-[:MAY_DEPEND_ON]->(p2:PlantUml:Package),
    (p3:Java:Package)-[:DEPENDS_ON]->(p4:Java:Package)
WHERE
    p1.fqn = p4.fqn
    AND p2.fqn = p3.fqn
    AND NOT (p3)-[:TECHNICAL_DEBT]->(p4)
RETURN
    p3.fqn + "-->" + p4.fqn AS WrongDirection;
----

XML

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
    <constraint id="dependency:WrongDirection" severity="critical">
        <requiresConcept refId="dependency:Package"/>
        <requiresConcept refId="dependency:TransitivePackageDependencies"/>
        <requiresConcept refId="dependency:WrongDirection"/>
        <description>
            Finds package dependencies which are in the wrong direction 
            according to the documentation.
        </description>
        <cypher><![CDATA[
            MATCH
                (p1:PlantUml:Package)-[:MAY_DEPEND_ON]->(p2:PlantUml:Package),
                (p3:Java:Package)-[:DEPENDS_ON]->(p4:Java:Package)
            WHERE
                p1.fqn = p4.fqn
                AND p2.fqn = p3.fqn
                AND NOT (p3)-[:TECHNICAL_DEBT]->(p4)
            RETURN
                p3.fqn + "-->" + p4.fqn AS WrongDirection;
        ]]></cypher>
    </constraint>

Running the checks again

If you run the checks in the example project again, the four constraint violations should be gone.

Some closing words

If dependencies in a project got never checked, there are most like unwanted dependencies. Not surprising and it also hit me with the C++ project I wrote about Breaking the build with any wrong dependency makes the build red for a long time. Resolving those dependencies is not a five minute task. But documenting them and defining exceptions from the dependency architecture rules with exactly the same table gives us a good chance to improve the architecture step by step over several iterations.

As you may have guessed from the title, more posts about handling Technical Debt are in the pipeline. But before that, I have to introduce you to some other things first.

Some while ago I wrote how we make sure that the documented and the actual architecture match using the idea of Executable Architecture Documentation. This time I want to re-visit this again, but now a little bit more as a tutorial.

An ultra-short recapitulation of the idea

arc42 is a widely used template for architecture documentation. Chapter 5 describes the Building Block View UML Package diagrams can be used to picture the building blocks. There is the current version of the arc42 template on GitHub. Out of the template in Asciidoc several other formats can be generated, but we go with Asciidoc. PlantUML is a Domain Specific Language to describe UML diagrams in textual notation and tooling to make standalone or embedded graphics out of this. jQAssistant scans Java projects into a Neo4j database and can be easily extended by plugins. Kontext E provides a Plugin for reading PlantUML package diagrams.

So the main building blocks described in the architecture documentation can be put into the same database where the actual architecture artifacts are located. Only one simple Cypher query is needed to compare the documented and actual package dependencies.

Add the PlantUML plugin to the jQAssistant configuration

First we need to add the Kontext E PlantUML plugin to jQAssistant in the build file. In the following sections I’ll give examples for Maven and Gradle

Maven

As described in the jQAssistant documentation you add another dependency to the jQA Maven plugin:

1
2
3
4
5
6
7
    <dependencies>
        <dependency>
            <groupId>de.kontext-e.jqassistant.plugin</groupId>
            <artifactId>jqassistant.plugin.plantuml</artifactId>
            <version>1.1.4</version>
        </dependency>
    </dependencies>

Gradle

Given you have a separate configuration for jQAssistant plugins:

1
2
3
    configurations {
        jqaRt
    }

you add the PlantUML plugin this way:

1
	jqaRt("de.kontext-e.jqassistant.plugin:jqassistant.plugin.plantuml:1.1.4")

Put the PlantUML diagram into arc42 Building Block View chapter

It is quite easy to put a UML diagram into an Asciidoc file. For the jQA PlantUML plugin itself it could look like this:

    ["plantuml","MainBuildingBlocks.png","png"]
    -----
    package de.kontext_e.jqassistant.plugin.plantuml.scanner {}
    package de.kontext_e.jqassistant.plugin.plantuml.store {
        package de.kontext_e.jqassistant.plugin.plantuml.store.descriptor{}
    }
    
    de.kontext_e.jqassistant.plugin.plantuml.scanner ---> de.kontext_e.jqassistant.plugin.plantuml.store
    
    -----

It gets rendered into a UML Package Diagram:

UML Package Diagram.

As you noticed, the example is from the jQAssistant PlantUML plugin itself. The rendered Asciidoc example Building Block View chapter looks like this:

Rendered Asciidoc Building Block View

Add the architecture documentation directory to the scan path

To make jQA also scan the architecture documentation, it must be configured to look into the very same directory.

Maven

Given the architecture documentation resides in ‘doc/architecture’, simply add this folder as a scan target like this:

1
2
3
4
5
6
7
    <configuration>
        <scanIncludes>
            <scanInclude>
                <path>doc/architecture</path>
            </scanInclude>
        </scanIncludes>
    </configuration>

Gradle

This is a snippet for scanning a multi-module Gradle project, again with an additional directory to scan for the architecture documentation:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    task(jqascan, type: JavaExec) {
      main = 'com.buschmais.jqassistant.scm.cli.Main'
      classpath = configurations.jqaRt
      args 'scan'
      args '-p'
      args 'jqassistant/jqassistant.properties'
      args '-f'
    
      rootProject.subprojects {
        args 'java:classpath::'+it.name+'/build/classes/main'
        args 'java:classpath::'+it.name+'/build/classes/test'
        args it.name+'/build/reports'
        args it.name+'/src/main'
        args it.name+'/src/test'
      }
    
      args 'doc/architecture'
    }

After scanning the project we get a graph like this:

Graph in Neo4j browser

That’s a screenshot of the Neo4j graph browser which I pimped a little bit to make it more expressive. If you try it out exactly at this point, you will notice that in the original scan there is no dependency from scanner to store in the real architecture. This brings us directly to the following section.

jQAssistant Concepts and Constraints

Now as all information is put into one database, expected and actual state can be matched using jQA Concepts and Constraints. In jQAssistant, Concepts and Constraints can be given in XML and Asciidoc. I’ll give the examples for both of them.

XML

In the style of this XML example we add the following snippets.

First, we need a package level as described here:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
    <concept id="package:PackageLevel">
        <requiresConcept refId="dependency:Package"/>
        <description>
            Set the level property of a package, 
            e.g. 1 for de, 2 for de.kontext_e and so on
        </description>
        <cypher><![CDATA[
			MATCH
				(p:Java:Package)
			WITH
				SPLIT(p.fqn,".") AS splitted, p
			SET
				p.level=SIZE(splitted)
			RETURN
				splitted, SIZE(splitted);
		]]></cypher>
    </concept>

This package levels are used to add some transitive package dependencies:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    <concept id="dependency:TransitivePackageDependencies">
        <requiresConcept refId="package:PackageLevel"/>
        <requiresConcept refId="dependency:Package"/>
        <description>
            Add a DEPENDS_ON relationship to parents of a package P 
            from other packages up to the same level of the source package.
        </description>
        <cypher><![CDATA[
            MATCH
                (p:Java:Package)-[:DEPENDS_ON]->(p2:Java:Package),
                (parent:Java:Package)-[:CONTAINS*]->(p2:Java:Package)
            WHERE
                p.level <= parent.level
            CREATE UNIQUE
                (p)-[:DEPENDS_ON]->(parent)
            RETURN
                p.fqn, parent.fqn;
        ]]></cypher>
    </concept>

which come handy to find package dependencies in the wrong direction with this little Constraint:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    <constraint id="dependency:WrongDirection" severity="critical">
        <requiresConcept refId="dependency:Package"/>
        <requiresConcept refId="dependency:TransitivePackageDependencies"/>
        <description>
            Finds package dependencies which are in the wrong direction 
            according to the documentation.
        </description>
        <cypher><![CDATA[
            MATCH
                (p1:PlantUml:Package)-[:MAY_DEPEND_ON]->(p2:PlantUml:Package),
                (p3:Java:Package)-[:DEPENDS_ON]->(p4:Java:Package)
            WHERE
                p1.fqn = p4.fqn
                AND p2.fqn = p3.fqn
            RETURN
                p3.fqn + "-->" + p4.fqn AS WrongDirection;
        ]]></cypher>
    </constraint>

Don’t forget to add all of that to the default group:

1
2
3
4
5
6
7
    <group id="default">
        <includeConcept refId="package:PackageLevel"/>
        <includeConcept refId="dependency:TransitivePackageDependencies"/>
        <includeConstraint refId="dependency:WrongDirection" 
                           severity="critical"/>
    </group>

That’s it.

Asciidoc

And now in the style of the Asciidoc example:

    [[default]]
    [role=group,includesConstraints="dependency:WrongDirection(critical)"]
    - <<package:PackageLevel>>
    - <<dependency:TransitivePackageDependencies>>
    - <<dependency:WrongDirection>>
    [[package:PackageLevel]]
    .Set the level property of a package, e.g. 1 for de, 2 for de.kontext_e and so on.
    [source,cypher,role=concept,requiresConcepts="dependency:Package"]
    ----
    MATCH
        (p:Java:Package)
    WITH
        SPLIT(p.fqn,".") AS splitted, p
    SET
        p.level=SIZE(splitted)
    RETURN
        splitted, SIZE(splitted);
    ----
    [[dependency:TransitivePackageDependencies]]
    .Add a DEPENDS_ON relationship to parents of a package P from other packages up to the same level of the source package.
    [source,cypher,role=concept,requiresConcepts="package:PackageLevel"]
    ----
    MATCH
        (p:Java:Package)-[:DEPENDS_ON]->(p2:Java:Package),
        (parent:Java:Package)-[:CONTAINS*]->(p2:Java:Package)
    WHERE
        p.level <= parent.level
    CREATE UNIQUE
        (p)-[:DEPENDS_ON]->(parent)
    RETURN
        p.fqn, parent.fqn;
    ----
    [[dependency:WrongDirection]]
    .Finds package dependencies which are in the wrong direction according to the documentation.
    [source,cypher,role=constraint,requiresConcepts="dependency:TransitivePackageDependencies",severity=critical]
    ----
    MATCH
        (p1:PlantUml:Package)-[:MAY_DEPEND_ON]->(p2:PlantUml:Package),
        (p3:Java:Package)-[:DEPENDS_ON]->(p4:Java:Package)
    WHERE
        p1.fqn = p4.fqn
        AND p2.fqn = p3.fqn
    RETURN
        p3.fqn + "-->" + p4.fqn AS WrongDirection;
    ----

Running the check

No special things have to be done to run the actual check. It is done in the normal jQAssistant run. So it works on the local machine as well as in the CI process quite easily.

Making HTML (or PDF) out of Asciidoc

One widely used toolchain is Asciidoctor with Asciidoctor Diagram to generate a nice set of HTML documents. There is also Asciidoctor PDF for a direct generation of PDF.

A complete example

These were a lot of snippets. Therefore I created a very basic example that contains all to make it run but nothing else. You can download the sources on GitHub, explore and play around.

If you do a

1
mvn verify

you’ll find four constraint violations.

Stay tune for follow up posts. They explain why the example is called “Uneven Modules” and what can be done for taming the architecture with jQAssistant.

Some closing words

There are of course many other arc42 chapters. Some of them are not suited to be checked automatically, but others may contain also checkable contents like the Design Decisions chapter. For example rules like ‘no java.util.Date anymore’ or ‘use log4j instead of java.util.logging’ are quite easy to enforce.

The Concepts and Constraints above do not cover two things:

  • packages that were found but not documented
  • packages that were documented but not found

I’m not sure if that is needed and/or useful. Everyone should decide this for the concrete project situation.

So now the CI build creates HTML/PDF architecture documentation and uses the same document to check the defined against the actual architecture. How crazy is this?

In this post you can read that it may surprisingly quick and simple to make use of jQAssistant for verifying module dependencies in C++.

Over the last year I was busy developing some C++ application. As usual it starts nice and clean, gets into production - and evolves. Although I employed analyses tools like Valgrind and CPPCheck I missed something like jQAssistant to check architectural rules. As I updated the architecture documentation again I noticed that the thing I really wanted was to check module dependencies. Sure, there are tools like CppDepend, Sonargraph etc., but I also wanted to take advantage of the Executable Architecture Documentation described in a previous post.

My first attempt was to use the Clang Tools for reading the source code and dump the AST to a file. It turns out that a C++ AST is considerably more complex than one may think in the first place. While I began to write a jQA plugin for that, it may take a while - but I wanted something working now.

My second thought was to dump the CLion PSI tree. But that would not work for CI builds.

But finally I had a nice idea reading the LLSA book: Carola Lilienthal describes that the Sotograph uses regular expressions to determine modules, patterns, layers etc. Because my project structure is very simple - in the src folder are subfolders for each module, containing only files and no submodules - it turns out that analyzing the #include declarations should be sufficient. As the Agile Principle says: “the simplest thing that could possibly work”. The effort for an experiment is very low, so I gave it a try to find out if it could possibly work.

I created a “plaintext” plugin for jQAssistant which does exactly that: import the plain text line by line into the jQA database. jQAssistant comes already with the notion of a “File” and a “Directory”. So I can create a relationship between two files which are connected by an #include with this Cypher statement:

    MATCH
        (x:File:Plaintext), (d:Directory)-->(f:File:Plaintext)-->(l:Line:Plaintext)
    WHERE
        l.text=~'#include.*' and l.text=~'.*../.*' and d.fileName=~'/.*' and l.text=~('#include.*'+x.fileName+'.*')
    MERGE
        (f)-[:DEPENDS_ON]->(x)
    RETURN
        d.fileName, f.fileName, l.text, x.fileName

Next step is to connect the directories where the connected files are located:

    MATCH
        (d1:Directory)-->(a), (d2:Directory)-->(b)
    WHERE
        (a)-[:DEPENDS_ON]->(b) and d1.fileName=~'/.*' and d2.fileName=~'/.*'
    MERGE
        (d1)-[:DEPENDS_ON]->(d2)
    RETURN
        a.fileName, b.fileName, d1.fileName, d2.fileName

But wait: I told you that I organized my source code so that one directory contains one module. So let’s mark the directories as modules:

    MATCH
        (d:Directory)
    WHERE
        d.fileName=~'/.*'
    SET
        d:Module
    RETURN
        d.fileName

Now it is simple to find dependencies

    MATCH
        (d1:Cpp:Module)-[:DEPENDS_ON]->(d2:Cpp:Module)
    RETURN
        d1.fileName, d2.fileName
    ORDER BY
        d1.fileName

or direct cycles

    MATCH
        (d1:Cpp:Module)-[:DEPENDS_ON]->(d2:Cpp:Module)
    WHERE
        (d2)-[:DEPENDS_ON]->(d1)
    RETURN
        d1.fileName, d2.fileName
    ORDER BY
        d1.fileName

This works indeed astonishingly well for my purpose. The effort was really very low because jQA brings a nice plugin concept and Neo4j Cypher supports the regular expressions. I gained interesting insights into how the architecture developed and what unintended dependencies I created while adding more features. Now it’s time to pay back some Technical Debt…

In her exciting talk Applying Java 8 Idioms to Existing Code, Trisha Gee states that demoing the refactoring to returning java.util.Optional could be a mess because so much caller statements have to be changed. You may find yourself easily in a similar situation every now and then if you have some spare time in the project and think by yourself: “Would’nt it be nice to do a quick refactoring now?” If you decide to return an Optional and get 538 compiler errors because of a wrong return type you can forget about ‘quick’. So how do you find a nice spot fitting in your refactoring time box?

It is just one simple Neo4j Cypher query if you are already using jQAssistant in your project. Let’s suppose you want to know how many callers are of method located in the de.kontext_e.techblog.service package. Here is the query:

    MATCH 
        (caller:Method:Java)-[:INVOKES]->(callee:Method:Java)<-[:DECLARES]-(t:Type) 
    WHERE 
        t.fqn=~'de.kontext_e.techblog.service.*' 
    RETURN 
        t.fqn, callee.name, count(caller) AS callers
    ORDER BY 
        callers

That’s it. Now you can assess the impact and choose wisely.

In her German book “Langlebige Software-Architekturen” Carola Lilienthal tells the story of an architect who wants to know which public methods are currently not called from outside the package (p. 117).

Sure, with a powerful tool like Sotograph she is using this is no problem - if you have the money. But can you achieve this also with Open Source tools? Having read some of my previous posts (especially the ones about jQAssistant) you already know the answer: yes, of course! I’ll show you how easy that is.

For this post, I created a little demo project with a Service and a Client calling this Service:

Demo Project

The Service has two methods

1
2
3
4
5
6
    public class Service {
        public void calledFromDifferentPackage(){
            onlyCalledInPackage();
        }    
        public void onlyCalledInPackage(){}
    }

and the Client calls one of them from a different package

1
2
3
4
5
    public class Client {    
        public void call() {
            new Service().calledFromDifferentPackage();
        }
    }

I scanned the project into a jQAssistant database and started the server for exploration. Now I can query for the public methods not accessed from a different package in three easy steps.

First step: put a label ‘Public’ on the public methods

            MATCH
                (c:Type:Class)-[:DECLARES]->(m:Method)
            WHERE
                m.visibility='public'
            SET
                m:Public

Second step: put a label ‘UsedFromDifferentPackage’ on methods which are called from a different package

            MATCH
                (t1:Type)-[:DECLARES]->(m:Method),
                (t2:Type)-[:DECLARES]->(p:Method:Public),
                (package1:Package)-[:CONTAINS]->(t1),
                (package2:Package)-[:CONTAINS]->(t2),
                (m)-[:INVOKES]->(p)
            WHERE
                package1.fqn <> package2.fqn
            SET p:UsedFromDifferentPackage

Third step: query for the methods which have no label ‘UsedFromDifferentPackage’

            MATCH
                (c:Type)-[:DECLARES]->(u:Method:Public)
            WHERE NOT
                u:UsedFromDifferentPackage
            RETURN
                c.fqn, u.name

Of course I could have done this in one more complex step. But I decided to separate the concerns in this way because most likely I would add some WHERE clauses in the third step to exclude public APIs, unscanned entry points, uninteresting packages, or examine only some submodules. As a nice side effect, the first two steps can be easily transformed into jQAssistant concepts and the third one into a jQAssistant constraint.

I published the source code on GitHub.