Replacing Redux

This is an educational piece on state management in modern JavaScript single page applications.

I’m hardly recommending any frameworks and strongly advise against using React. However, Redux, React’s state manager is among the select few dependencies I have recommended several times, including on this blog. The reason is, it’s small yet powerful. And that’s what you should be looking for. Because the code you depend upon becomes a maintenance concern and you should maximize the bang you get for your maintenance buck.

Redux also may be the only dependency I keep using myself in a private project, because it’s just so good. Now I faced a performance problem in that app and had difficulty finding the culprit. Redux kept popping up (spoiler: Redux was innocent!) and I considered what I’d need to get rid of it.

Said application consists of 50% Redux specific code – always maximize the purely functional proportion of your code! And I wasn’t going to rewrite that. However, I looked at what parts of the Redux API my app is actually using and I thought it might be feasible to replace that by something simpler of my own devise.

I embarked on what I thought might be a few days of learning and fun programming. 15 minutes later I was done. And my app was running the same as before. I’ll first show you my Redux replacement then discuss Redux a bit more. This is not going to be Redux bashing, quite to the contrary!

Redux replacement code

export class ReduxReplacement {
	#listeners = [];
	#reducer;
	#store = {};

	constructor(topLevelReducer) { this.#reducer = topLevelReducer; }

	getState() { return this.#store; }

	dispatch(action) {
		this.#store = this.#reducer(this.#store, action);
		deepFreeze(this.#store);
		for(const listener of this.#listeners) listener();
	}

	subscribe(listener) { this.#listeners.push(listener); }
}

function deepFreeze(object) {
	const propNames = Reflect.ownKeys(object);
	for(const name of propNames) {
		const value = object[name];
		if (
			(value && typeof value === 'object') ||
			typeof value === 'function'
		) deepFreeze(value);
	}

	return Object.freeze(object);
}

If you haven’t seen the funny #… syntax at the top: that’s private class members in JavaScript. So, that’s it, my complete Redux replacement. I think Redux clones the state when you call getState. I freeze it instead (with code taken from MDN) for performance reasons and because I know, I can get away with it, because I nowhere manipulate the state passed to me. But the cloning function would look quite similar.

I’ll probably keep using my replacement, because it saves my SPA some 17k (unminified, not zipped) code, bringing it to below a 100k (unminified, not zipped) total. But I wouldn’t even strongly recommend that. Redux is fine, and 17k is tiny in most modern SPAs.

The reason I’m sharing this is an implication that only became clear to me after this exercise: Redux is a great concept. The code is very fine, too, as far as I can tell, but it’s the concept that really shines. And it’s API is obviously quite outstanding, clean, simple and transparent – otherwise my little stunt wouldn’t have had a snowballs chance in hell. But let’s stick with the concept:

Redux abridged

Your data (the whole application’s state!) is kept in a JSON style object. When the server or user changes that state it does so by dispatching a change event to your data store. The store then calls a pure function (I recommend getting yourself familiar with functional programming if you aren’t already) to create a new version of the state.

The state is never changed, only cloned with changes made while creating the clones. That is (plus first class functions) functional programming in a nutshell. The new state creation is executed by a pure function that gets the old state and the event as input.

The whole JSONesque state usually takes the shape of a tree of nested objects and arrays. The new state creation function usually calls other pure functions to handle parts of the state tree. Each function determines itself if an event affects the part of the tree it manages. If it doesn’t affect that part, the function returns the previous version of the state.

To this end all functions are passed that previous version of their respective part of the state, the event, and the whole state. And thus change detection is as simple as old === new on any part of the state.

That’s most of it, though I guess I’ll have missed some important bit. Anyway, this Redux way of state management may feel a bit cumbersome when you get started with it. But the results are outstanding (assuming you stick to some more rules of clean code). I highly recommend it for all but the simplest applications. And I actually do consider replacing Redux to be a serious and very valuable option for any project, including professional projects.

The reason is the ownership problem. Should Redux ever become abandoned or become too closely coupled with React, then you do not need to rewrite your state management. You can simply fall back on your own tiny replacement. Just having that as an option is a great asset for the longevity of your project and no reason to ditch Redux.

The Nanny Lib

And there are, sadly, indications, that Redux’ best time is over. As the average web developer experience and competency – beyond god frameworks that take the developer’s hand and never let go – is never improving due to the continued exponential growth and turnaround of the market, Redux apparently feels compelled to also take the developer’s hand, set up their stores with Redux Toolkit, manipulate their state with Immer … you get the picture.

Add layer upon layer of abstraction onto each problem as an insurance against incompetence and bad choices. And thus bloat your software, reduce user experience and make long maintenance periods ever more tedious.

Actually, if you don’t know what your doing and don’t have someone around who does, I second that approach. But you should also learn. And with that said, Redux is a great place to learn state management. Check it out. And hopefully, in the mid term, acquire the competence to devise your own state management.

Own it

As an example, I’ll show the top level state creator of my app here:

function toplevelReducer(state = {}, action) {
	const newState = createNewState(state, action);
	newState.displayItems = displayItems(
		state.displayItems, action, newState
	);
	return newState;
}

function createNewState(state, action) {
	const reducers = [
		addingShop, filter, mode, nav, items, opened,
		selected, serverUpdates, shops, tags,
	];
	return {
		...Object.fromEntries(reducers.map(
			reducer => [
				reducer.name,
				reducer(state[reducer.name], action, state)
			],
		)),
	};
}

In many cases the createNewState function will do. Just add your “reducer” functions for different branches of the tree to the reducers array and you’re done. In my case the displayItems “reducer” needs information created by other reducers, thus it’s a two step process here.

The point is: It’s not that difficult! Find a simple elegant way to express your needs. Learn from Redux (and other libraries) instead of just using them. You and your results will profit.

Ihr macht kein CI/CD

Neulich saß ich mal wieder in einem Meeting, wo es dem Vernehmen nach um CI/CD ging. Ging es – wie meist – nicht. Und das ist völlig okay. Alle wissen meist worum es grob geht und das ist oft genug. Dass alle dauernd von CI/CD sprechen, auch wenn sie sich damit gar nicht befassen, hat wie so oft, mit Marketing zu tun. Es ist eine coole Catch-Phrase geworden. Eine kleine Einordnung:

Was tatsächlich meist gemeint ist, wenn von CI/CD die Rede ist, sind diverse Technologien, Prozesse und Herangehensweisen, die im Rahmen von CI/CD entwickelt wurden. Und diese Technologien, Prozesse und Herangehensweisen sind gut und meist vorteilhaft. Sie sind nur kein CI/CD.

Das moderne Antiblockier-Bremssystem in PKW wurde zuerst für einen Rennwagen entwickelt. Wenn ich jetzt in meine Familienkutsche mit ABS steige und sage: “Ich gehe jetzt Rennfahren”, das ist ganz ähnlich, wie die Rede von CI/CD bei den meisten Teams. Dabei ist ABS super, sollte jedes Auto haben – so wie auch die allermeisten Teams die besagten Technologien usw. nutzen sollten!

Die Cs in “CI/CD” stehen für das C in “continuous”, zu Deutsch “kontinuierlich”. Also ständig, immer, dauernd. “CI/CD” heißt man integriert jede kleine Code-Änderung sofort (das heißt alle Entwickler arbeiten auf dem gleichen Code, es gibt keine “Branches”, oder nur extrem kurzlebige) und liefert sie sofort an Nutzer aus. Dafür braucht es mehr als die üblicher Weise verwendeten Rosinen aus dem CI/CD Umfeld. Es braucht z.B. “Feature Toggles”, damit man “unfertigen” Code ausliefern kann, ohne ihn gleich scharf zu schalten. Es braucht aber auch Disziplin, Kommunikation, Verantwortung und mehr.

Diverse sehr produktive Teams in oft großen Unternehmen arbeiten so und weil die damit so produktiv sind, hat das Marketing den Begriff zu einem Liebling erklärt. Aber so müssen – und können – nicht alle arbeiten. Trotzdem ist es auch für Teams, die (noch?) nicht so arbeiten, sehr sinnvoll, sich bestimmte Dinge aus dem CI/CD Umfeld abzuschauen. Dabei geht es hauptsächlich darum, viele Schritte vom Entwicklungsrechner zum Nutzer zu automatisieren – Schritte wie Bau, Qualitätssicherung und Auslieferung der Software.

Es macht immer noch einen riesigen Unterschied, ob die Entwickler dann die Knöpfe dieser Automatisierung mehrmals täglich drücken, oder nicht – ein Unterschied mit grundsätzlichen Konsequenzen für Arbeitsweise, Arbeitsergebnis … und Produktivität.

Ein treffenderer Begriff wäre Software Lebenszyklus Automatisierung oder Software Lifecycle Automation statt CI/CD. Wir werden natürlich trotzdem alle weiter von CI/CD reden, was völlig okay ist. Und jetzt wissen wir ja sogar wovon wir reden 😉

Betrieb nachhaltiger Software

Dies ist der letzte von sechs Teilen meiner Artikelserie über nachhaltige Software. Der vorige Teil findet sich hier, der erste hier.

Im vorigen dieser Teil dieser Serie wurde unter anderem erörtert, dass bestimmte Teile der Entwicklung auf Systemen statt finden sollten, die sich unter Kontrolle der Auftraggeberin befinden. Das gilt ebenso für den Betrieb der Software, der ausschließlich auf Systemen der Auftraggeberin stattfinden sollte.

Lieferantinnen bieten gerne an, auch den Betrieb auf eigenen Systemen zu übernehmen. Davon ist tendenziell abzuraten. Erfahrungsgemäß wird für den Betrieb im eigenen Keller weniger Sorgfalt aufgewendet, als für den Betrieb auf Systemen der Kundinnen. Auch führt ein Betrieb auf Systemen der Lieferantinnen wieder ein Stück weiter in die Abhängigkeit von einer speziellen Lieferantin.

Personal

Um diese Abhängigkeit zu minimieren ist es auch sinnvoll, separates Personal für den Software-Betrieb vorzuhalten. Wenn frühzeitig andere und unabhängige Personen für die Entwicklung der Software und ihren Betrieb zuständig sind, ist die Chance gut, mindestens für den Betrieb anbieterunabhängig zu sein.

Die Aufgaben für den Betrieb von Software bestehen u.a. in Folgendem:

  • Überwachung der Hardware auf der die Software läuft
  • Überwachung und Pflege des Netzwerkbetriebes des Systems
  • Regelmäßige Aktualisierung der Software-Umgebung (z.B. Betriebssystem)
  • Aktualisierung der Software

Das für den Betrieb zuständige Personal ist i.d.R. auch die erste Ansprechpartnerin bei Problemen mit der Software. Es ist aber wichtig, langfristig auch Zugriff auf Entwicklerinnen der Software zu behalten. Dies wird im besten Fall nur selten und kurz nötig sein, kann aber in Einzelfällen der einzige Weg sein, bestimmte Probleme im Betrieb zu lösen.

Der Betrieb von Software erfordert völlig andere Kompetenzen als ihre Entwicklung. Letztlich wird es auch kosteneffizienter sein, hier Spezialistinnen einzusetzen. Diese werden bei kleinen und mittleren Projekten wahrscheinlich nur einige Stunden pro Monat mit dem Betrieb beschäftigt sein. Daher liegt ein Outsourcing hier oft nahe.

Container

Für den Betrieb von Software müssen oft sehr spezifische Bedingungen hergestellt werden. Eine einfache Serveranwendung erfordert z.B. i.d.R. mindestens:

All diese Komponenten müssen miteinander verknüpft werden und i.d.R. in spezifischen Versionen vorliegen. Und diese Komponenten haben ihrerseits Abhängigkeiten, die in der weiteren Softwareumgebung (z.B. im Betriebssystem) vorliegen müssen und die sich schlimmstenfalls sogar teilweise ausschließen können.

Aufgrund dieser Komplexität kommt es immer wieder vor, dass bestimmte für den Betrieb einer Firma essentielle Software nur auf einem ganz bestimmten Computer läuft und sich nicht aktualisieren oder woanders replizieren lässt. Das ist offensichtlich eine riskante Herangehensweise.

Es ist daher meist sinnvoll – auch unabhängig von skalierbaren Clouddeployments, wo dies Gang und Gäbe ist – Software in sogenannte Container zu verpacken. Das sind standardisierte Pakete, die genau definieren, welche Abhängigkeiten die Software genau benötigt. Diese Pakete sind im Betrieb so gekapselt, dass es nicht zu unerwünschten Konflikten zwischen unterschiedlichen Teilen eines Systems kommt. Server, Anwendungsumgebung und Datenbank werden in ihren jeweils eigenen gekapselten Umgebungen betrieben und sind so deutlich einfacher zu verwalten.

Der Aufwand der Containerisierung ist bei entsprechender Kompetenz überschaubar und zahlt sich oft schon in der Entwicklung wieder aus – weil sie auch die Zusammenarbeit unterschiedlicher Teams während der Entwicklung erleichtern kann. Die Containerisierung sollte i.d.R. von der Softwarelieferantin übernommen werden und ist Teil des automatischen Deployments.

On-Premise, Cloud, Serverless

Es gibt mittlerweile zahlreiche Möglichkeiten, wo man Software laufen lassen kann, z.B.:

  • On-Premise, also auf Systemen des Auftraggebers,
  • auf Servern in der Cloud,
  • oder gar “serverless” in der Cloud

“On-Premise” muss dabei nicht heißen, dass der entsprechenden Computer im Firmengebäude der Auftraggeberin steht. Man kann auch komplette Systeme, z.B. bei kleinen Anbietern mieten, wo diese in geeigneten klimatisierten Serverräumen mit unterbrechungsfreier Stromversorgung und redundanter Netzanbindung laufen.

Für kleine und mittlere Unternehmen ist eine solche Miete oft günstiger als solche Infrastruktur selbst zu betreiben. Allerdings sollte stets bedacht werden: Daten die auf fremden Systemen liegen, stehen letztlich unter fremder Kontrolle. Hier müssen Datensensibilität, Vertrauen und Kosten abgewogen werden.

Bei einem Betrieb in großen Clouds sollte darauf geachtet werden, den Betrieb möglichst Anbieterinnen-unabhängig zu gestalten. Das Geschäftsmodell der großen Anbieterinnen besteht allerdings darin, Kundinnen von den eigenen Systemen abhängig zu machen, und dies zu vermeiden erfordert signifikantes Knowhow und verursacht Kosten. So oder so, ist ein Betrieb in der Cloud vorab gut zu planen. Es gibt zahlreiche Optionen für den Betrieb die alle unterschiedliche Kosten-/Nutzen-Konstellationen mitbringen und entsprechend auf das Szenario der Auftraggeberin abgestimmt sein sollten.

Ein modernes Extrem des Cloudbetriebes ist ein “serverless” Deployment. Hier kommen entgegen der Benennung sehr wohl Server zum Einsatz, allerdings werden diese komplett von der Anbieterin betrieben. Dies spart der Auftraggeberin erhebliche Kosten in der Wartung, kann aber auch noch erheblichere Kosten in den Rechnungen der Anbieterin verursachen. Serverless Deployments liegen nahe, wenn mit relativ wenigen Aufrufen der Software gerechnet werden kann.

Mit einem serverless Deployment begibt man sich aber in maximale Abhängigkeit von einer Anbieterin. Es ist auch möglich, Software so zu entwickeln, dass sie sich sowohl serverless als auch mit einem eigenen Server betreiben lässt. Dies kann die Abhängigkeit von einer Anbieterin mindern, verursacht aber wieder eigene Kosten.

Epilog

Dies schließt meine Serie zur Einführung in die Entwicklung und den Betrieb von Software ab, die einen nachhaltigen Wert für Ihr Unternehmen liefert und nebenbei Ressourcen schont und damit auch ökologische Nachhaltigkeit anstrebt. Wie ich hier gezeigt habe, kann man bei jedem Schritt im Lebenszyklus eines Software-Projektes etwas dafür tun, die Nachhaltigkeit und Langlebigkeit des Projektes zu fördern. In vielen Fällen sind das aber Dinge, die Aufmerksamkeit, Problembewusstsein und Initiative/Umlenken von Seiten des auftraggebenden Unternehmens erfordern.

Ich wünsche Ihnen viel Erfolg mit Ihrem Softwareprojekt!

Entwicklung nachhaltiger Software

Dies ist der fünfte von sechs Teilen meiner Artikelserie über nachhaltige Software. Der vorige Teil findet sich hier, der erste hier.

Hierarchien

Wenn mit der eigentlichen Entwicklung begonnen wird, steht fest mit welcher Technologie das Projekt angegangen wird. Wahrscheinlich werden im Laufe des Projektes noch mindestens kleinere Ergänzungen der verwendeten Technologien vorkommen. Eine Lieferantin ist ebenfalls ausgewählt.

Falls diese Voraussetzungen mit Hilfe einer externen Beraterin geschaffen wurden, sollte nun darauf geachtet werden, dass die unabhängige Beraterin nicht in der Hierarchie über dem mit der Lieferung beauftragten Unternehmen steht. Dies ergibt sich oft automatisch, weil die Unabhängige die Grobplanung vorlegt, die dann von der Lieferantin im Detail umzusetzen ist. Für das beauftragende Unternehmen ist es aber am wertvollsten, wenn die Unabhängige und die Lieferantin auf Augenhöhe miteinander diskutieren und die Auftraggeberin die Argumente abwägen kann.

Entwicklungsplattform

Bevor die Entwicklerinnen anfangen Code zu schreiben, sollten wichtige Voraussetzungen geschaffen werden, die die langfristige Kontrolle über die zu entwickelnde Technologie für die Auftraggeberin sichern. Wenn man als Auftraggeberin nichts anderes vorgibt, wird die Softwareentwicklung i.d.R. auf Systemen der Lieferantin statt finden. Damit gibt man als Auftraggeberin einen sehr wichtigen Aspekt der Software-Ownership aus der Hand. Daher sollte hier vorgebeugt und darauf geachtet werden, dass die Entwicklung teilweise auf eigenen Systemen stattfindet.

Während der Software-Entwicklung werden den Entwicklerinnen zahlreiche einzelne Aufgaben gegeben. Die Umsetzung dieser Aufgaben schlägt sich in Änderungen des Programmcodes nieder und diese Änderungen werden in einer Softwareversionsverwaltung dokumentiert. Die Aufgaben werden in einem Ticket-System dokumentiert.

Diese beiden Systeme – Softwareversionsverwaltung und Ticketsystem – sollten in Besitz und unter Kontrolle der Auftraggeberin stehen. Denn hier lässt sich nachverfolgen, warum (Aufgabe) spezifische Änderungen (Versionsverwaltung) am Code vorgenommen wurden. Dies ist eine sehr wertvolle technische Dokumentation, die die Auftraggeberin haben muss, falls sie in der Lage sein möchte, die Lieferantin zu wechseln.

Wenn Entwicklerinnen den Code ändern, muss dieser (zumindest in professionellen Entwicklungsprozessen) noch für die Ausführung übersetzt und an bestimmte Stellen gebracht werden, wo erst Testerinnen und später Nutzerinnen das Ergebnis sehen. Dieser Prozess des “Deployments” sollte von der Lieferantin automatisiert werden und Teil der Entwicklungsplattform sein, die sich unter Kontrolle der Auftraggeberin befindet. Andernfalls stellt das “Deployment” eine weitere, potentiell signifikante Hürde für einen Lieferantinnenwechsel dar.

Meiner Erfahrung nach haben sich für all diese Systeme einfache integrierte Lösungen wie GitLab bewährt. Dies ist relativ einfach lokal zu installieren und zu warten, adressiert alle oben genannten Punkte und ist dabei nicht so überkomplex wie z.B. Atlassian Jira.

Es ist auch möglich, hierfür Cloud-Systeme zu verwenden. Idealer Weise sollten solche Systeme gewählt werden, wo sich die Daten exportieren lassen und auch auf lokalen Installationen funktionieren. Andernfalls begibt man sich in kaum auflösbare Abhängigkeit zum Cloudprovider. Auch letzteres kann eine valide Option sein, wenn man sein Unternehmen z.B. bereits vollständig in Abhängigkeit von Microsoft gebracht hat, und mit ein bisschen Unabhängigkeit in der Code-Ownership nichts mehr gewinnen zu können glaubt.

Agile Entwicklung

Wo man früher vor Beginn der Entwicklung detailliert festgelegt hat, wie das fertige Produkt aussehen soll (Pflichtenheft) geht man heute “agil” vor. Denn es hat sich gezeigt, dass vor Entwicklungsbeginn fast nie die nötigen Informationen vorliegen und fest definierte Produkte regelmäßig in der Umsetzung scheitern.

Statt dessen macht die Entwicklung kleine Schritte und stimmt sich immer wieder mit den künftigen Nutzerinnen ab, ob die Zwischenergebnisse sinnvoll aussehen und den Nutzerinnen tatsächlich Nutzen bringen, sowie, wie dann die nächsten kleinen Schritte aussehen sollen. Es muss für eine erfolgreiche Umsetzung also eine intensive Kommunikation zwischen Nutzerinnen und Entwicklerinnen geben (evtl. indirekte Kommunikation via z.B. User-Experience-Entwicklerinnen).

Das bedeutet, die Auftraggeberin muss, um den Erfolg des Projektes nicht zu gefährden, die künftigen Nutzerinnen teilweise freistellen, falls es sich um Kolleginnen handelt, oder regelmäßige systematische Evaluationen mit Testnutzerinnen organisieren, falls es sich um ein digitales Produkt für seine Kundinnen handelt.

Da das Projekt am Anfang nicht detailliert definiert ist, lässt sich auch kein Festpreis vereinbaren. Statt dessen verhandelt man wie hoch die laufenden Kosten der Entwicklung sind. Außerdem sollte die Lieferantin eine Indikation geben, wie groß sein Aufwand für die ersten Schritte ist, bis man Nutzerinnen etwas sinnvolles (einen minimalen Prototypen) zeigen kann.

KISS & technologische Abhängigkeiten

Bei der Umsetzung selbst, sollte darauf geachtet werden, alles so einfach wie irgend möglich zu halten. Dies ist als das KISS– (Keep It Simple Stupid) Prinzip bekannt. Technologinnen neigen dazu, komplexe Technologien einzusetzen, die (noch) nicht zwingend notwendig sind (YAGNI-Prinzip: You Ain’t Gonna Need It). Das kann langfristig erhebliche unnötige Kosten verursachen.

Möglichst viel Funktionalität sollte mit möglichst “tiefen” Schichten der gewählten Technologie umgesetzt werden. Im Zuge der fortschreitenden Entwicklung der Software-Ökosysteme, wird auch immer mehr Funktionalität in solch “tiefere” Schichten übernommen. Dies kommt oft erst stark verzögert bei den Lieferanten an, die wie gewohnt mit den “höheren” Schichten ihrer Frameworks arbeiten. Die “tieferen” Schichten haben den immensen Vorteil, dass sie viel stabiler sind und langfristig geringere Kosten verursachen.

Jede zusätzliche technische Abhängigkeit (zusätzliche “höhere” Schicht) birgt das Risiko, dass die Lebensdauer dieser Abhängigkeit kleiner ist, als die des Projektes, in dem sie verwendet wird. Wenn eine Abhängigkeit ihr Lebensende erreicht, “erbt” das Projekt diese Abhängigkeit: es müssen evtl. signifikante Ressourcen investiert werden, um diesen Teil der Software sicher (oder überhaupt) weiter betreiben zu können.

In der Softwareentwicklung taucht immer mal wieder das Problem auf, dass ein Programm zu langsam läuft. Eine einfache Lösung, die aufgrund ihrer Einfachheit meist angestrebt wird, besteht darin, der Software mehr Hardwareressourcen zur Verfügung zu stellen. In vielen Fällen lässt sich aber die Software mit überschaubarem Aufwand so verbessern, dass keine andere Hardware nötig ist. Zu erkennen, wann Softwareverbesserung und wann stärkere Hardware sinnvoll ist, erfordert einige Erfahrung.

Die in diesem Abschnitt erörterten Punkte sind der Hauptgrund dafür, dass in dieser Art wirtschaftlich nachhaltig entwickelte Software auch ressourcenschonender ist und damit zur ökologischen Nachhaltigkeit beiträgt.

Die Berücksichtigung dieser Punkte erfordert signifikantes technisches Knowhow auf Seiten des auftraggebenden Unternehmens. Denn zahlreiche technische Entscheidung müssen auf Konsistenz mit diesem Ziel der Einfachheit geprüft werden. Wenn dieses Knowhow nicht vorhanden ist oder zugekauft wird, besteht die beste Strategie darin, möglichst breit genutzte und aktuell weiterentwickelte Technologien einzusetzen.

Abschluss der Entwicklung

Agile Entwicklung ist ein Prozess ohne eingebautes Ende. Es finden sich erfahrungsgemäß immer neue Features von denen (mindestens einzelne) Nutzerinnen meinen, das wäre doch auch noch nützlich. Doch der zusätzliche Nutzen nimmt ab, je länger die Entwicklung dauert. Er kann sogar insgesamt negativ werden, wenn das Produkt für Nutzerinnen dadurch zu komplex wird.

Und selbst wenn das nicht der Fall ist: die Entwicklung kostet Geld und die Wartung ebenso. Und die Kosten von beidem steigen nicht-linear (sondern exponentiell, wenn auch zunächst flach), mit zunehmender Dauer der Entwicklung. Denn je länger die Entwicklung dauert, desto komplexer wird das System in der Regel und desto schwieriger wird somit das Hinzufügen weiterer Features und die Wartung des Systems.

Daher ist die Investition der Auftraggeberin lohnender, je früher der Abschluss geschafft wird. Sollte sich kein guter Abschluss abzeichnen, kann es sinnvoll sein, für weitere Features jeweils eigene Business Cases zu rechnen, damit die Entwicklung nicht ausufert.

Ist der Abschluss geschafft, geht das Projekt in den Dauerbetrieb. Worauf Auftraggeberinnen hier achten sollten, wird im nächsten Teil dieser Serie besprochen.

Lieferantenauswahl für nachhaltige Software

Dies ist der vierte von sechs Teilen meiner Artikelserie über nachhaltige Software. Der vorige Teil findet sich hier, der erste hier.

Wie ich im vorigen Teil ausführte, grenzt die Vorauswahl der Technologie das Feld möglicher Lieferantinnen ein. Ist eine Vorauswahl der Technologie also getroffen, kann eine Ausschreibung des Projektes erfolgen. Wenn es sich nicht um ein ganz kleines Projekt handelt, ist die Ausschreibung ein mehrstufiger Prozess. Traditionell wurde zunächst ein detailliertes Lastenheft erstellt, dieser Ansatz hat sich jedoch als nicht optimal herausgestellt.

Der Auswahlprozess

Ein Ausschreibungsprozess kann heute z.B. so aussehen: Es wird eine Vorauswahl potentieller Lieferantinnen getroffen. Mit jeder potentiellen Lieferantin werden folgende Schritte ausgeführt:

  1. Kontaktaufnahme
  2. Erläuterung des Grobplanes für Technikerinnen der Lieferantin
  3. Ggf. Klärung von Rückfragen
  4. Vorstellung eines technischen Umsetzungsplanes durch Technikerinnen der Lieferantin und eines Budgetplanes durch Managerinnen der Lieferantin

Danach werden alle Lieferantinnen nach unterschiedlichen Kriterien verglichen. Der Preis ist ein Kriterium aber nicht unbedingt das ausschlaggebende. Unter anderem ist der Preis nur eine Indikation des Mindestpreises der Erstellung. Die Auftraggeberin sollte bestrebt sein, das langfristige Kosten-Nutzen-Verhältnis zu optimieren. Der von der Lieferantin genannte Preis ist lediglich eine Untergrenze für die Hälfte der Kosten. Die andere Hälfte der Kosten (oder mehr) fällt voraussichtlich im Betrieb an und der erwartete Nutzen kann sich abhängig von der Lieferantin ebenfalls stark unterscheiden.

Die Höhe der Betriebs- und Wartungskosten der Software hängen von der konkret von der Lieferantin vorgeschlagenen Technologie ab. Der erwartete Nutzen kann z.B. bezüglich der künftigen Erweiterbarkeit abgeschätzt werden, je nachdem wie systematisch/analytisch sich die Lieferantin präsentiert hat und welche Technologien vorgesehen sind. Der kommerzielle Wert eines Endnutzerinnenproduktes wird auch davon abhängen, wie gut die Lieferantin im User Experience Design aufgestellt ist, und wie er sich diesbezüglich präsentiert.

Sowohl Kosten als auch Nutzen werden auch davon abhängen, wie gut die Kommunikation mit der Lieferantin verläuft. Die Kommunikation zwischen Auftraggeberin und Lieferantin ist bei der Erstellung eigener Software absolut kritisch. Sie berührt, ja bestimmt alle Aspekte des Erstellungsprozesses und seines Ergebnisses und ist daher ein weiterer wichtiger Punkt in der Lieferantinnenauswahl.

Alle Lieferantinnen werden also nach einer ganzen Reihe von Kriterien bewertet und verglichen. Auf dieser Basis wird eine Lieferantin für die Umsetzung ausgewählt. Im nächsten Teil dieser Artikelserie wird erörtert, worauf bei der Entwicklung selbst zu achten ist, um ein nachhaltiges Resultat für die Auftraggeberin zu erzielen.

Agenturen, Freiberufler, Offshore

Bei der Vorauswahl potentieller Lieferantinnen können unterschiedliche Optionen in Erwägung gezogen werden. Wie ich oben erörterte, ist die Kommunikation zwischen Auftraggeberin und Lieferantin von zentraler Bedeutung. Sie ist auch ein zentraler Faktor in der Vorauswahl. Als Standardoption für Softwarelieferantinnen kann eine deutsche Medien- oder Softwareagentur gelten. Diese zeichnen sich i.d.R. durch optimierte Kommunikation zwischen Auftraggeberin und Lieferantinnenmanagement aus. Eine deutsche Agentur spricht die Sprache einer deutschen Auftraggeberin.

Es ist aber zu beachten, das das letztlich Ausschlaggebende die Kommunikation zwischen künftigen Nutzerinnen der Software und ihren Entwicklerinnen ist. Das Management ist oft dazwischen geschaltet, und teils werden vor die Nutzerinnen noch spezielle Rollen wie User Experience Designerin, Requirements Engineer oder Product Owner gesetzt. Stille Post macht Kommunikation nicht unbedingt besser (obwohl das in der Softwareentwicklung auch nicht ausgeschlossen ist).

Daher ist es für Auftraggeberinnen ein Pluspunkt, wenn sie schon im Auswahlprozess direkt mit den künftigen Entwicklerinnen kommunizieren können. Dies wird z.B. der Fall sein, wenn statt mit einer Agentur mit Freiberuflerinnen gearbeitet wird. Die am besten qualifizierten Entwicklerinnen (sowohl in der Entwicklung von Software als auch in der Kommunikation mit Nicht-Technikerinnen) arbeiten oft als Freiberuflerinnen. Auch spart man in der direkten Zusammenarbeit mit Freiberuflerinnen die Aufschläge, die Agenturen für ihren eigenen Overhead und Gewinn berechnen müssen.

Aber das Risiko für die Auftraggeberin erhöht sich auch, wenn sie ein eigenes Team aus Freiberuflerinnen zusammenstellt. Dieses Team muss seine eigenen Prozesse neu entwickeln, und es kann persönliche Inkompatibilitäten zwischen Mitwirkenden geben. Ein solcher Ansatz ist nur empfehlenswert, wenn die Auftraggeberin über hinlängliche Erfahrung in der Umsetzung von Softwareprojekten verfügt.

Eine weitere Option ist der Einsatz von Agenturen aus anderen Ländern, z.B. aus Osteuropa oder Indien. Eine Entwicklung mit Osteuropäerinnen kann qualitativ gleichwertige Ergebnisse bei einem etwas günstigeren Preis erzielen als eine vergleichbare Entwicklung in Deutschland. Die kulturellen Unterschiede sind überschaubar und das Qualifikationslevel oft ähnlich. Es werden aber sehr wahrscheinlich Abstriche in der Kommunikation in Kauf genommen werden müssen – mit allen Konsequenzen. Daher muss sich eine solche Option auch in signifikanten Ersparnissen niederschlagen.

Bei der Entwicklung von Software für den (deutschen) Endnutzerinnenmarkt würde ich mindestens die User Experience in Deutschland entwickeln lassen. Bei osteuropäischen Lieferantinnen würde ich solche bevorzugen, die Erfahrung in der Zusammenarbeit mit deutschen Kundinnen haben.

Das Kostengefälle noch weiter nach Osten (nach Indien) ist potentiell geringer als das zwischen Deutschland und Osteuropa. Die kulturelle Barriere ist aber sehr hoch und der Qualifikationsunterschied kann ebenfalls größer sein. Eine Zusammenarbeit mit indischen Entwicklerinnen sollte ausschließlich in Erwägung gezogen werden, wenn die Auftraggeberin über umfangreiche Erfahrung in der Zusammenarbeit mit Indien verfügt. Andernfalls wird es fast zwangsläufig zu potentiell teuren Missverständnissen kommen.

Ist die Lieferantinnenauswahl getroffen, geht es an die Entwicklung der eigenen Software. Worauf Auftraggeberinnen hier achten sollten, wird im nächsten Teil dieser Serie besprochen.

Technologieauswahl für nachhaltige Software

Dies ist der dritte von sechs Teilen meiner Artikelserie über nachhaltige Software. Der vorige Teil findet sich hier, der erste hier.

Wie ich im vorigen Teil ausführte, sollte die Planung einer speziell entwickelten Software mit fundiertem, unabhängigen Knowhow beginnen, das idealer Weise in der Firma vorhanden ist und andernfalls unabhängig vom Lieferanten eingekauft werden sollte. Mit diesem Knowhow wird eine Grobplanung erstellt, die die Basis für die Technologie- und Lieferantinnenauswahl darstellt.

Technologie und Personal

Die Entscheidungen über Technologie und Lieferantinnen hängen zusammen, da Lieferantinnen meist auf bestimmte Technologien spezialisiert sind. Mit der Technologie wiederum entscheidet man sich für ein ganzes technologisches Ökosystem. Diese Systeme haben unterschiedliche historische Hintergründe und werden tendenziell in unterschiedlichen Bereichen eingesetzt. Es gilt also, zahlreiche Aspekte zu beachten.

Da die Entscheidung für eine bestimmte Technologie und Lieferantinnen Konsequenzen für Preis, Risiko und Nachhaltigkeit des entwickelten Produktes hat, lohnt es sich, diese Entscheidung systematisch anzugehen und Zeit und Ressourcen in den Entscheidungsfindungsprozess zu stecken.

Es kann sein, dass schon vor Projektbeginn gute Erfahrungen mit einer bestimmten Lieferantin vorliegen, und diese daher gesetzt ist. Da jedes Softwareprojekt ein erhebliches Risiko mit sich bringt, nur Kosten und keinerlei Nutzen mit sich zu bringen, hat es einen hohen Wert, eine Lieferantin zu haben, die bereits bewiesen hat, dass sie erfolgreich liefern kann, und der man vertraut. Daher kann es durchaus vernünftig sein – zumindest bei kleinen und mittleren Projekten – nicht die Technik sondern die Lieferantin an erste Stelle zu setzen. Dies kann auch zu signifikanten Einsparungen in der Vorbereitung führen – siehe im Folgenden.

Technologieauswahl

Doch da Softwareprojekte oft sehr langlebig sind, und die Wahl der Technik erhebliche Konsequenzen hat – die Kosten von Softwareprojekten entstehen im Schnitt zu mindestens 50% nicht bei der Erstellung der Software sondern in Betrieb und Wartung – sollte vor der Ausschreibung mindestens eine Vorauswahl der Technologie stehen.

Diese Auswahl berührt so viele Aspekte, dass ich hier nur ein paar allgemeine Gedanken dazu teilen kann. Für Kosten und Qualität einer Software ist es viel wichtiger, die richtigen Leute zu engagieren, als die richtige Technologie zu wählen. Aber die Wahl der Technologie führt umgekehrt dazu, dass je nach gewählter Technologie unterschiedliche Personenkreise für die Umsetzung fokussiert werden.

Zwischen Basteln und Enterprise

Das Erstellen von Software ist ein Handwerk, für das es kaum brauchbare formelle Ausbildung gibt. Die Art, wie wir Softwareprojekte umsetzen, hat sich in den letzten 50 Jahren auch so schnell gewandelt (und wandelt sich noch), dass eine solche Ausbildung nicht wirklich möglich gewesen wäre. Daher haben Softwareentwickler sehr vielfältige Hintergründe, die sich auch in der Qualität der gelieferten Software niederschlagen können.

Mit der Wahl der Technologie bevorzugt man jeweils bestimmte Personenkreise. Andererseits haben unterschiedliche Technologien unterschiedliche Strategien, den Mangel an brauchbarer Ausbildung auszugleichen. Da Entwicklerinnen und ihre Managerinnen nur eher selten das Knowhow mitbringen, wie man ein komplexes Projekt sinnvoll strukturiert und umsetzt, machen unterschiedliche Technologien hierzu unterschiedliche Vorgaben. Sie leiten Entwicklerinnen in bestimmte Richtungen.

Wenn Sie sich z.B. für Java (mit Spring) entscheiden, werden sie eher Entwicklerinnen bekommen, die Informatik studiert und Erfahrung in großen Projekten gesammelt haben. Das Informatikstudium lehrt nicht, wie man komplexe Projekte erfolgreich umsetzt. Aber es vermittelt immerhin eine systematische, analytische Herangehensweise, und die Arbeit in großen Projekten lehrt bestimmte sinnvolle Praktiken.

Dass Java-Entwicklerinnen eher Erfahrung aus großen Projekten mitbringen, liegt daran, dass Java (Spring) für solche Projekte optimiert ist. Java macht es z.B. einfach, einen kleinen Teil der Software separat zu analysieren, ohne sich um das Gesamtsystem zu kümmern. Es macht es aber viel schwerer zu verstehen, wie das Gesamtsystem funktioniert. Das ist gut, wenn sie ein sehr großes Projekt mit sehr vielen Entwicklerinnen haben. Das ist aber schlecht, wenn sie ein kleines Projekt mit wenigen Entwicklerinnen haben, die das Gesamtsystem möglichst komplett verstehen sollten.

Am anderen Ende der Skala steht PHP. Sehr viele Menschen mit ganz unterschiedlichen Hintergründen haben irgendwann gelernt, ein bisschen HTML zu schreiben. PHP macht es trivial einfach ins HTML hinein ein paar Datenbankabfragen zu schreiben und so einfache Webanwendungen zu erstellen. Daher haben PHP-Entwicklerinnen tendenziell einen weniger akademischen Hintergrund als Java-Entwicklerinnen. Sie haben auch eher Erfahrung mit kleineren und kleinsten Projekten.

Selbsternannte echte Entwicklerinnen nehmen PHP oft nicht sonderlich ernst. Es gilt als unprofessionelles Gebastel (obwohl es sich gerade in den letzten Jahren stark professionalisiert hat). Aber wenn sie ein kleines Projekt haben, für das PHP eine gute Passung hat, werden sie sehr wahrscheinlich einen Bruchteil des Java-Preises für die Umsetzung zahlen. Und wenn das Projekt “richtig” aufgesetzt wurde, werden Sie sogar ein besseres Ergebnis (langfristig günstiger zu betreiben und zu warten) erhalten als mit Java.

Zwischen PHP und Java gibt es noch ein halbes Dutzend anderer möglicher Software-Ökosysteme und innerhalb dieser Ökosysteme gibt es ganz unterschiedliche Ausprägungen. Ein Javacript Projekt, das Angular einsetzt, tendiert z.B. eher ins Java-Umfeld, wohingegen ein Javascript/React Projekt in diversen Kontexten zu finden ist.

Dabei gibt es einige Indikatoren, die eine Wahl relativ einfach machen können: wenn sie im Projekt z.B. Machine Learning einsetzen wollen, werden sie sehr wahrscheinlich auch Python einsetzen. Wenn es ein kleines Projekt ist, können Sie evtl. ganz auf Python setzen. Wenn sie eine grafische Web-Applikation eines bestimmten Typs entwickeln lassen, kommen sie kaum um Javascript herum. Es kann dann (muss aber nicht) sinnvoll sein, ganz auf Javascript zu setzen.

Da die Wahl der richtigen Technologie völlig vom umzusetzenden Projekt und seinem Umfeld abhängt, kann ich hier nur den allgemeinen Rat geben, als kleines oder mittleres Unternehmen nicht auf Technologien zu fokussieren, die für große Unternehmen optimiert sind. “Professionalität”, die mit solchen Technologien assoziiert wird, heißt vor allem auch viele Beteiligte und stark spezialisierte Rollen wie Softwarearchitektin. Tendenziell haben sie die besten Chancen auf ein gutes Preis-Leistungs-Verhältnis, wenn sie “kleine” Technologien professionell einsetzen.

Wie ein solch professioneller Einsatz “kleinerer” Technologien aussehen kann, wird im übernächsten Teil dieser Serie beschrieben. Zunächst geht es im nächsten Teil um die Auswahl einer geeigneten Lieferantin.

Die Vorbereitung nachhaltiger Softwareentwicklung

Dies ist der zweite von sechs Teilen meiner Artikelserie über nachhaltige Software. Der erste Teil findet sich hier.

Dort habe ich u.a. die grundsätzliche Entscheidung für die Entwicklung einer eigenen Software diskutiert. Wenn diese Entscheidung gefallen ist, beginnt die Planung der Software. Diese sollte mit fundiertem, unabhängigen Knowhow beginnen, das idealer Weise in der Firma vorhanden ist und andernfalls unabhängig vom (künftigen) Lieferanten eingekauft werden sollte.

Eine zweite Meinung

Wenn das Knowhow nicht in der eigenen Firma vorhanden ist, sollte von Beginn an ein Fokus darauf gelegt werden, entsprechendes Knowhow im Haus aufzubauen. Das bedeutet nicht, dass jede Firma eine Technologie-Expertin benötigt, sondern, dass Erfahrung beim eigenen Personal aufgebaut wird, Erfahrung mit dem Management von Technologie-Expertinnen, sowie dass der Blick für Problemkreise wie die hier besprochenen geschärft wird.

Es ist ratsam, grundsätzliches Knowhow diesbezüglich in der eigenen Firma zu haben, da nur so der Besitz der beauftragten Software für die eigene Firma langfristig sichergestellt werden kann. Wenn keinerlei Knowhow in-House vorhanden ist, wird die Firma sehr weitgehend den externen Expertinnen ausgeliefert sein. Es wird dann z.B. potentiell unmöglich, externe Lieferantinnen gegen andere externe Lieferantinnen auszutauschen. Die Externen bleiben die wahren “Besitzerinnen” der beauftragten Software, auch wenn das juristisch nicht der Fall ist.

Sollte es noch wenig oder keine Erfahrung mit der Beauftragung von Software im Unternehmen geben, empfehle ich, mindestens eine unabhängige Beraterin zu engagieren. Es kann ggf. auch mal mehr als eine sein: der Wesentliche Punkt ist, dass das beauftragende Unternehmen, nicht ausschließlich der Meinung des mit der Umsetzung beauftragten Unternehmens ausgeliefert ist. Dies ist für Kleinstprojekte natürlich nicht sinnvoll.

Jedes Unternehmen das, und jede Freiberuflerin, die Software für andere Unternehmen liefert, hat spezifische Ressourcen und spezifisches Knowhow, das für Lieferung bevorzugt eingesetzt wird. Das bedingt aber oft nicht den optimalen Ansatz für das auftraggebende Unternehmen. Daher ist es sehr wertvoll, eine zweite Meinung zu wichtigen Fragen zu haben.

Wird eine Externe beauftragt, sollte dieser eine interne Mitarbeiterin zur Seite gestellt werden, um Wissen ins Unternehmen zu transferieren. Es geht wie gesagt nicht darum, umfassendes technisches Knowhow zu erwerben – dafür kann kann man jederzeit externen Rat erwerben – sondern Erfahrung mit dem Management von Softwareprojekten zu sammeln und den Blick für Dinge zu schärfen, die Software nachhaltig für das eigene Unternehmen machen können.

Ein grober Plan

Auf Basis des vorhandenen oder erworben Knowhows wird eine erste grobe Planung erstellt, wie das Projekt umgesetzt wird. Es wird hierbei ermittelt, welche Komponenten das System benötigt – z.B. Datenhaltung, Benutzeroberfläche, APIs für die Nutzung durch andere Systeme und so weiter. Hier wird auch geplant, wie das neue System mit existierenden oder anderen geplanten Systemen der Firma interagieren wird und welche Ressourcen (technisch und human) für den späteren Betrieb genutzt werden sollen.

Eine solche Planung kann abhängig von der Komplexität des Vorhabens Stunden bis Monate in Anspruch nehmen. Für die nächsten Schritte der Umsetzung ist es sehr wichtig, einen groben Plan für die oben angesprochenen Punkte zu haben, denn dieser Plan gibt wichtige Indikationen für in Frage kommende Technologien und Lieferanten.

Die Auswahl der Technologie wird im nächsten Teil dieser Artikelserie besprochen.

Nachhaltige Software

Als Unternehmer sind wir dem Diktat wirtschaftlicher Nachhaltigkeit unterworfen. Und auf die wirtschaftliche Nachhaltigkeit von Software-Entwicklung und -Betrieb werde ich hier auch den Schwerpunkt legen. Das bedeutet ich beantworte die Frage wie sich Software wirtschaftlich entwickeln und langfristig kosteneffizient betreiben lässt.

Für kleine und mittelständische Unternehmen – für die ich hier hauptsächlich schreibe – bringt ein solcher Ansatz meist automatisch energetische und somit ökologische Nachhaltigkeit mit sich. Bei Großunternehmen kann das anders aussehen.

Diese Artikelserie wurde komplett ohne Zuhilfenahme von Sprachmodellen erstellt und vom Autor Wort für Wort selbst geschrieben.

Make or Buy

Die erste Frage, die sich bei einem potentiellen Softwareprojekt stellt, ist: soll eine existierende Software von der Stange gekauft und ggf. angepasst werden, oder soll eine eigene Software entwickelt werden. Letzteres kommt nur in Frage, wenn die beauftragende Firma eine gewisse Finanzkraft mitbringt.

Software ist immer eine Art von Automation. Diese kann nur lohnen, wenn durch die Automation Kosten eingespart werden. Die Kosten von professioneller Softwareentwicklung und -betrieb haben eine vergleichbare Größenordnung wie Personalkosten: Wenn Sie im Betrieb Aufgaben erledigen und dafür Personal einstellen müssen, ist es denkbar, dass Sie aufgrund der Automation speziell entwickelter Software weniger zusätzliches Personal benötigen und eine Eigenentwicklung somit wirtschaftlich wird.

Wenn Ihr Betrieb keine Umsätze in solcher Größenordnung generiert, kommt eine professionelle Eigenentwicklung i.d.R. nicht in Frage. Ein Ein-Personen-Betrieb kann sich für drei- bis vierstellige Beträge Progrämmchen entwickeln lassen und diese nutzen und das kann sinnvoll sein, stellt aber keine professionelle Entwicklung in einem Projekt mit einer gewissen Komplexität dar und wird hier nicht erörtert. Es gibt spezielle Software, wie Homepages, die sich auch für fünfstellige Beträge sinnvoll entwickeln lassen. Aber die meisten realen Projekte verschlingen mindestens sechsstellige Beträge oder sind nicht-nachhaltiges Gebastel.

Aufgrund dieser Größenordnung und der hinzukommenden finanziellen Risiken bei Eigenentwicklungen sollte der Fokus wo möglich zunächst immer auf dem Kauf existierender Produkte liegen. Allerdings wird sehr viel Software (z.B. Buchhaltungs- und ERP-Systeme) auch individuell angepasst. Dies kann durchaus ähnliche Kosten wie eine Neuentwicklung erzeugen, sollte sich dann aber durch einen höheren Funktionsumfang auszeichnen. Bei solchen Produkten ist darauf zu achten, dass man sich nicht von einem einzigen Anbieter abhängig macht, da auch dies regelmäßig in Kostenfallen führt.

Make

Ist die Entscheidung für eine Eigenentwicklung gefallen, stellt sich die Frage, wie genau das Projekt umgesetzt werden soll. Auch “Eigenentwicklung” bedeutet bei näherer Betrachtung, dass existierende Systeme an spezifische Bedürfnisse angepasst werden – nur sind die Freiheitsgrade hier größer als bei der vorgenannten Anpassung existierender Produkte. Keine Entwicklung findet heute im Luftleeren Raum statt, da die Kosten sonst unüberschaubar wären.

Kleine und mittelständische Unternehmen befinden sich hier in einer besonders schwierigen Situation. Denn ein Großteil der populären Softwarelandschaft, die auf ihre Bedürfnisse angepasst werden soll, ist sehr spezifisch und tiefgehend für die Bedürfnisse sehr großer Unternehmen optimiert.

Die verfügbaren Entwicklerinnen kennen und schätzen die Software, die (oft von und) für Großunternehmen entworfen wurde, für große Teams, komplexe Cloud-Umgebungen und komplexe Entwicklungs- und Betriebsszenarien. All diese Komplexität schlägt sich auch in den verwendeten Softwaresystemen nieder und ist langfristig für kleinere Unternehmen teurer Ballast, der gepflegt werden muss und Kosten verursacht, ohne im kleineren Unternehmen die gleichen Einsparungen zu ermöglichen wie in den großen Teams großer Unternehmen.

In den meisten Fällen läuft das darauf hinaus, dass auch für kleine und mittelständische Unternehmen jeweils ein gerade populäres und gefühlt modernes System angepasst wird. Solche Moden halten bestenfalls fünf bis zehn Jahre. Und wenn die eigene Modesoftware aus der Mode gekommen ist, wird sie ein teurer Klotz am Bein, der sich nur mit erheblichen Kosten oder durch Ignorieren aller Sicherheitsbedenken weiter betreiben lässt.

Nachhaltigkeit

Dies besser zu machen erfordert eine Reihe von Strategien, die unterschiedliche Aspekte der Entwicklung und des Betriebs von Software betreffen. Da es sich um ein sehr vielschichtiges Problem handelt, werde ich den Prozess von Entwicklung und Betrieb nachhaltiger Software in Phasen teilen und zu jeder Phase einen eigenen Artikel verfassen.

Eine dieser Phasen habe ich in diesem einführenden Artikel bereits behandelt: Die Entscheidung zwischen der Umsetzung mit einem existierenden Produkt oder als Eigenentwicklung. Schon in dieser Phase ist ein gewisses technisches Knowhow hilfreich, welches in der nächsten Phase essentiell wird:

  1. Make or Buy
  2. Vorbereitung
  3. Technologieauswahl
  4. Ausschreibung
  5. Entwicklung
  6. Betrieb

minimal stack

My minimal stack and approach for writing professional single page applications (SPAs).

Introduction

When writing a web application, one has to choose from a vast collection of options. You may deploy it as PHP enriched with some jQuery (don’t laugh, that’s probably the majority of contemporary web apps).

PHP usually means server side rendering. I opt for single page applications instead, because in my experience it is a similar effort but if done right, you end up with a superior architecture, better user experience and better maintainability. But the expertise required for getting this right is quite different and in some regards goes far beyond what’s required for server side rendering.

A major point to consider is that you’ll need a server API if you want a SPA. API design goes wrong more often than not. So if you are unsure and don’t have somebody with the required experience, go for server side rendering.

Modern SPA development usually involves pulling in hundreds of thousands or millions of lines of third party code. This is no exaggeration. Webpack is the go-to solution for building and bundling. Its latest release as of this writing is a 20 million byte download. That’s millions of lines before you even started to choose a framework.

The problem with all that third party code is: you use it, you own it. That means you will have to invest effort into keeping your third party code patched and running. A couple of years down the line this effort will likely become significant. It also means that in that future you’ll need to find developers willing to work with your dusty weird code base instead of the hot new stuff others are having fun with.

If you are unwise and unlucky enough to choose third party code that becomes abandoned upstream, you’ll really own it – you’ll not merely have to patch it, you’ll have to develop these patches. If that codebase is substantial, you’re toast.

Thus the best third party library is the one you did not include – just as your best code is the code you never wrote. However, that latter wisdom does call for library code. Best established practice has you organize your code in a certain way and without using any library that means you’ll write a considerable amount of repetitive boilerplate. I don’t want that, it’s bad.

Over many years I went from pure vanilla via libraries and frameworks to my current compromise. The rest of this article lies out the principles on which I build my current approach. I hope it enables you to figure out your own minimal stack.

Two Kinds of Code

Actually more than two, but for simplicity’s sake: you’ll write user facing code that runs in the browser (tier one code). You’ll also write code for building, testing, deploying and whatnot this code (tier two code). You don’t control the environment (browser) in which the user facing code will run. You do control the environment that the latter life-cycle automation and management code will run in. Thus the former code should be held to stricter standards than the latter.

One of my guiding principles is: keep my tier one code standard conforming. It’s code that a modern browser can execute without any ado. That means, the tier two code is – for a large part – exchangeable!

For tier two code I try to restrict myself to stuff that is either exchangeable or such a widespread standard, that chances of it being abandoned in the next decades is minimal. In web projects NPM is such a standard. Thus life-cycle management happens as far as reasonably possible in NPM scripts (calling shell scripts or gulp if they become too complex).

Here’s a list of the NPM tier two dependencies in a project I’m currently working on:

	"devDependencies": {
		"@rollup/plugin-node-resolve": "latest",
		"browser-sync": "latest",
		"chai": "latest",
		"chrome-coverage": "latest",
		"eslint": "^8.4.1",
		"eslint-config-google": "^0.7.1",
		"eslint-plugin-html": "^1.7.0",
		"jsdoc": "latest",
		"jsdoc-to-markdown": "^4.0.1",
		"mocha": "latest",
		"mocha-headless-chrome": "git://github.com/schrotie/mocha-headless-chrome.git",
		"rollup": "latest",
		"rollup-plugin-terser": "latest"
	},

These do 5 things:

  1. provide a development server (run the code) browser-sync
  2. lint the code eslint-*
  3. build the code rollup*
  4. test the code mocha* chai & chrome-coverage
  5. document the code jsdoc*

1-3 are exchangeable and require very little configuration. Should they become obsolete/abandoned it’s very easy to skip or replace them. 4 and 5 are the most widespread standard tools for what they do. Should Mocha become obsolete, that would be a major pain in the behind, but it is rather unlikely to happen in the foreseeable future. I’ll get into more detail about testing below. JSDoc has been around this whole century, yet it feels slightly more obscure. Still the best available doc-standard to my knowledge.

require import

If you are still using require, that’s a hallmark, that your code is soon going to be legacy. You absolutely should be using Ecma Script modules with import by now. Personally I’ve been using import for years now, and I’ve been using it without a build step, and you should, too!

It has been a minor pain to do that, because one needed to use full qualified paths everywhere, which comes with its own set of maintainability drawbacks. It was still better than using require or a build step, as far as I’m concerned. Note: you are certainly going to use a build step for production, see below. However, now you can also use nice module names and paths in Chrome and Firefox, and very likely stick to the future standard with importmaps.

For my liking skipping a build step during development is a major gain. It gets a huge layer of complexity out of my way during development and it speeds up development. With something like browser-sync and without a build step, iteration/feedback is instantaneous. I know and love stuff like vuex state persistence. But in my experience, this can usually be trivially mocked in development and the speed and simplicity of working without builds and codemaps is much preferable to me.

One significant drawback is, however, third party dependencies often do not yet play well with this approach. The situation is improving, but as of this writing, if you tried this approach, you would find yourself throwing out some dependencies because they are too difficult to get running, and considerably bloat your importmap in order to still get others to work.

If you are me, though, this is manageable, because you radically restrict the use of third party dependencies in tier one code anyway.

no God Framework

God frameworks are big frameworks that do everything for you (Angular, Vue, React and others). I got the term from this excellent article, which I also recommend to persuade yourself, that you may want to avoid them. Also consider this excellent and balanced analysis.

I’d like to add one essential point, though, that the aforementioned article misses, because it concentrates on technology: the problem that god frameworks solve is not a technical problem, but an organizational problem – they drastically limit the degrees of freedom of implementing your project. Thus they get all involved developers on track, reduce the need for communication, improve coherence and high level structure of a project.

All of these are good things! If you do not have a good organization, good communication, if you are unsure about how to structure code, by all means, choose a framework. If you don’t you’ll have to do this work yourself. It is not more work, you’ll have to do, but you better know what you are doing. Skipping frameworks increases the number of ways in which you can fail. However, when you succeed, you’ll be rewarded by a more fulfilling job – because of a much improved feeling of self agency – and a superior result.

The platform is getting better and better, continuously reducing the need to resort to any third party or library code at all. However, there is a few things, where you should resort to library code.

API microservices are mostly pretty trivial. What you need there (if you are not coding in Go – its standard library has you covered), is a request router to help you structure your code in a transparent reproducible way. Try to find something that does routing and nothing else. I wrote prouty because it allows me to write very concise modern code, but that’s a matter of taste. Just find a router or write one yourself, it’s no magic. You also need a DB driver. That’ll mostly be determined by the DB you use.

The frontend needs two things: state management and DOM manipulation. If you work on a somewhat complex SPA, just use Redux for state management. It’s minimal, completely independent from React and does its job. Redux is as good as popular libraries get.

On a side note: Redux’ value lies in nudging you to write stateless state manager code. What sounds like an ironic remark is a major gain in maintainability. If you are not familiar with functional programming and/or if you are not used to think in terms of stateful/stateless code, I suggest to rather use a framework and familiarize yourself with these concepts before embarking on min-stacking.

If your software is not that complex, Redux will add considerable complexity overhead. I wrote xt8 as a minuscule toolkit for putting together your own state management. But again: just choose or write some state manager that suits your needs. If you know what you’re doing, writing your own alongside your project is also perfectly fine.

DOM manipulation is not so much about manipulating DOM as it is about structuring your code. This is a pattern that repeats all through this section as you may have noticed. The DOM is organized in a tree, and from looking at the DOM it should be easy to find the place in your code, that deals with that DOM. That’s about it.

Nowadays this job is usually taken by the data binding facilities of your favorite god framework. But you can also do this yourself, just do it the same way anywhere in order to remain maintainable. Cut your DOM up into web components (likely without shadow DOM) and use these for structuring. I wrote bindom for my own needs and it does structuring and data binding at pretty low cost and non-intrusively.

With all the tools I mentioned above, my personal tier one stack clocks in below 2K lines of code for front- and backend, gives me great structure, great performance and no bloat. This makes a world of difference. With this stack I’m at least a freaking order of magnitude below the code size any of the shelf framework can offer – probably it’s more like two orders of magnitude and possibly even three (if you go full angular and stuff …).

This is a huge gain and for that it is okay to compromise. 2K lines instead of 200K means you don’t have to worry at all about whether that code will be maintainable ten years from now. Sure it will be. The stack becomes abandoned? Just own it, 2K lines will be less than 10% of your whole app-code, even if your app is on the simpler side. Only real simple stuff will be on par with 2K lines of code, and in that case you don’t need to worry about structure and should absolutely go full native without any helper libraries.

Quality Assurance

Professional software comes with automatic quality assurance. If you deliver anything without automatic QA, you have not done your job and you wasted your time and your clients money. Simple as that. And because that is so fundamentally true, don’t let your client or anybody tell you, you don’t need automatic QA. That is your decision not theirs, you should be qualified to decide that, not them. Take pride in your work, deliver quality and the tools to prove it.

The most basic kind of JavaScript QA is a linter. This is an exchangeable tool, so don’t ruminate over it, just choose eslint 😉 Whatever – since you should have at most a few linter specific comments in your code, swapping it out for something else later should be doable. Just choose a linter and make good use of it.

Then there’s unit tests. Mocha is the most common test runner and it also underlies other tools. Thus Mocha is a good bet for a test runner. The runner may be your most critical third party software choice. The reason is that much of your test code will be more or less specific to your chosen runner and exchanging the runner will imply a major test rewrite.

I also use Chai for assertion in Mocha tests. Chai is pretty popular but the case for Chai is not as strong as the case for Mocha. You can go without an assertion library without excessive overhead, while going without a runner is less advisable.

Look for clear structure and good reports in your runner. Unit tests are also an essential part of your documentation because the tests articulate very precise expectations for your code’s behavior. Thus you want to be able to find the test for a given part of code.

I found that aiming for a unit test coverage of 95%+ is realistic and sensible. You really want most of your code tested but there are cases, where it’s not worth the effort (like browser specific code and some integration code and sometimes error handling).

The key for getting good coverage with reasonable effort is writing your tier one code to be testable. All code in your state manager should be trivially testable. It should be stateless and utterly DOM-independent, so just pull it in and run it. Your DOM manipulation code should ideally be mostly setting bound (as in data-binding) variables, then testing that is also trivial. This leaves little code that’s more effort to get to.

I usually write mocha tests that run in the browser. That way I can rely on the browser’s debugger when developing my tests. But tests should also run in automatic pipelines where the test report should be logged and evaluated in the console. I use mocha-headless-chrome for this. I can thus use the exact same tests for debugging in my favorite browser and in the command line in pipelines anywhere.

Ensuring that the unit tests cover the code sufficiently should also be automatically enforced. I found that only browsers reliably ship the newest features I tend to use and I want my code to be unit-testable without polyfilling and/or transpiling – thus I can also enjoy minimal stack complexity when developing tests.

However, that means test coverage has to be determined in the browser on the raw source code. That is not something that istanbul – the goto coverage solution – does. However, Chrome comes with built in coverage reporting. I modified mocha-headless-chrome to allow me to extract Chrome’s coverage report from its runs. I also wrote a collection of simple tools to work with Chrome’s coverage report.

This setup allows me to significantly reduce the complexity of my test-environment (as compared to e.g. instanbul). At the same time I get superior coverage evaluation and reporting because Chrome’s report catches unexecuted statements in lines that have executed statements, too. My own coverage report that parses Chrome’s report is superior to Chrome’s own report inside its dev-tools, which has some problems. Mine does not account for source maps, though.

Should you need coverage reporting for node.js projects, you should look into C8 or something like it. It gives you configuration free exchangeable coverage reporting with a lot less complexity than istanbul (since you use node anyway).

In most cases you also want integration and possibly end to end tests for your frontend (and the latter also testing your backend). There are several frameworks for this (here you absolutely need a framework) and I recommend Selenium. It’s standard and it’s the only widespread solution that I’m aware of, that has wide cross browser support.

Apple’s Safari is the new Internet Explorer and you likely want to test for it. You may also want to test on desktops and mobiles in order to assert the responsiveness of your app. Selenium allows all of this, possibly with the help of something like browserstack.

Writing Selenium tests has also become a lot more straightforward with the advent of async/await. So these days I consider it a reasonable choice. As a runner you can still use Mocha with Selenium.

Build & Deploy

I aim for keeping my JavaScript files below a hundred lines or so, and I belief so should you. But that means even rather small projects usually comprise dozens of files. When there is few fonts and graphics I aim for delivering my SPA as one single HTML file. That way I can occasionally just send the file to stakeholders for review without them requiring a server. Again: minimal complexity.

I any case production frontend code should be comprised of few files, thus a build/bundling step is absolutely required in the end. But since everything I do runs in modern browsers without build, building is just bundling (and possibly including some extra polyfills) and can be done with an exchangeable toolchain. I chose rollup years ago, when there were less available alternatives. You should just choose a minimal standards compliant ESM bundler that suits you, and have an eye on exchangeability.

I already wrote a bit about JsDoc. I use jsdoc-to-markdown in order to be able to deploy docs in tools like GitLab or GitHub and have them display nicely there. Up to date docs should always be deployed automatically.

Finally the act of deployment is completely dependent on where your pipeline runs and where you want to deploy. I believe everything, including deployment, should also run locally on any developer’s computer just using NPM. That means you try to restrict your use of platform specific tools on e.g. Azure or AWS.

Still, for complete lifecycle management, NPM alone may be too limited, especially if you want to share lifecycle management code between projects. I much prefer writing code for that instead of configuration and thus I gravitate towards shell or gulp for such tasks instead of things like grunt.

Conclusion

One can write professional SPAs and microservices with minimal dependencies in tier one code. The key is leveraging the standard libraries of the browsers and of node/deno/Go/… to their fullest. Tier two code is harder to free of dependencies and I argue that in case of tier two code dependencies are more acceptable.

When you embark on this journey of figuring out your very own minimal stack, you’ll be rewarded with a more fulfilling work and better long term value for your customers.

The 30 Year Web App

How to build to last – the PERI Web Framework

“We built our current ERP which lasted 30 years and we want you to build our next ERP to last for another 30 years.”

The current ERP is built with COBOL and the next one will be a micro service suite with web UIs. My part is mostly the user interface. I’m paraphrasing and condensing the introductory quote from my memory of the original German utterance. Yet that sentence captures the primary and driving inspiration that motivates the work I do for my current customer.

Mind you: I will be writing about somewhat complex web applications here. Those are quite different beasts from your standard web pages (or even shopware). The latter may have a lot of coded bells and whistles. But the former require a level of architecture and code structure that no “mere” presentational page needs.

Eternity Squared

In case you missed the outrage in the introduction: in web development 3 years is the timeframe in which the whole technology stack of web development used to be redefined. Things are just slightly slowing down these days but 30 years is still, for a web user interface, eternity squared.

So how do you even attempt that? This article describes my take on building web tech to last. Only time may tell whether my approach to this problem is worth a dime.

However, I believe that some considerations I will be presenting here are well worth considering by many businesses. It is not essential that my approach is great in order for the time thinking about building to last to be well spent. I will keep this article on the PERI Web Framework high level so that non technical folks may have a chance to follow.

Own It

I’m pretty convinced, there is one aspect that is hard to avoid, if your aim is to build for a time frame of decades: You own it. All of it. If you now pull in megabytes of libraries, 20 years from now you will have to maintain megabytes of libraries. And that’s not going to happen. 10 years from now you will have a very hard time finding developers willing to maintain old versions of framework x. And thus, in one fell swoop, Angular, React, Vue, and all their smaller brethren are out.

The Arvo (Angular, React, Vue, and Others; take a second to memorize “Arvo”, it’ll occur dozens of times below) approach does one thing very well: Provide a technological framework and low level technical process to guide a web project to success. We all know, tech projects fail. More often than not. Arvo reduces the failure rate.

The Best, Established Framework

Before I move on, I’d like to mention a related aspect: I evaluated frameworks to use for PERI. The clear winner is React. Still talking about long term viability here.

Angular arrived on its decline. It is still very strong but many indicators point to it loosing significance in the long run. Vue is great and promising but as of this writing it is still too young to tell whether it will keep its appeal and whether its lack of backing by strong business entities will allow it to thrive in the long run. Jquery is technologically 90% obsolete and does not address questions of architecture and code structure. Everything else is too obscure to consider.

So if you think about building to last and are not going to be convinced by what follows, I suggest to have a very good look at React. It is extremely popular, still rising, has strong business support and it has more things going for it in terms of long term viability than mentioned here.

Don’t Fail

So, if your business happens to not be Google, you probably should strive to try to not own Angular. There’s more to it than its eventually inevitable demise: Whatever Arvo you use, you’ll need to update every year or so to keep up with your framework. If you don’t, you’ll have a hard time hiring developers five years from now, that are willing to work with a long outdated tech stack.

You see: web developers maintain their labor value by keeping up with the fast evolving web tech stack. Working on old tech will degrade a developer’s business options over time. And it does not appeal to most.

However, in all likelihood, there will be little slack in your project. Deadlines will keep approaching and updates will be missed if you are not very persistent about them. If you keep missing updates the cost of updating will increase and you may fast enter a vicious circle that ends in your code becoming obsolete long before the ultimate demise of your chosen Arvo.

Arvos shine at initial success yet are mediocre at best at long term maintainability. Still, we will need to replicate that initial success in order to get to the long term part that this article is about. So what is it, that raises the success rate of Arvo projects?

Structure & Productivity

It’s two things, actually: structure and productivity – though both end up being two sides of the same coin. “Structure” is part of the very definition of a framework as opposed to a library. A framework imposes structure on your code. You can still pretty much do whatever you like, but with a given Arvo the “natural” (and in many cases pre-documented) way of doing something will likely be less fatal than what your average rookie developer will do without the Arvo.

“Fatal” here means just that: fatal for your project. No line of code and no hundred lines of code are ever fatal. But if the bad-decision-ratio in your whole code base exceeds a certain threshold, it’s better to start clean. Arvos improve the bad-decision-ratio developers make while implementing your project.

Developers are also initially more productive using an Arvo, and that is the reason they generally approve of employing one. However, the structural quality of a project’s code base determines the long term productivity.

If bad decisions keep piling up, changes and new features become ever harder and slower to implement – up to a point where development is deemed exceedingly expensive or slow and the project fails. Thus short term productivity is the initial driving appeal of Arvos while the structural superiority they impose drives the long term productivity and with it the project success.

In other words: we’ll need something that boosts initial productivity in order to drive adoption and keep our developers happy, but we really need to impose great structure in order to ensure long term productivity and ultimately success in the long run.

Initial Productivity Deemed Essential

With regards to initial productivity you may doubt that it’s a necessity. You are building your project and you are hiring your developers and you can just tell them to use your great framework. And they will.

However, they will also take shortcuts. If you want to keep things reasonably simple, you can just not technically exclude the possibility of shortcuts. And if you maintain any kind of pressure or just expectation on development, developers will take shortcuts. Always.

Thus initial productivity is also our lever to keep the number of shortcuts down. If developers are more productive doing things the right way than doing it any other way, then they will more likely do it the right way.

This intimate connection between productivity, structure, and long term success raises the bar for replicating Arvo’s initial success quite high.

KISS

Above I scrapped Arvo for long term pushing too complex a code base into our ownership. So obviously, whatever we do – maybe you find some obscure project that fulfills all requirements, maybe you end up shipping your own or stitch something together from various projects – we must Keep It Simple, Stupid, i.e. we must adhere to the KISS principle.

This is something else to consider about Arvos: They are built to be able to cope with everything. Angular was developed by Google for its vastly complex Google web apps, React was developed by Facebook to drive Facebook. All successful Arvos are capable of driving the most complex Apps and come with modules to address the most obscure recurring programming problems.

The structure they impose (if you go for something like Redux/Vuex as you absolutely should) errs safely on the most complex side. That’s because they need to cover Facebook. If you are not developing Facebook but just some slightly complex ERP, that structure is clearly over the top overengineering, resulting in reduced productivity.

You built it or you just simply use it (for thirty years) you ultimately own it. Thus it is well worth a significant up front investment to arrive at the simplest thing that could possibly do. The PERI web framework is about 2K lines including extensive documentary (API reference) comments. This is well inside the scope a medium-sized non-software-focused company can maintain.

4 Kinds of Code

Whether you go full Arvo, full custom or something in between: you’ll end up with 4 kinds of code that you should evaluate differently. When you go Arvo, 2 of these kinds of code will come from the Arvo. I’ll go over them in the order of their level of criticality, most critical first.

The web framework code is the customer facing code that will run in all the apps you will develop. It will run on a plethora of browser engines over the decades, facing countless individuals on their screens, AR glasses, retina implants …

If that code breaks because of some future incompatibility – as it likely eventually will – then all your apps break. You must hold that code (whether it comes from an Arvo or from yourself) to the absolutely highest standards of quality and maintainability. It must be small, very intelligible, well documented, thoroughly tested – altogether nothing short of great.

The next kind of code is the actual customer facing implementation of your apps on top of the framework. This will be the bulk of the code that you’ll need to worry about. This code is also the Raison d’Être for the other three kinds of code. It may thus be your primary concern most of the time.

If that code breaks – it will; the same applies as for the framework since it will also run on countless yet unknown platforms – one application breaks. If you use the same idiom in several apps all of these will break.

While you may be more lenient with regards to its quality, this will still constitute the bulk of the code you absolutely must keep running. Thus the absolute maintainability of the whole thing is essential and exceeding thresholds of maintainability equals failure.

The third kind of code is test code that automatically tests the first two kinds of code. Furthermore there potentially are several different kinds of test code, but for the sake of this discussion subsuming them as one will suffice.

The volume of the test code should be in the same order of magnitude as the volume of the former two kinds of code. However, the complexity of test code is usually much lower than that of the implementation code.

If the test code breaks, you’ll usually lose your ability to deploy new versions of your software. But you are the master of the environment where the test code runs. Thus – while tedious – you may set aside a dedicated software environment for that code to run for the next three decades. You will not have to react to unexpected breakage immediately.

The test code must still be maintained. It will become more important with time. It constitutes the ultimate documentation of the original intent of the tested code. It will be absolutely essential for developers 20 years from now maintaining any reasonable level of productivity when addressing new features or problems in the above two kinds of code.

In short you must maintain the test code along with anything else. But due to its lower complexity and due to the fact that you have absolute control over its execution environment, you may be more lenient with regards to quality here, than with the two former kinds of code.

The fourth and final kind of code to worry about is code that you will use throughout development but that is not itself part of your projects: the build scripts for your apps, the test environment, the development server for web developers and so on. This code may come from an Arvo, but you’ll still interact with it, be it by configuring your build, injecting mock data into the development server, or running your test suite.

If that code breaks, development slows down or comes to a halt. You somewhat control the environment it runs on. However, if it is less demanding with regards to its environment, productivity will profit.

If parts or the whole thing break for good, you can swap it out completely. While this will be a pain, you will likely do it eventually as new, better tech becomes available.

For these reasons, with regards to the fourth kind of code you have more freedom than with the others. You may pull in a plethora of dependencies in order to boost developer productivity. If stuff breaks, productivity may suffer intermittently but breakage here does not equal failure.

∑ Premise

Congratulations! You just made it through the introduction – almost. Let me sum up what we learned about replicating Arvo’s initial success while maintaining long term viability of our code base. The framework we are looking for must fulfill these requirements:

  1. It must provide an initial productivity boost.
  2. It must impose a good structure on the code written on top of it.
  3. It must be as simple a possible.
  4. It must adhere to different standards of maintainability depending on which kind of code we are talking about.

Sounds great, right? Well, without further ado: let’s do this!

Structure, what Structure?

I’ve been yammering on about good code structure without going into any specifics what that might be. The first thing to note here is that virtually any structure is better than no structure. “No structure” is one endless line of code without functions, just one abominable line of spaghetti. Be aware that any program can in principle be written that way and that by default much too much code actually looks like that – with the minor bonus of some whitespaces having been replaced by newlines.

There is a really simple remedy for this, an easy management win: lint. A linter is a piece of software that checks code for various criteria. “Eslint” does this for EcmaScript/Javascript for example. So put a linter into your continuous integration chain and let a code commit fail if it does not adhere to your coding standards.

You should have a good look at what your linter offers and decide with the team, what to enforce. But one thing you’ll want to enforce for sure is: structure. So have a reasonable limit on line length (e.g. 80 characters), a reasonable limit on function/method length (e.g. 10 lines) and a reasonable limit on file length (e.g. a hundred lines). I recommend liberally permitting exceptions to these rules but in general enforcing them.

Your colleagues/developers may complain that these limits are too low. Trust me, they are not. These limits are 90% of your code documentation and thus extremely valuable for your long term success. You will also employ the good coding practice of using good speaking names for variables, functions, and files.

A developer worth her money will quickly find a well named function in a well named 100 lines file in a well named directory  and she will merely need seconds to roughly understand what that ten reasonably short lines of code do. No outdated comments or documentation required in 99% of the cases.

Divide & Conquer

The above mentioned method of enforcing any structure is not directly Arvo related. What will be discussed next goes to the very heart and soul of Arvo, though. However, in order to understand what this is about, we must discuss a coding problem very specific to user interface programming.

Your web application consists of user interface components (aka widgets) like dialog windows. You will want your dialogs to have a consistent appearance and behavior (i.e. user experience or UX) across your applications. Thus you will re-use code that delivers consistent UX with each use of a dialog regardless of its content. You will inevitably end up writing at least some custom widget code.

Widgets constitute one (reusable) kind of application code. Another kind is the actual application logic or business logic of the app. These two kinds of code are vastly different beasts. Widget code directly interacts with the huge browser APIs for working on how things look and what the user does with the UI. Widget code is usually re-usable. Business logic is pure custom JavaScript code. It is usually not reusable.

However, business logic ties together several widget APIs and other kinds of APIs (like communication, state management and so on). 

Trouble is: (Widget-)APIs change and such changes break apps. Now almost any Arvo employs the same simple principle that does not solve but significantly alleviates this problem: Data Binding.

Data binding means: when a developer changes a (state-) variable in his business logic code, that change will “automatically” be reflected in the visualization (widget). And changes a user triggers in the visualization will “automatically” trigger specific code of the business logic.

This magic trick of data binding in itself fulfills the two essential requirements we identified above: it makes the developer’s life easier, i.e. provides an initial productivity boost, and it separates business logic from UI logic, i.e. it imposes superior structure.

The latter is achieved by redefining the API that developers use for interacting with the DOM (i.e. visualization). Instead of calling various functions with more or less complex arguments, all they ever do is change variables and register callbacks. How variables influence the DOM and vice versa, and how the DOM triggers callbacks is defined where the DOM is written down.

By defining a parsimonious yet somewhat comprehensive DOM-API, Arvo significantly reduces the pain caused by Widget-API changes. Since all you ever do is change single variables, it is most of the time easy to keep existing API calls working while adding new variables on you widgets, thus extending your API without breaking existing stuff.

Put another way: Arvos impose superior structure by defining a relatively small universal DOM API and thus significantly improving separation of business logic from visualization code.

Bindings

Replicating this may seem like an ambitious proposition. It is indeed not trivial to get this right and requires significant experience. Yet it is less of a feat than it might seem.

The browser’s DOM APIs show you the way. There is grand total of six ways of interacting with the DOM:

  • change DOM attributes
  • change DOM properties
  • change DOM text
  • change DOM elements
  • react to DOM events
  • call DOM methods

For your data binding needs it is totally sufficient and indeed wise to restrict yourself to four of these: attribute, property, and text changes and reacting to DOM events.

Attributes, properties and text lend themselves to plain two-way data-binding, i.e. you’ll want your framework to keep them in sync with bound state variables.

I strongly suggest following the proven React philosophy of bindings-down/events-up. That means that you use data bindings to change the state of the visualization and prefer (DOM-) events to trigger business logic code when things happen in the visualization.

It does make sense, though, to bind atomic values of form fields to business logic variables and have those changes trigger code along with updating the bound variable. This will save quite some typing and does not ruin the code structure. However, if you have changes (especially in more complex widgets or nested sub-modules of business logic) then don’t propagate those upward through bindings, but use events instead!

As for the binding syntax I strongly oppose the almost universal approach of whisker bindings. The reason for this is, that it introduces magic right into the DOM itself. Very few developers understand how whisker bindings work. Using stuff that few developers understand is bad now for many reasons and will be worse decades down the line, when the syntax may be forgotten or (which may be even worse) has become part of some standard.

It is my strong conviction, that the DOM that a developer writes should be plain and valid standard DOM. No strings attached. The reason for this is: your DOM will thus be plain and valid standard DOM for decades to come and developers not even born yet, will be sure to understand what it says – without reading decades old documentation of long dead frameworks.

That approach leaves you with precisely one (sensible) way to express your binding syntax: put it in (custom-) attributes. I chose to use one universal attribute: data-bind. Therein one or more bindings can be expressed, the syntax clearly shows the direction of the binding (i.e. from:to) and by prefixing with special characters expresses what kind of binding we have. 

data-bind=$variable:@attribute;.property:$variable;$variable:§;methodName(domEventName)

Note that this is valid HTML without quotes, with quotes you could use line-breaks instead of semicolons in order to improve readability. The paragraph (§) character indicates binding to the element’s text.

This is really all you need. I added a very little bit on top of this, but the most important thing when building to last is KISS!

Widgets

We are still missing two DOM interaction modes: changing DOM elements and calling DOM methods. You may disregard them in your data binding, but you will still need to use them occasionally. So if you need to get down to that nuts-and-bolts level of DOM interaction: write a widget.

For this you do not need any framework because the web standard defines everything you need to write widgets. They are called web components. I’ve written about those in previous articles, so I’m not going to repeat that here.

A tiny DOM manipulation helper will come in handy though (i.e. boost productivity). I use ShadowQuery about which I have also written extensively already. ShadowQuery can also provide 50% of the reusable code you need for data binding.

I learned – over many years of working with web components – one fundamental lesson about web components: don’t use shadow DOM but do use custom built-ins.

Shadow DOM is an extremely powerful feature of web components and it makes a lot of sense, too. However, if you are writing your own suite of web applications (or even just one sizable app) steer away of shadow DOM. It’s more trouble than it’s worth. Shadow DOM is designed to write universal widgets that are independent of a given (suite of) applications and will be re-usable across companies. This is likely not your primary concern. Shadow DOM also has unresolved issues with regards to styling/theming.

Custom built-ins on the other hand allow you to add custom functionality to existing HTML elements like <input>. This is extremely valuable. In particular it allows you to add custom behavior to the HTML <template> element.

I won’t go into details here, but a customized template (along with your data binding) is all you need for conditional rendering (i.e. much of the missing “change DOM elements“ part) and rendering arrays right from your business logic. It also gives you dialogs and more.

If you take this route of custom-built-ins-but-no-shadow-DOM then you will have an easy time even supporting legacy browsers like Internet Explorer. The reason for this is that without the shadow DOM the rest is reasonably easy to polyfill. And the resulting DOM structure can easily be debugged in IE (which shadow DOM cannot).

Scope

Your business logic should be compartmentalized in all but the most simple apps. Remember: Divide & Conquer! And one business logic module should have a definite DOM binding scope. That means if you nest business logic modules inside of each other, you absolutely do not want parent modules to change anything in the nested module’s DOM – and vice versa.

Luckily the DOM tree yields all the structure you need here. I implemented binding in special <peri-bind> widgets and attach business logic scopes to binding elements through special attributes. Thus the DOM tree automatically yields sensible and self explaining binding scopes. As a bonus the binding syntax isn’t universal but limited to where you use the special binding elements. This makes the whole concept even more self explaining to developers to come.

For business logic modules I recommend using pure EcmaScript classes without providing any direct access to the bound DOM. That way developers are strongly incentivized to follow the division into business logic and widgets.

Thus your business logic code will be much easier to unit test. The reason for this is, that you can mostly disregard complex DOM APIs and just call the methods of the business logic modules and check return values/state changes. Better, simpler and more comprehensive unit tests constitute a huge value down the decades.

This approach also yields another level of code structure improvement: the bound variables of the business logic classes constitute the module’s state. Thus you gain an implicit separation into model (state variables), view (widgets), controller (business logic) with a dead simple API.

Tooling

In olden days you could write an HTML file, have it load some CSS and JavaScript from separate files and run that in your browser without any tooling. Today the common practice is to have some build tools between your code and the browser. The reason for this is that many frameworks do not work with valid code but translate your framework specific code to something valid. Obviously I strongly advise against this practice. Exclusively write valid standard conformant code. Only that has a chance to be intelligible in the long run.

EcmaScript modules (i.e. import, export and friends) are supported by modern browsers and allow you to load your raw modular code into your development browser and have it run as intended. For legacy browsers you can do whatever build steps you like, and for deployment some minification, bundling, gzipping and possibly polyfilling is certainly advisable.

∑mary

So that’s it:

  • reduce the amount of code running in your user’s browsers at all significant cost
  • optimize structure of said code
  • provide simple data binding for your developers
  • push for clear separation of business logic from visualization code (and ideally app state)
  • leverage standard technology, then leverage it some more, and then some
  • wish me luck 🙂

I am thankful to PERI for giving me the opportunity to implement a framework built to last.