Friday, July 14, 2023

Software Documentation: Less is More, Architecture Decision Records and arc42

"Less is more" is true for many things and especially true for Software Documentation. If you need to document your source code, I really believe that this will not help, because most of the time the problem exists on a higher level: poor code quality, no or very few tests, non modular design and violation of the SOLID principles (https://en.wikipedia.org/wiki/SOLID). As long as you don't generate your documentation from the current source code automatically, it is very difficult to keep the documentation in sync. Because the source code contains many details and can be subject to frequent changes, this can lead to deprecating your documentation faster than you might think. Furthermore, Quite often the documentation is not kept in a single location but distributed over many systems and written by many different people. Therefore, you don't know all the occurrences of the documentation that you have to update or adjust when you change certain parts of your source code.

The big question is, what do I document and how do I document it? As written before, you should not document details - the specification of your software - because details are subject to change (e.g. when you refactor your code, which should be done on a regular basis). Good automated test cases will serve as an executable specification (https://www.martinfowler.com/bliki/SpecificationByExample.html): just look at your tests, if you want to know more details. So, what should we document? I believe that the most valueable and important documentation is about the things that are the least obvious. For example: consider that you have a Java service that uses a PostgreSQL database. What is the benefit, when I document the fact that the service uses a PostgreSQL database? I already can see this when I look at the source code. So in this case, the documentation does not give me any added value - it's just one more thing that I've to keep updating when things change. What should be documented is the decision WHY we use a SQL database and for example not MongoDB. This is a problem that can be solved by using Architecture Decision Records (https://martinfowler.com/articles/scaling-architecture-conversationally.html). There are different templates for ADRs and I like the one from Michael Nygard, because its simple and straight forward (https://github.com/joelparkerhenderson/architecture-decision-record/blob/main/templates/decision-record-template-by-michael-nygard/index.md). Now, you have to write the ADR which probably is the most difficult part. Focus on "less is more" and try to be as clear and precise as possible. Because I believe that everything that is closely connected to the source code, should be part of the source code repository, the ADR should be versioned in the repository together the source code and have its own section in the README. This makes refactoring more easy, because you can utilize the full text search of your IDE and it gives you a better chance to keep documentation and source code in sync.

But most likely, ADRs which are part of your repository will not solve all the documentation problems that you are facing. You might also need some more high level documentation and your company maybe want you to write such a documentation in something like Confluence. It could also be the case that you have to use a different language for the higher level documentation than for the ADRs (e.g. because ADRs can be a team decision, but the company "policy" dictates that all teams have to write high level documentation in Confluence and in German for example). If you need to document more and if you want to write a more sophisticated Software Architecture Documentation (SWAD), you can use the arc42 template (https://arc42.org/). The arc42 template defines 12 "chapters" that you can use for your Software Architecture Documentation. These chapters cover all the important categories which are usually important. in a Software Architecture Documentation. Again, the important thing here: less is more or "travel light". This means that you should only put the chapters from the arc42 template into your documentation that you really need. If for example you don't have anything to say about 7. Deployment View than don't try to write something about it.

Friday, July 7, 2023

How to provide Angular Environment Configuration after Build Time

When you want to deploy a containerized Spring Boot application to Kubernetes and provide environment specific configuration during deployment time (e.g. with a Build Pipeline), this is very easy: you just have to set operating system environment variables in the context where your container process runs and these environment variables have to match your Spring Boot configuration properties in order to provide the configuration you want to use (https://docs.spring.io/spring-boot/docs/3.1.1/reference/htmlsingle/#features.external-config.typesafe-configuration-properties.relaxed-binding.environment-variables).

But this approach does not work with Web Applications which for example are build with Angular and run in a Browser. For example, you can use the official Nginx Docker Image to deploy an Angular app to Kubernetes and you could set operating system environment variables which then can be accessed by the Nginx process in the container . So far, no problem. But how do you want to access such environment variables in a Browser, which runs on a completely different machine than Nginx? The only way to communicate from the Browser with Nginx is via HTTP requests. Furthermore, the default Angular environment.ts configuration can only be used to provide configuration at build time, which means that this configuration will be part of the Docker Image.

Therefore, you have to "inject" a file into your Nginx container, which holds the environment specific configuration. Such a file can then be loaded from the Browser via a HTTP GET request and processed by your Angular app. It makes much sense, that such a configuration file contains JSON data, because this is easy to process with Angular. One important aspect when you are building "cloud native" apps is that you want to build your Docker Image only once and then use it for all environments (e.g. local, dev, prod). To make this work, you have to separate configuration from code, which should automatically be the case when you have a clean and modular design. To separate configuration from code is also one factor in the Twelve-Factor App (https://12factor.net/config). One generic way to "inject" a file into a container at Deployment time is by using Kubernetes Volume Mounts in combination with a Kubernetes ConfigMap (if you're using Kubernetes; see https://kubernetes.io/docs/concepts/storage/volumes/#configmap).

The recommended way to load the configuration into your Angular app is by providing an APP_INITIALIZER which will load the configuration when your Angular App bootstraps (https://angular.io/api/core/APP_INITIALIZER). Because of the asynchronous nature of Angular and the underlying Observer Pattern, the configuration is only accessible through an Observable (https://angular.io/guide/observables). This means that when you make a GET request with the Angular HttpClient, you will get an Observable that holds the configuration as a response. As a consequence, you have to transform this Observable in all areas of your code where you need to access your configuration. Let's say we have a RemoteConfigService which will load the configuration when the APP_INITIALIZER is invoked. The RemoteConfigService can now provide access to the configuration by declaring a getter named getAppConfig with the following signature:

getAppConfig: Observable<AppConfig>

When you want to access a certain property of your JSON configuration, you can use the pipe/switchMap pattern (from rxjs) to transform the Observable. The following method demonstrates how to do that:

getPropertyFromConfig(): Observable<string> {
    return this.remoteConfigService.getAppConfig().pipe(
        switchMap(config => of(config.myPropertyName)));
}

The return value is another Observable which holds the property value. You can directly access the Observable inside an Angular HTML template by using the "async pipe" (https://angular.io/api/common/AsyncPipe).

Wednesday, February 10, 2021

Closures in Python3

Let's write a closure that implements the behavior of a simple counter.

def create_counter():
    value = 0
    def counter(x):
        nonlocal value
        value += x
        return value
 
    return counter
 
c1 = create_counter()
c2 = create_counter()
 
print(c1(3))
print(c1(4))
print(c2(1))
print(c2(7))


The only non-obvious thing here, is the use of the nonlocal statement. Without using the nonlocal statement, we would get the following error:

UnboundLocalError: local variable 'value' referenced before assignment

The reason for this is:

"...the default behavior for binding is to search the local namespace first. The statement allows encapsulated code to rebind variables outside of the local scope besides the global (module) scope."

See https://docs.python.org/3/reference/simple_stmts.html#nonlocal

Wednesday, August 26, 2020

Unlock your Android Smartphone with a USB-Mouse

What do you do, when you can't unlock your Android Smartphone anymore, because the screen glass is broken? The Situation gets even worse, if you have data on that Smartphone. In case your data is stored on an external SD-Card, its not possible to read the SD-Card from another Smartphone or Laptop. This is due to the fact, that by default every Android device encrypts the SD-Card so that the data can only be accessed by the device itself.

You could replace the display glass of your Smartphone, but depending on your model, this can get quite expensive. Fortunately, there is another alternative: You can connect a USB-Mouse with the help of an USB OTG Adapter.

In my case, I managed to destroy the screen glass of my Motorola Moto e4 XT1762. Therefore, I bought an ednet Micro USB 2.0 OTG Adapter for 7,90€.



Plug'n'Play

Sunday, September 29, 2019

Injecting external configuration into Spring Boot and Docker

First, in order to use the configuration from an application.yml oder application.properties, its handy to abstract that with a Spring Component class.

@Component
@ConfigurationProperties("address")
public class AddressConfiguration {
    private String city;
    private String street;

    public AddressConfiguration(String city, String street) {
        this.city = city;
        this.street = street;
    }

    public String getCity() {
        return city;
    }

    public String getStreet() {
        return street;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public void setStreet(String street) {
        this.street = street;
    }
}


The above class maps to the following application.yml:

address:
  city: "New York"


The name that is specified for the ConfigurationProperties annotation must match with the root element name of your configuration, that you want to load. As you can see, the application.yml doesn't contain an entry for street. You can set additional entries (or overwrite existing ones) by supplying a SPRING_APPLICATION_JSON environment variable:

export SPRING_APPLICATION_JSON='{"address":{"street":"Broadway"}}'

If you are running Spring Boot inside a Docker container, you can pass the exported SPRING_APPLICATION_JSON environment variable to the container by providing the -e flag:

docker run -e SPRING_APPLICATION_JSON="$SPRING_APPLICATION_JSON"

Thursday, November 15, 2018

Raspbian Edimax USB WLAN Adapter

In order to get the Edimax USB WLAN Adapter working on Debian / Raspbian, proceed as follows.

(1) Use lsusb to list all USB devices:

$ lsusb

 Bus 001 Device 004: ID 7392:7811 Edimax Technology Co., Ltd EW-7811Un 802.11n Wireless Adapter [Realtek RTL8188CUS]

The Edimax USB WLAN Adapter uses the Realtek RTL8188CUS chipset.

(2) To figure out which driver is used and hence which module needs to be loaded, we can use the system message log:

$ dmesg | grep rtl

[   16.919884] usbcore: registered new interface driver rtl8192cu

Now we know the module name: 8192cu

(3) To load the module during system boot, create the following file with the given content:

$ sudo touch /etc/modprobe.d/8192cu.conf
$ sudo echo "options 8192cu rtw_power_mgnt=0 rtw_enusbss=0" > /etc/modprobe.d/8192cu.conf

(4) Enable the wlan0 device (eth0 will be disabled with this configuration):

#auto eth0
#iface eth0 inet dhcp
 

auto wlan0
iface wlan0 inet dhcp
wpa-ap-scan 1
wpa-scan-ssid 1
wpa-ssid "your-ssid"
wpa-psk "your-wlan-passphrase"


(5) Reboot your PI

Sunday, August 19, 2018

Install VirtualBox on Debian Stretch 9.5

Prerequisites

Add the backports repository to /etc/apt/sources.list:

deb http://ftp.de.debian.org/debian/ stretch-backports main contrib
deb-src http://ftp.de.debian.org/debian/ stretch-backports main contrib


Installation

$ apt-get update
$ apt-get install virtualbox
$ apt-get install virtualbox-ext-pack
$ apt-get install linux-headers-$(uname -r)


Build Kernel Module

dpkg-reconfigure virtualbox-dkms
$ dpkg-reconfigure virtualbox


Links