<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>bitbetter blog</title><description>Wir sind zwei Software-Entwickler aus Hamburg und lösen Probleme im Bereich der Digitalisierung: Making the world a bit better.</description><link>https://bitbetter.de/</link><language>de-DE</language><lastBuildDate>Sat, 18 Apr 2026 14:44:29 GMT</lastBuildDate><generator>astro</generator><item><title>Ein Kirby-Theme für Fab Cities</title><link>https://bitbetter.de/blog/projektvorstellung-fab-kirby/</link><guid isPermaLink="true">https://bitbetter.de/blog/projektvorstellung-fab-kirby/</guid><description>Unser React-basiertes Starterkit für das Kirby CMS hilft sogenannten Fab Cities und Fab Regions dabei, ihre Webseiten selber zu bauen. Dieses Projekt ist das Ergebnis vieler vorheriger Projekte.</description><pubDate>Wed, 08 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In den letzten Monaten durften wir die Webseite der &lt;a href=&quot;https://www.fab-bergisch.org/&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;Fab Region Bergisches Städtedreieck&lt;/a&gt; erneuern. Dabei haben wir existierende UI-Komponenten mit einem modernen CMS verbunden, um eine nutzerfreundliche und flexible Open-Source-Lösung zu schaffen.&lt;/p&gt;
&lt;p&gt;Unser Kunde, das &lt;a href=&quot;https://www.cscp.org/&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;Collaborating Centre on Sustainable Consumption and Production (CSCP)&lt;/a&gt;, beteiligt sich und fördert die Initiative “Fab Region Bergisches Städtedreieck”. Diese ist Teil der weltweiten &lt;a href=&quot;https://fab.city/&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;Fab-City-Initiative&lt;/a&gt;, die sich zum Ziel gesetzt hat, Städte nachhaltiger und widerstandsfähiger zu machen, indem sie lokale Produktion und Kreislaufwirtschaft fördert. Die Fab Region vernetzt Akteure aus den Bereichen Bildung, Wirtschaft, Politik und Zivilgesellschaft, um gemeinsam an einer nachhaltigen Zukunft zu arbeiten. Durch unsere Arbeit für die Fab-City-Initiative in Hamburg sind wir mit dem Konzept bereits gut vertraut.&lt;/p&gt;
&lt;figure class=&quot;&quot;&gt;p&gt;img]:mx-auto&quot;&gt;&lt;img src=&quot;https://bitbetter.de/images/blog/fab-kirby/fab-region-kirby-panel.jpg&quot; alt=&quot;Ein Screenshot des Kirby Admin Panels der neuen Webseite&quot;&gt;&lt;/figure&gt;
&lt;h2 id=&quot;vorarbeiten-ui-komponentenbibliothek-mit-react&quot;&gt;Vorarbeiten: UI-Komponentenbibliothek mit React&lt;/h2&gt;
&lt;p&gt;Bereits 2022 hatten wir für die &lt;a href=&quot;https://www.fabcity.hamburg&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;Fab City Hamburg&lt;/a&gt; eine &lt;a href=&quot;https://gitlab.fabcity.hamburg/fcos-suite/fcos-suite-ui&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;React-basierte Komponentenbibliothek namens &lt;code&gt;fcos-suite-ui&lt;/code&gt;&lt;/a&gt; sowie eine &lt;a href=&quot;https://gitlab.fabcity.hamburg/fcos-suite/fcos-suite-map&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;interaktive “Fab Map”&lt;/a&gt; unter einer Open-Source-Lizenz entwickelt. Im damaligen Projekt wurde diese UI-Bibliothek mit dem statischen Webseiten-Generator &lt;a class=&quot;tooltag inline-flex align-top items-center gap-1&quot; href=&quot;https://www.astro.build&quot; target=&quot;_blank&quot;&gt; &lt;span class=&quot;flex items-center justify-center h-5 [&amp;_svg]:h-full [&amp;_svg]:w-auto&quot;&gt;&lt;svg viewBox=&quot;0 0 63 79&quot; fill=&quot;none&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;&lt;title&gt;Astro Logo&lt;/title&gt;&lt;path d=&quot;M19.4924 65.9282C15.6165 62.432 14.4851 55.0859 16.0999 49.7638C18.8998 53.1193 22.7793 54.1822 26.7977 54.7822C33.0013 55.7081 39.0937 55.3618 44.8565 52.5637C45.5158 52.2434 46.125 51.8174 46.8454 51.386C47.3861 52.9341 47.5268 54.497 47.338 56.0877C46.8787 59.9617 44.9251 62.9542 41.8177 65.2227C40.5752 66.13 39.2604 66.9411 37.9771 67.7967C34.0346 70.4262 32.9679 73.5095 34.4494 77.9946C34.4846 78.1038 34.5161 78.2131 34.5957 78.4797C32.5828 77.5909 31.1124 76.2965 29.9921 74.5946C28.8088 72.7984 28.2458 70.8114 28.2162 68.6615C28.2014 67.6152 28.2014 66.5597 28.0588 65.5282C27.7107 63.0135 26.5144 61.8876 24.2608 61.8227C21.9479 61.7561 20.1183 63.1672 19.6331 65.3893C19.5961 65.5597 19.5424 65.7282 19.4887 65.9263L19.4924 65.9282Z&quot; fill=&quot;currentColor&quot; /&gt;&lt;path d=&quot;M0 51.3932C0 51.3932 10.5979 46.2433 21.2254 46.2433L29.2382 21.5069C29.5381 20.3106 30.4141 19.4977 31.4029 19.4977C32.3918 19.4977 33.2677 20.3106 33.5677 21.5069L41.5804 46.2433C54.1672 46.2433 62.8058 51.3932 62.8058 51.3932C62.8058 51.3932 44.8044 2.47586 44.7692 2.37772C44.2526 0.931458 43.3804 0 42.2045 0H20.6032C19.4273 0 18.5903 0.931458 18.0384 2.37772C17.9995 2.47401 0 51.3932 0 51.3932Z&quot; fill=&quot;currentColor&quot; /&gt;&lt;/svg&gt;&lt;/span&gt; Astro &lt;/a&gt; kombiniert, um eine performante und moderne Webseite zu erstellen. Die Benutzung eines solchen Site Generators ist aber technisch oft kein adäquater Ersatz für die komfortablen Benutzeroberflächen klassischer CMS-Systeme wie Wordpress und Co. Um die Webseite also potenziell auch einem größeren Redaktionsteam ohne technisches Wissen zugänglich zu machen, wollte das Team der “Fab Region Bergisches Städtedreieck” unsere UI-Komponenten mit einem nutzerfreundlichen CMS verbinden.&lt;/p&gt;
&lt;figure class=&quot;&quot;&gt;p&gt;img]:mx-auto&quot;&gt;&lt;img src=&quot;https://bitbetter.de/images/blog/fab-kirby/fab-region-map.jpg&quot; alt=&quot;Ein Screenshot des Kirby Admin Panels der neuen Webseite&quot;&gt;&lt;/figure&gt;
&lt;h2 id=&quot;kirby-als-flexible-und-aufgeräumte-cms-lösung&quot;&gt;Kirby als flexible und aufgeräumte CMS-Lösung&lt;/h2&gt;
&lt;p&gt;Nach dem Abklären der individuellen Anforderungen, haben wir uns dazu entschieden, &lt;a class=&quot;tooltag inline-flex align-top items-center gap-1&quot; href=&quot;https://getkirby.com/&quot; target=&quot;_blank&quot;&gt; &lt;span class=&quot;flex items-center justify-center h-5 [&amp;_svg]:h-full [&amp;_svg]:w-auto&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; fill=&quot;currentColor&quot; viewBox=&quot;0 0 36 42&quot;&gt;&lt;title&gt;Kirby Logo&lt;/title&gt;&lt;path d=&quot;M18 0l18 10.498v21.004L18 42 0 31.502V10.498L18 0zM2 11.693v18.614l16 9.332 16-9.332V11.693L18 2.36 2 11.693z&quot; /&gt;&lt;path d=&quot;M26 21l-5 2.59V24h5v4H10v-4h5v-.437L10 21v-5l8 4.297L26 16&quot; /&gt;&lt;/svg&gt;&lt;/span&gt; Kirby &lt;/a&gt; CMS für diese Aufgabe zu verwenden. Kirby ist ein dateibasiertes CMS, das sehr flexibel und einfach zu bedienen ist. Es bietet eine gute Balance zwischen Benutzerfreundlichkeit und Anpassungsfähigkeit, was es ideal für Projekte macht, die sowohl Redakteure als auch Entwickler ansprechen sollen. Außerdem haben wir mit Kirby in der Vergangenheit bereits viele gute Erfahrungen sammeln können.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class=&quot;tooltag inline-flex align-top items-center gap-1&quot; href=&quot;https://getkirby.com/&quot; target=&quot;_blank&quot;&gt; &lt;span class=&quot;flex items-center justify-center h-5 [&amp;_svg]:h-full [&amp;_svg]:w-auto&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; fill=&quot;currentColor&quot; viewBox=&quot;0 0 36 42&quot;&gt;&lt;title&gt;Kirby Logo&lt;/title&gt;&lt;path d=&quot;M18 0l18 10.498v21.004L18 42 0 31.502V10.498L18 0zM2 11.693v18.614l16 9.332 16-9.332V11.693L18 2.36 2 11.693z&quot; /&gt;&lt;path d=&quot;M26 21l-5 2.59V24h5v4H10v-4h5v-.437L10 21v-5l8 4.297L26 16&quot; /&gt;&lt;/svg&gt;&lt;/span&gt; Kirby &lt;/a&gt; CMS (PHP) als Admin-Backend&lt;/li&gt;
&lt;li&gt;&lt;a class=&quot;tooltag inline-flex align-top items-center gap-1&quot; href=&quot;https://bitbetter.de/leistungen/react/&quot;&gt; &lt;span class=&quot;flex items-center justify-center h-5 [&amp;_svg]:h-full [&amp;_svg]:w-auto&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;-11.5 -10.23174 23 20.46348&quot;&gt;&lt;title&gt;React Logo&lt;/title&gt;&lt;circle cx=&quot;0&quot; cy=&quot;0&quot; r=&quot;2.05&quot; fill=&quot;currentColor&quot; /&gt;&lt;g stroke=&quot;currentColor&quot; stroke-width=&quot;1&quot; fill=&quot;none&quot;&gt;&lt;ellipse rx=&quot;11&quot; ry=&quot;4.2&quot; /&gt;&lt;ellipse rx=&quot;11&quot; ry=&quot;4.2&quot; transform=&quot;rotate(60)&quot; /&gt;&lt;ellipse rx=&quot;11&quot; ry=&quot;4.2&quot; transform=&quot;rotate(120)&quot; /&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/span&gt; React &lt;/a&gt; Frontend&lt;/li&gt;
&lt;li&gt;Verbindung von Frontend und Backend über das &lt;a href=&quot;https://plugins.getkirby.com/tobimori/inertia&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;“Inertia Adapter” Plugin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Unsere interaktive &lt;a href=&quot;https://gitlab.fabcity.hamburg/fcos-suite/fcos-suite-map&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;fcos-suite-map&lt;/code&gt;&lt;/a&gt; kann nun ebenfalls über Kirby gepflegt werden&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Das Ergebnis ist ein Starterkit für Kirby, welches es erlaubt schnell neue Webseiten im gleichen Look und mit der gleichen Funktionalität zu starten. Unser Auftraggeber hat uns außerdem gestattet, auch dieses Startekit unter einer Open-Source-Lizenz zu veröffentlichen (&lt;strong&gt;Achtung&lt;/strong&gt;: Kirby selbst ist ein “Source Available”-Produkt, das heißt, es wird eine – sehr günstige – proprietäre Lizenz benötigt um es zu nutzen).&lt;/p&gt;
&lt;div class=&quot;flex gap-4 not-prose items-center justify-center&quot;&gt;&lt;a href=&quot;https://www.fab-bergisch.org&quot; class=&quot;flex w-fit gap-2 py-4 px-5 rounded-full items-center font-medium hover:bg-opacity-75 bg-black text-white py-4 px-5 &quot; target=&quot;_blank&quot;&gt;Zur Webseite der Fab Region&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 256 256&quot; fill=&quot;currentColor&quot; class=&quot;h-6 w-6&quot;&gt;&lt;path d=&quot;M221.66,133.66l-72,72a8,8,0,0,1-11.32-11.32L196.69,136H40a8,8,0,0,1,0-16H196.69L138.34,61.66a8,8,0,0,1,11.32-11.32l72,72A8,8,0,0,1,221.66,133.66Z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a href=&quot;https://github.com/bitbetterde/fab-kirby&quot; class=&quot;flex w-fit gap-2 py-4 px-5 rounded-full items-center font-medium hover:bg-opacity-75 bg-bb-grey-200 text-bb-grey-500 py-4 px-5 &quot; target=&quot;_blank&quot;&gt;Zum Quelltext auf GitHub&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 256 256&quot; fill=&quot;currentColor&quot; class=&quot;h-6 w-6&quot;&gt;&lt;path d=&quot;M221.66,133.66l-72,72a8,8,0,0,1-11.32-11.32L196.69,136H40a8,8,0,0,1,0-16H196.69L138.34,61.66a8,8,0,0,1,11.32-11.32l72,72A8,8,0,0,1,221.66,133.66Z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/div&gt;</content:encoded></item><item><title>„Macht ihr eigentlich auch Webseiten?”</title><link>https://bitbetter.de/blog/macht-ihr-webseiten/</link><guid isPermaLink="true">https://bitbetter.de/blog/macht-ihr-webseiten/</guid><description>Oft werden wir gefragt, ob wir &quot;eigentlich auch Webseiten machen&quot;. Die Antwort ist ein klares &quot;Jein&quot;. In diesem Beitrag erklären wir, warum das so ist.</description><pubDate>Tue, 13 May 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Wenn wir jemandem beschreiben, was wir bei bitbetter eigentlich tun, dann fallen oft die Begriffe “individuelle Software-Entwicklung” und “Web-Technologien”. Häufig lautet die Rückfrage dann: “Also macht ihr Webseiten?“. Auf diese Fragen haben wir oft keine klare, kurze Antwort. Deswegen gibt es hier nun die ausführliche Erklärung dazu, und warum das gar nicht so leicht zu beantworten ist.&lt;/p&gt;
&lt;h2 id=&quot;was-ist-eigentlich-eine-webseite&quot;&gt;Was ist eigentlich eine Webseite?&lt;/h2&gt;
&lt;p&gt;Bei bitbetter bauen wir (vorwiegend/meistens) Software-Anwendungen, die in einem Browser genutzt werden können. Wenn man “Webseite” so definiert (also als “Alles, was im Browser benutzt werden kann”), dann ist die Antwort eindeutig: Ja, technisch gesehen bauen wir Webseiten. In der Praxis wird der Begriff Webseite aber vor allem für informative, repräsentative Webseiten genutzt auf denen sich Menschen, Unternehmen und Projekte im Internet “nur” repräsentieren. Und genau diese Projekte bauen wir in der Regel eben nicht.&lt;/p&gt;
&lt;h2 id=&quot;warum-wir-keine-einfachen-webseiten-bauen&quot;&gt;Warum wir keine “einfachen Webseiten” bauen&lt;/h2&gt;
&lt;p&gt;Solche klassischen, repräsentativen Webseiten können heutzutage mithilfe dutzender verschiedener Tools und Baukästen sehr einfach und günstig umgesetzt werden dank SaaS-Baukästen wie etwa &lt;a href=&quot;https://www.wix.com/&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;Wix&lt;/a&gt;, &lt;a href=&quot;https://www.squarespace.com/&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;Squarespace&lt;/a&gt; und etablierten CMS-Systeme wie &lt;a href=&quot;https://wordpress.org/&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;WordPress&lt;/a&gt;, &lt;a href=&quot;https://craftcms.com/&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;Craft&lt;/a&gt; oder &lt;a class=&quot;tooltag inline-flex align-top items-center gap-1&quot; href=&quot;https://getkirby.com/&quot; target=&quot;_blank&quot;&gt; &lt;span class=&quot;flex items-center justify-center h-5 [&amp;_svg]:h-full [&amp;_svg]:w-auto&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; fill=&quot;currentColor&quot; viewBox=&quot;0 0 36 42&quot;&gt;&lt;title&gt;Kirby Logo&lt;/title&gt;&lt;path d=&quot;M18 0l18 10.498v21.004L18 42 0 31.502V10.498L18 0zM2 11.693v18.614l16 9.332 16-9.332V11.693L18 2.36 2 11.693z&quot; /&gt;&lt;path d=&quot;M26 21l-5 2.59V24h5v4H10v-4h5v-.437L10 21v-5l8 4.297L26 16&quot; /&gt;&lt;/svg&gt;&lt;/span&gt; Kirby &lt;/a&gt;. Wir &lt;a href=&quot;https://bitbetter.de/leistungen/cms-beratung/&quot;&gt;beraten gerne bei der Planung, Auswahl und Vergleich dieser Tools&lt;/a&gt;, da wir über die vergangenen Jahre mit sehr vielen dieser Tools gearbeitet haben. Jedoch setzen wir einfache Webseiten-Projekte mit diesen Tools nur selten selber um.&lt;/p&gt;
&lt;p&gt;Wir sind also &lt;strong&gt;nicht&lt;/strong&gt; die richtigen Ansprechpartner bei den folgenden Themen:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Aufsetzen simpler, repräsentativer Webseiten&lt;/li&gt;
&lt;li&gt;Implementierung und Installation bestehender Themes und Plugins&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Für solche Tätigkeiten gibt es heute viele gute Agenturen und Freelancer im In- und Ausland, die sich genau darauf spezialisiert haben. Der Informatik-Anteil an diesen Aufgaben ist nur sehr gering und als studierte Informatiker können wir mit besagten Anbietern preislich einfach nicht konkurrieren.&lt;/p&gt;
&lt;h2 id=&quot;warum-wir-doch-webseiten-bauen&quot;&gt;Warum wir doch Webseiten bauen&lt;/h2&gt;
&lt;p&gt;Unsere Dienste schließen jedoch nahtlos an diese genannten Technologien an. Sobald es etwas spezieller oder individueller wird, sind wir wieder die richtigen Ansprechpartner. Beispiele für unsere Dienstleistungen sind:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;🛠️ Individuelle Anpassungen und Erweiterungen von bestehenden CMS-Systemen, z.B. durch die Entwicklung oder Anpassung individueller Plugins&lt;/li&gt;
&lt;li&gt;🚀 Optimierung und Migration bestehender Webseiten auf moderne Technologien und Architekturen&lt;/li&gt;
&lt;li&gt;🎨 Implementierung von individuellen Designs und Themes mit sehr hohen Ansprüchen an Performance, Barrierefreiheit und SEO&lt;/li&gt;
&lt;li&gt;📋 Erweiterung von bestehenden Webseiten um individuelle Funktionen, z.B. für dynamische Inhalte, Formulare, Schnittstellen zu Drittsystemen usw.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Der Hauptteil unserer Arbeit ist jedoch die &lt;a href=&quot;https://bitbetter.de/blog/webapps/&quot;&gt;Erstellung von &lt;strong&gt;komplett individuellen Web-Anwendungen&lt;/strong&gt;&lt;/a&gt;, die über die reine Informationsvermittlung hinausgehen. Wir erstellen also Software-Anwendungen, die zwar im Browser genutzt, aber klassischerweise nicht als “Webseite” bezeichnet werden können. Dazu gehören z.B. Anwendungen für die interne Nutzung in Unternehmen, Software-as-a-Service-Produkte (SaaS) für Endkunden, Web-Portale und Dashboards, Webshops und vieles mehr.&lt;/p&gt;</content:encoded></item><item><title>Wofür die E-Rechnung gebraucht wird</title><link>https://bitbetter.de/blog/e-rechnung-sinnvoll/</link><guid isPermaLink="true">https://bitbetter.de/blog/e-rechnung-sinnvoll/</guid><description>Seit dem 1. Januar 2025 wird die E-Rechnung für bestimmte Bereiche verpflichtend. Aber wofür braucht man überhaupt eine E-Rechnung? Schließlich haben wir alle schon vorher Rechnungen per E-Mail verschickt – wo ist also der Vorteil?</description><pubDate>Tue, 17 Dec 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Jedes Unternehmen in Deutschland muss ab dem 1. Januar 2025 in der Lage sein, E-Rechnungen zu empfangen. Das bedeutet oftmals nervige Updates und neue Systeme. Aber warum der ganze Stress?&lt;/p&gt;
&lt;h2 id=&quot;was-ist-das-problem-an-rechnungen-im-pdf-format&quot;&gt;Was ist das Problem an Rechnungen im PDF-Format?&lt;/h2&gt;
&lt;p&gt;Eine Rechnung als PDF auszufertigen und zu verschicken ist heutzutage gängig. Der Empfänger kann die PDF mit einem PDF-Reader öffnen und so die Rechnung lesen und z.B. eine zugehörige Überweisung tätigen ausführen.
Außerdem ist PDF ja bereits ein elektronisches Format, mit welchem Computer umgehen können – so sollte man meinen.
Dies ist aber leider nur zum Teil richtig. Die Idee des PDF-Formates ist es, dass ein Leser einer PDF-Datei das Schriftstück immer in der Form betrachten und ausdrucken kann, die der Autor festgelegt hat (im Gegensatz zu einem “offenen” Format wie etwa &lt;code&gt;.doc(x)&lt;/code&gt; aus Microsoft Word).&lt;/p&gt;
&lt;p&gt;Eine PDF ist also für einen Menschen bestimmt, weshalb sie optional auch nur aus Bildern bestehen kann ohne überhaupt Text zu beinhalten. Aber selbst wenn eine PDF Textinformationen beinhaltet, so steht bei der einen Rechnung der Absender oben rechts, bei der nächsten Rechnung unten in der Fußzeile und bei der Nächsten ganz woanders. Bei manchen steht außerdem ein “Absender:” davor, bei anderen wieder nicht.&lt;/p&gt;
&lt;p&gt;Wer also einen Ordner mit 100 Rechnungen im PDF-Format nach Rechnungen von einem bestimmten Absender filtern und den Gesamtbetrag ausrechnen lassen möchte, wird dies nur sehr kompliziert und fehleranfällig mit “herkömmlichen” PDF-Rechnungen erreichen.
Besser wäre es, wenn es eine Textdatei gäbe, deren Aufbau klar definiert ist. So dass zum Beispiel immer in der 1. Zeile der Absender stehen muss.
Man wüsste dann, dass der Absender immer in der ersten Zeile einer jeden Rechnung zu finden ist und der Name des Absenders immer nach dem Doppelpunkt auftaucht.
Und genau das ist die Idee hinter der E-Rechnung – ein “strukturiertes, elektronisches Format” gibt uns also vor, wo der Absender, der Empfänger, der Rechnungsbetrag einer Position usw. zu finden ist, damit die Daten einfach und sicher automatisch von Comoutern verarbeitet werden können.&lt;/p&gt;
&lt;h2 id=&quot;aufbau-einer-e-rechnung&quot;&gt;Aufbau einer E-Rechnung&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;xml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;ram:SellerTradeParty&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;ram:Name&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;Meine Firma&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;ram:Name&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;ram:PostalTradeAddress&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;ram:PostcodeCode&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;12345&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;ram:PostcodeCode&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;ram:LineOne&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;abc-strasse&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;ram:LineOne&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;ram:CityName&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;Stadt&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;ram:CityName&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;ram:CountryID&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;DE&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;ram:CountryID&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;ram:PostalTradeAddress&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;ram:SpecifiedTaxRegistration&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;ram:ID&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; schemeID&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&amp;quot;VA&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;DE19990815&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;ram:ID&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;ram:SpecifiedTaxRegistration&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;ram:SpecifiedTaxRegistration&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;ram:ID&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; schemeID&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&amp;quot;FC&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;4711&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;ram:ID&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;ram:SpecifiedTaxRegistration&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;ram:SellerTradeParty&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In Deutschland gibt es momentan zwei führende Formate für die E-Rechnung, einerseits die &lt;a href=&quot;https://xeinkauf.de/xrechnung/&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;XRechnung&lt;/a&gt; und andererseits das Format &lt;a href=&quot;https://www.ferd-net.de/&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;ZUGFeRD&lt;/a&gt;.
Beide Formate basieren auf &lt;a href=&quot;https://de.wikipedia.org/wiki/Extensible_Markup_Language&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;XML&lt;/a&gt;, das bedeutet, beide Formate können mit einem Texteditor geöffnet und betrachtet werden, und ihre Daten sind in sogenannten XML-Tags gruppiert (ein Tag wird von spitzen Klammern markiert, wie etwa &lt;code&gt;&amp;lt;ram:CityName&amp;gt;&lt;/code&gt;). Bei der XRechnung handelt es sich um eine reine XML-Datei (ohne PDF), während bei ZUGFeRD die XML-Daten an eine PDF angehangen werden.
Somit besteht eine ZUGFeRD-PDF einerseits aus einer normalen PDF und andererseits aus der XML, welche an die PDF angehängt wurde, wodurch eine ZUGFeRD-PDF sowohl von Menschen gelesen, als auch von Computern gut verarbeitet werden kann.&lt;/p&gt;
&lt;h2 id=&quot;möglichkeiten-durch-die-e-rechnung&quot;&gt;Möglichkeiten durch die E-Rechnung&lt;/h2&gt;
&lt;p&gt;Die E-Rechnung ist also ein strukturiertes elektronisches Format, wodurch die Daten einer Rechnung (Absender, Empfänger, Positionen, etc.) automatisch erfasst und verarbeitet werden können.
Wenn also bis jetzt die Daten einer PDF-Rechnung per Hand (oder bestensfalls über die optische Texterkennung OCR, z.B. mittels &lt;a class=&quot;tooltag inline-flex align-top items-center gap-1&quot; href=&quot;https://bitbetter.de/leistungen/paperless-ngx/&quot;&gt; &lt;span class=&quot;flex items-center justify-center h-5 [&amp;_svg]:h-full [&amp;_svg]:w-auto&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; xml:space=&quot;preserve&quot; fill-rule=&quot;evenodd&quot; stroke-miterlimit=&quot;10&quot; clip-rule=&quot;evenodd&quot; viewBox=&quot;0 0 456 547&quot;&gt;&lt;path fill=&quot;currentColor&quot; fill-rule=&quot;nonzero&quot; stroke=&quot;#000&quot; stroke-width=&quot;.7598&quot; d=&quot;M100.495 521.09c-2.66-12.538-7.902-37.687-8.586-37.687-111.766-66.862-98.546-182.58-61.544-248.758 7.902 83.35 155.455 140.867 69.446 242.756-.684 1.291 3.951 17.171 7.902 31.76 17.171-29.101 43.005-64.128 41.637-67.47C43.51 183.737 374.25 163.907 443.012 3.817c31.076 154.771-15.88 394.26-281.733 455.12-1.292.683-48.247 83.35-50.299 84.033 0-1.291-19.83-.684-17.171-7.294 1.443-4.027 4.027-9.27 6.686-14.588Zm-3.267-62.152c33.735-39.054-5.927-105.84-29.784-127.647 40.345 69.37 37.686 109.715 29.784 127.647Z&quot; /&gt;&lt;/svg&gt;&lt;/span&gt; Paperless-ngx &lt;/a&gt;) ins Buchhaltungssystem übertragen werden mussten, so kann dies jetzt automatisch (und ohne Erkennungsfehler) passieren. Wie bereits oben beschrieben, kann außerdem über eine ganze Liste von Rechnungen gefiltert, sortiert und zusammengefasst werden. Auch automatisierte Rechnungsprozesse profitieren von der E-Rechnung. Wenn Sie besipielsweise alle eingehenden Rechnungen in einer E-Mail-Adresse sammeln (etwa &lt;code&gt;rechnungen@bitbetter.de&lt;/code&gt;), können automatisch alle Rechnungen erfasst und Überweisungsvorschläge automatisch angelegt werden, die lediglich noch manuell freigegeben werden müssen. Die Rechnung kann dannach automatisch der Überweisung zugeordnet werden.&lt;/p&gt;
&lt;h2 id=&quot;fazit&quot;&gt;Fazit&lt;/h2&gt;
&lt;p&gt;Die E-Rechnung ist noch neu und vor allem im Zusammenspiel mit diversen Software-Anwendungen gibt es noch die ein oder andere Kinderkrankheit – aber es ist ein richtiger und wertvoller Schritt in Richtung einer effektiven Digitalisierung. Die daraus folgenden Möglichkeiten für Automatisierungen können am Ende vielen Unternehmen die Buchhaltung deutlich leichter machen.&lt;/p&gt;</content:encoded></item><item><title>Nextcloud: Verschiedene Freigabearten im Überblick</title><link>https://bitbetter.de/blog/nextcloud-freigaben-infografik/</link><guid isPermaLink="true">https://bitbetter.de/blog/nextcloud-freigaben-infografik/</guid><description>Nextcloud ist eine beliebte Plattform für das Teilen von Dateien und Ordnern in einer Gruppe. Das ist aber oft komplizierter als angenommen, weil es viele verschiedene Verfahren dafür gibt. Wir helfen mit einer freien Infografik, die alle Varianten gegenübergestellt.</description><pubDate>Fri, 11 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Nextcloud ist eine tolle Open-Source-Lösung zum Austauschen von Dateien, zur Nutzung als Groupware und vieles mehr. Die Kernfunktion – das Teilen von Dateien in einer Gruppe von Menschen – ist allerdings gar nicht mal so einfach. Inzwischen gibt es mehrere unabhängige Mechanismen zur Dateifreigabe. Das sorgt bei Nextcloud-Neueinsteigern oft für Verwirrung. Wir haben deswegen eine freie Infografik erstellt, welche die unterschiedlichen Varianten gegenüberstellt und euch dabei hilft, die am bestende passende Funktion für euren Anwendungsfall auszuwählen.&lt;/p&gt;
&lt;h2 id=&quot;drei-verschiedene-mechanismen-zur-dateifreigabe&quot;&gt;Drei verschiedene Mechanismen zur Dateifreigabe&lt;/h2&gt;
&lt;p&gt;Vor einiger Zeit haben wir &lt;a href=&quot;https://bitbetter.de/blog/nextcloud-freigabe-chaos/&quot;&gt;in einem Blogpost bereits beschrieben&lt;/a&gt;, wieso das Teilen von Dateien in Nextcloud oft im Chaos endet: Es gibt nämlich (je nach Zählweise) drei bis vier verschiedene Mechanismen um Dateien miteinander zu teilen:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Freigabe für mehrere Nutzer&lt;/li&gt;
&lt;li&gt;Freigabe für eine Gruppe&lt;/li&gt;
&lt;li&gt;Freigabe für ein Team (früher als Circles bekannt)&lt;/li&gt;
&lt;li&gt;Gruppenordner&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Wer daraus die falsche Variante wählt, der wird oft von unbeabsichtigten Seiteneffekten überrascht (z.B. das auf einmal Dateien fehlen, wenn ein Teilnehmer ausscheidet). Deswegen helfen wir euch dabei, die richtige Methode zu wählen indem wir die wichtigsten Unterschiede dieser Verfahren übersichtlich &lt;strong&gt;in einer Infografik&lt;/strong&gt; aufbereitet haben. Die Grafik eignet sich daher gut um ausgedruckt, oder via Social Media geteilt zu werden. Wir hoffen dass Sie etwas Klarheit in das Freigabe-Chaos bringt.&lt;/p&gt;
&lt;h2 id=&quot;infografik-zum-download&quot;&gt;Infografik zum Download&lt;/h2&gt;
&lt;figure class=&quot;&quot;&gt;p&gt;img]:mx-auto&quot;&gt;&lt;img src=&quot;https://bitbetter.de/images/blog/nextcloud-freigaben-infografik/nextcloud-freigaben-infografik.png&quot; alt=&quot;Die Infografik vergleicht vier verschiedene Mechanismen zum Teilen von Dateien unter Nextcloud. Die verglichenen Methoden sind &amp;#34;Einzelfreigabe&amp;#34;, &amp;#34;Gruppenfreigabe&amp;#34;, &amp;#34;Teams-Freigabe&amp;#34; und &amp;#34;Gruppenordner&amp;#34;.&quot;&gt;&lt;/figure&gt;
&lt;br&gt;
&lt;br&gt;
&lt;div class=&quot;flex gap-4 not-prose items-center justify-center&quot;&gt;&lt;a href=&quot;https://bitbetter.de/images/blog/nextcloud-freigaben-infografik/nextcloud-freigaben-infografik.png&quot; class=&quot;flex w-fit gap-2 py-4 px-5 rounded-full items-center font-medium hover:bg-opacity-75 bg-black text-white py-4 px-5 &quot; target=&quot;_blank&quot; download=&quot;&quot; data-umami-event=&quot;nextcloud-infografik-download-png&quot;&gt;Download .png&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 256 256&quot; fill=&quot;currentColor&quot; class=&quot;h-6 w-6&quot;&gt;&lt;path d=&quot;M221.66,133.66l-72,72a8,8,0,0,1-11.32-11.32L196.69,136H40a8,8,0,0,1,0-16H196.69L138.34,61.66a8,8,0,0,1,11.32-11.32l72,72A8,8,0,0,1,221.66,133.66Z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a href=&quot;https://bitbetter.de/images/blog/nextcloud-freigaben-infografik/nextcloud-freigaben-infografik.pdf&quot; class=&quot;flex w-fit gap-2 py-4 px-5 rounded-full items-center font-medium hover:bg-opacity-75 bg-bb-grey-200 text-bb-grey-500 py-4 px-5 &quot; target=&quot;_blank&quot; download=&quot;&quot; data-umami-event=&quot;nextcloud-infografik-download-pdf&quot;&gt;Download .pdf&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 256 256&quot; fill=&quot;currentColor&quot; class=&quot;h-6 w-6&quot;&gt;&lt;path d=&quot;M221.66,133.66l-72,72a8,8,0,0,1-11.32-11.32L196.69,136H40a8,8,0,0,1,0-16H196.69L138.34,61.66a8,8,0,0,1,11.32-11.32l72,72A8,8,0,0,1,221.66,133.66Z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/div&gt;
&lt;h2 id=&quot;freie-creative-commons-lizenz-modifizieren-erwünscht&quot;&gt;Freie Creative-Commons-Lizenz: Modifizieren erwünscht&lt;/h2&gt;
&lt;p&gt;Für Hinweise, Fehler und Ergänzungen sind wir natürlich dankbar, und am besten per E-Mail erreichbar. Bei Korrekturen oder Ergänzungen werden wir diesen Post hier aktualisieren. Das offene Format (für Affinity Designer) zur Bearbeitung der Grafik steht &lt;a href=&quot;https://bitbetter.de/images/blog/nextcloud-freigaben-infografik/nextcloud-freigaben-infografik.afdesign&quot;&gt;hier ebenfalls zum Download bereit&lt;/a&gt;. Die Grafik steht &lt;strong&gt;unter einer freien Creative-Commons-Lizenz zur Verfügung&lt;/strong&gt;. Wir haben uns für die &lt;a href=&quot;https://creativecommons.org/licenses/by-sa/4.0/deed.de&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;CC BY-SA 4.0&lt;/a&gt; entschieden. Das bedeutet, dass die Grafik und ihre Inhalte geteilt, bearbeitet und frei benutzt werden können – gerne auch kommerziell. Die einzigen Bedingungen dafür sind die Namensnennung (ein Verweis auf diesen Blogpost ist ausreichend) und die Veröffentlichung von daraus hervorgehenden Werken unter der selben Lizenz.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update (13.11.24)&lt;/strong&gt;: Wir haben in der Infografik noch den Hinweis auf die &lt;a href=&quot;https://apps.nextcloud.com/apps/guests&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;Nextcloud-Apps “Guests”&lt;/a&gt; eingebaut, welche das Nutzen von Gruppenordnern auch mit Externen möglich macht. Außerdem haben wir eine 🇬🇧 &lt;strong&gt;englische Version&lt;/strong&gt; der Grafik (&lt;a href=&quot;https://bitbetter.de/images/blog/nextcloud-freigaben-infografik/nextcloud-shares-infographic-en.png&quot;&gt;Download as .png&lt;/a&gt; - &lt;a href=&quot;https://bitbetter.de/images/blog/nextcloud-freigaben-infografik/nextcloud-shares-infographic-en.pdf&quot;&gt;Download as .pdf&lt;/a&gt;) erstellt.&lt;/p&gt;</content:encoded></item><item><title>Leadershift: Begleit-Anwendung für die Organisationsentwicklung</title><link>https://bitbetter.de/blog/projektvorstellung-leadershift/</link><guid isPermaLink="true">https://bitbetter.de/blog/projektvorstellung-leadershift/</guid><description>Wir haben eine Fullstack-Webapplikation gebaut, die unserem Kunden dabei hilft, seine Organisationsentwicklungs-Prozesse zu dokumentieren.</description><pubDate>Mon, 16 Sep 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Über einen gemeinsamen Bekannten kontaktierte uns im April 2024 Johannes Schley, der Geschäftsführer der &lt;a href=&quot;https://www.ios-schley.de/&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;IOS Institut für Organisationsentwicklung und
Systemberatung Schley &amp;amp; Partner GmbH&lt;/a&gt; aus Hamburg. Johannes und seine Firma helfen Kunden seit mittlerweile 39 Jahren bei allen Aspekten der Organisationsentwicklung und suchten nach Hilfe bei einem Digitalisierungsthema.&lt;/p&gt;
&lt;p&gt;Für uns war das Thema “Organisationsentwicklung” komplett neu, aber wir konnten schnell einen interessanten Einblick in die Branche und die Tätigkeit unseres Kunden bekommen. Der (für uns relevante) Teil der Arbeit von IOS Schley sind gemeinsame, mehrwöchigen Entwicklungsprozesse, in denen die Teilnehmer und IOS-Mitarbeiter in kleinen Gruppen über Probleme und Ideen für ihrer jeweilige Organisation sprechen und daran arbeiten.&lt;/p&gt;
&lt;p&gt;Einen solchen Entwicklungs-Prozess kann man heutzutage natürlich auch auf verschiedene Weisen digital begleiten und dokumentieren. Aber alle existierenden Anwendungen, die bei IOS Schley in den letzten Jahren diesbezüglich getestet wurden, konnten nie alle Anforderungen vollständig erfüllen.&lt;/p&gt;
&lt;p&gt;Durch die sehr konkreten Vorstellungen von IOS Schley konnten wir nach einem produktiven, initialen Requirements-Workshop sehr schnell mit der Umsetzung starten und dem Kunden eine maßgeschneiderte Anwendung mit dem folgenden Tech-Stack erstellen:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;✌️ Hybride Fullstack-Webapplikation mit &lt;a class=&quot;tooltag inline-flex align-top items-center gap-1&quot; href=&quot;https://www.astro.build&quot; target=&quot;_blank&quot;&gt; &lt;span class=&quot;flex items-center justify-center h-5 [&amp;_svg]:h-full [&amp;_svg]:w-auto&quot;&gt;&lt;svg viewBox=&quot;0 0 63 79&quot; fill=&quot;none&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;&lt;title&gt;Astro Logo&lt;/title&gt;&lt;path d=&quot;M19.4924 65.9282C15.6165 62.432 14.4851 55.0859 16.0999 49.7638C18.8998 53.1193 22.7793 54.1822 26.7977 54.7822C33.0013 55.7081 39.0937 55.3618 44.8565 52.5637C45.5158 52.2434 46.125 51.8174 46.8454 51.386C47.3861 52.9341 47.5268 54.497 47.338 56.0877C46.8787 59.9617 44.9251 62.9542 41.8177 65.2227C40.5752 66.13 39.2604 66.9411 37.9771 67.7967C34.0346 70.4262 32.9679 73.5095 34.4494 77.9946C34.4846 78.1038 34.5161 78.2131 34.5957 78.4797C32.5828 77.5909 31.1124 76.2965 29.9921 74.5946C28.8088 72.7984 28.2458 70.8114 28.2162 68.6615C28.2014 67.6152 28.2014 66.5597 28.0588 65.5282C27.7107 63.0135 26.5144 61.8876 24.2608 61.8227C21.9479 61.7561 20.1183 63.1672 19.6331 65.3893C19.5961 65.5597 19.5424 65.7282 19.4887 65.9263L19.4924 65.9282Z&quot; fill=&quot;currentColor&quot; /&gt;&lt;path d=&quot;M0 51.3932C0 51.3932 10.5979 46.2433 21.2254 46.2433L29.2382 21.5069C29.5381 20.3106 30.4141 19.4977 31.4029 19.4977C32.3918 19.4977 33.2677 20.3106 33.5677 21.5069L41.5804 46.2433C54.1672 46.2433 62.8058 51.3932 62.8058 51.3932C62.8058 51.3932 44.8044 2.47586 44.7692 2.37772C44.2526 0.931458 43.3804 0 42.2045 0H20.6032C19.4273 0 18.5903 0.931458 18.0384 2.37772C17.9995 2.47401 0 51.3932 0 51.3932Z&quot; fill=&quot;currentColor&quot; /&gt;&lt;/svg&gt;&lt;/span&gt; Astro &lt;/a&gt; mit zwei Frontends (eins für Teilnehmer und eins für Admins)&lt;/li&gt;
&lt;li&gt;⚛ Frontend größtenteils auf dem Server gerendert (SSR), interaktive Komponenten mit &lt;a class=&quot;tooltag inline-flex align-top items-center gap-1&quot; href=&quot;https://bitbetter.de/leistungen/react/&quot;&gt; &lt;span class=&quot;flex items-center justify-center h-5 [&amp;_svg]:h-full [&amp;_svg]:w-auto&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;-11.5 -10.23174 23 20.46348&quot;&gt;&lt;title&gt;React Logo&lt;/title&gt;&lt;circle cx=&quot;0&quot; cy=&quot;0&quot; r=&quot;2.05&quot; fill=&quot;currentColor&quot; /&gt;&lt;g stroke=&quot;currentColor&quot; stroke-width=&quot;1&quot; fill=&quot;none&quot;&gt;&lt;ellipse rx=&quot;11&quot; ry=&quot;4.2&quot; /&gt;&lt;ellipse rx=&quot;11&quot; ry=&quot;4.2&quot; transform=&quot;rotate(60)&quot; /&gt;&lt;ellipse rx=&quot;11&quot; ry=&quot;4.2&quot; transform=&quot;rotate(120)&quot; /&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/span&gt; React &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;💅 Styling des Admin-Interfaces mit &lt;a class=&quot;tooltag inline-flex align-top items-center gap-1&quot; href=&quot;https://tailwindcss.com/&quot; target=&quot;_blank&quot;&gt; &lt;span class=&quot;flex items-center justify-center h-5 [&amp;_svg]:h-full [&amp;_svg]:w-auto&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; fill=&quot;none&quot; viewBox=&quot;0 0 54 33&quot;&gt;&lt;g clip-path=&quot;url(#prefix__clip0)&quot;&gt;&lt;title&gt;Tailwind CSS Logo&lt;/title&gt;&lt;path fill=&quot;currentColor&quot; fill-rule=&quot;evenodd&quot; d=&quot;M27 0c-7.2 0-11.7 3.6-13.5 10.8 2.7-3.6 5.85-4.95 9.45-4.05 2.054.513 3.522 2.004 5.147 3.653C30.744 13.09 33.808 16.2 40.5 16.2c7.2 0 11.7-3.6 13.5-10.8-2.7 3.6-5.85 4.95-9.45 4.05-2.054-.513-3.522-2.004-5.147-3.653C36.756 3.11 33.692 0 27 0zM13.5 16.2C6.3 16.2 1.8 19.8 0 27c2.7-3.6 5.85-4.95 9.45-4.05 2.054.514 3.522 2.004 5.147 3.653C17.244 29.29 20.308 32.4 27 32.4c7.2 0 11.7-3.6 13.5-10.8-2.7 3.6-5.85 4.95-9.45 4.05-2.054-.513-3.522-2.004-5.147-3.653C23.256 19.31 20.192 16.2 13.5 16.2z&quot; clip-rule=&quot;evenodd&quot; /&gt;&lt;/g&gt;&lt;defs&gt;&lt;clipPath id=&quot;prefix__clip0&quot;&gt;&lt;path fill=&quot;#fff&quot; d=&quot;M0 0h54v32.4H0z&quot; /&gt;&lt;/clipPath&gt;&lt;/defs&gt;&lt;/svg&gt;&lt;/span&gt; Tailwind CSS &lt;/a&gt; und &lt;a href=&quot;https://flowbite.com&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;Flowbite&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;🖊️ Styling des Teilnehmer-Frontends durch ein eigenes Design von &lt;a href=&quot;https://sophiaweider.de/&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;Sophia Weider&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;✉️ Anbindung von Notifications über E-Mail (via SMTP) und SMS (via &lt;a href=&quot;https://gatewayapi.com/&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;GatewayAPI&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;👨‍🍳 Anbindung von Kundendaten aus &lt;a href=&quot;https://www.mocoapp.com/&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;Moco ERP&lt;/a&gt; via API&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Für uns war dieser Auftrag ein besonders dankbares Projekt, da wir mit tollen Menschen arbeiten durften, die konzeptionell bereits sehr genau wussten, was sie haben möchten. So konnten wir uns stark auf die Umsetzung und den technischen Part konzentrieren und schnell und effizient eine individuelle Software erstellen, die dem Kunden hoffentlich lange helfen wird.&lt;/p&gt;</content:encoded></item><item><title>Mengenoperationen in JavaScript – ein gefährliches Performance-Loch</title><link>https://bitbetter.de/blog/ecmascript-mengenoperationen/</link><guid isPermaLink="true">https://bitbetter.de/blog/ecmascript-mengenoperationen/</guid><description>Durch Funktionen wie Array.filter(), Array.some() oder das neue Set.difference() kann man mit sehr wenig Code sehr mächtige Mengenoperationen in JavaScript durchführen. Je nach Herangehensweise kann die Performance dabei aber sehr stark variieren.</description><pubDate>Tue, 03 Sep 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Bei einem unserer Projekte sind wir kürzlich auf ein unerwartete Performance-Problem in JavaScript gestoßen: Wir wollten zwei große JSON-Logdateien miteinander vergleichen und diejenigen Zeilen herausfiltern, welche in beiden Logdateien enthalten sind. Eigentlich ist das eine gängige und simple Aufgabe und mit wenigen Zeilen JavaScript erledigt. Mit dem Suchbegriff “Mengenoperationen JavaScript” findet man auch schnell die üblichen StackOverflow-Beiträge mit vermeintlichen Lösungen für diese Aufgabe zum Thema “Differenzmenge”. Bei kleinen Dateien werden alle gefundenen Lösungen auch keine Probleme machen, aber in unserem Fall ging es um JSON-Arrays mit ca. 1.000.000 Objekten/Zeilen. Als eine der vorgeschlagenen Lösungen nach über einer Stunde Laufzeit immer noch nicht fertig war, machten wir uns langsam Gedanken und haben ein paar Alternativen miteinander verglichen.&lt;/p&gt;
&lt;h2 id=&quot;mengenoperationen-erst-seit-2024-im-standard-verfügbar&quot;&gt;Mengenoperationen erst seit 2024 im Standard verfügbar&lt;/h2&gt;
&lt;p&gt;JavaScript bzw. ECMAScript (der “richtige” Name der Sprache) brachte lange Zeit keine expliziten Funktionen für Mengenoperationen mit. Obwohl diese Funktionen schon lange diskutiert wurden, erreichten sie &lt;a href=&quot;https://github.com/tc39/ecma262/pull/3306&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;erst 2024 die “Stage 4”&lt;/a&gt;, wurden also offiziell in den Standard aufgenommen. Seit einigen Monaten gibt es nun also in den meisten Runtimes explizit die Möglichkeit, Mengenoperationen wie &lt;code&gt;.union()&lt;/code&gt;, &lt;code&gt;.intersection()&lt;/code&gt; und &lt;code&gt;.difference()&lt;/code&gt; an &lt;code&gt;Set&lt;/code&gt;-Objekten berechnen zu lassen. Im Internet findet man aber natürlich oftmals noch Code, welcher für diese Aufgabe kein &lt;code&gt;Set&lt;/code&gt; verwendet. Wir haben also alte und neue Optionen miteinander verglichen:&lt;/p&gt;
&lt;h2 id=&quot;vorbereitung-wir-bauen-uns-zwei-große-arrays&quot;&gt;Vorbereitung: Wir bauen uns zwei große Arrays&lt;/h2&gt;
&lt;p&gt;Für die folgenden Vergleiche haben wir jeweils die gleichen zwei Arrays mit einer beispielhaften Länge von 200.000 Elementen initialisiert. Der Inhalt dieser Objekte spielt hier keine besondere Rolle. Nur so viel: Sie enthalten jeweils eine ID, über die sie eindeutig identifiziert werden können. Wir suchen also in beiden Arrays diejenigen Objekte mit der gleichen ID.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; bigArray1&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Array.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  { length: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;200000&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;v&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;k&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ({ id: k.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;toString&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;() }),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; bigArray2&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Array.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  { length: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;200000&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;v&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;k&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ({ id: (k &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 2&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;).&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;toString&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;() }),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;vergleichen-mit-schleifen-oder-filter-und-some&quot;&gt;Vergleichen mit Schleifen oder &lt;code&gt;.filter()&lt;/code&gt; und &lt;code&gt;.some()&lt;/code&gt;&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// Option 1a&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; diff&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; bigArray1.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;filter&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;obj&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; !&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;bigArray2.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;some&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;obj2&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; obj2.id &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;===&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; obj.id),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// =&amp;gt; Dauer ~120 s&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// Option 1b&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; diff&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; [];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;outerLoop&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; array1Element&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; of&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; bigArray1) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  for&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; array2Element&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; of&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; bigArray2) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (array1Element.id &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;===&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; array2Element.id) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;      continue&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; outerLoop&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  diff.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;push&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(array1Element);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// =&amp;gt; Dauer ~120 s&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Dieser Code war unser erster, naiver Ansatz zum Bilden der Differenz: Die Verwendung von &lt;code&gt;.filter()&lt;/code&gt; ist ein klassisches Konzept aus der funktionalen Programmierung. Der Code ist kurz und gut verständlich und es wird dabei relativ wenig Speicher verbraucht. Der Nachteil dieser Variante ist aber, dass relativ viel Zeit benötigt wird. Der Aufwand für die Laufzeit dieses Algorithmus wird als &lt;code&gt;O(n²)&lt;/code&gt; beschrieben – das heißt, jedes Array-Element des ersten Arrays wird mit jedem Array-Element des zweiten Arrays verglichen (also in diesem Fall finden &lt;code&gt;200.000² = 40 Mrd.&lt;/code&gt; Vergleichsoperationen statt). Auch die zweite Code-Variante basiert genau auf diesem Grundsatz, benutzt jedoch zwei for-Schleifen statt dem funktionalen &lt;code&gt;filter()&lt;/code&gt;-Aufruf und braucht deswegen etwa genau so lange für die Berechnung. Beide Varianten sind also nur dann sinnvoll, wenn wenig Arbeitsspeicher zur Verfügung steht oder die beiden Arrays eine Größe haben, bei denen die Laufzeit egal ist.&lt;/p&gt;
&lt;h2 id=&quot;ein-array-von-ids-und-arrayincludes&quot;&gt;Ein Array von IDs und Array.includes()&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// Option 2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; bigArray2Ids&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; bigArray2.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;map&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;obj&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; obj.id);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; diff&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; bigArray1.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;filter&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;obj&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; !&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;bigArray2Ids.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;includes&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(obj.id),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// =&amp;gt; Dauer ~80 s&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Bei der zweiten Option haben wir &lt;code&gt;Array.includes()&lt;/code&gt; benutzt, um zu prüfen, ob eine Logzeile in dem Array vorhanden ist. Da &lt;code&gt;includes()&lt;/code&gt; die Referenzen der Objekte vergleicht, und nicht deren Inhalt, müssen wir hier die IDs statt der kompletten Objekte benutzen. Deswegen erstellen wir zuerst ein Array mit allen IDs von einem der beiden Logfiles und sortieren dann diejenigen Einträge aus dem zweiten Logfile, deren IDs bereits im ersten Array enthalten sind. Die Laufzeit verbessert sich in diesem Ansatz etwas, dafür ist nun der Speicherbedarf höher, da wir ein zusätzliches Array mit den IDs benötigen. Diese Variante ist vielleicht etwas weniger intuitiv als die erste Variante und der Laufzeit-Vorteil ist relativ gering.&lt;/p&gt;
&lt;h2 id=&quot;sets-mit-jsonstringify&quot;&gt;Sets mit JSON.stringify()&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// Option 3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; bigArray2StringifiedSet&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Set&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  bigArray2.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;map&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;obj&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; JSON&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;stringify&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(obj)),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; bigArray1StringifiedSet&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Set&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  bigArray1.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;map&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;obj&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; JSON&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;stringify&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(obj)),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; differenceStringified&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Array.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  bigArray1StringifiedSet.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;difference&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    bigArray2StringifiedSet,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  ),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; diff&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; differenceStringified.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;map&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;obj&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  JSON&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;parse&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(obj),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// =&amp;gt; Dauer ~0.4 s&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nun verwenden wir endlich die “neuen” Mengenoperationen (konkret die &lt;code&gt;.difference()&lt;/code&gt;-Operation). Diese arbeiten aber nur mit Sets: das heißt wir müssen beide Arrays zunächst in geeignete Sets überführen. Da aber auch hier Referenzen von den jeweiligen Objekten verglichen werden würden, benutzen wir nun &lt;code&gt;JSON.stringify()&lt;/code&gt;, um vergleichbare Strings aus den Objekten zu erzeugen. Der Haken hier dran ist, dass beide Objekte die gleichen Eigenschaften haben müssen – die gleiche ID reicht nicht aus. Alternativ könnte man die Mengenoperation auch nur auf die zwei Sets der IDs anwenden. Dann müsste man im Nachhinein aber wieder die verbleibenden IDs zu ihren ursprünglichen Objekten auflösen – was in Summe langsamer ist, als dieser &lt;code&gt;JSON.stringify()&lt;/code&gt;-Ansatz. Hier stellen wir nun das erste mal einen deutlichen Sprung in der Laufzeit fest. Die Komplexität des Algorithmus sinkt nämlich wahrscheinlich auf &lt;code&gt;O(n)&lt;/code&gt; (abhängig von der Implementierung in der jeweiligen JavaScript-Engine). Der Speicherbedarf ist allerdings auch deutlich gestiegen, da wir beide Arrays nicht nur duplizieren, sondern auch als serialisierte Strings im Speicher halten.&lt;/p&gt;
&lt;h2 id=&quot;am-schnellsten-objektfelder-als-lookup-table&quot;&gt;Am schnellsten: Objektfelder als Lookup-Table&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// Option 4&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; map&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {};&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; ele&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; of&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; bigArray2) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  map[ele.id] &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; true&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; diff&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; bigArray1.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;filter&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;obj&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; !&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;map[obj.id]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// =&amp;gt; Dauer ~0.07 s&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Die mit Abstand schnellste Variante ist es, ein Objekt als eine einfache “Lookup Table” zu benutzen. Hierbei erzeugen wir für jede ID des einen Arrays ein Feld in einem neuen Objekt. Somit wird zusätlicher Speicher für diese Tabelle benötigt – allerdings nur in der Größe von &lt;code&gt;n (= Anzahl der Element im Array) * 2&lt;/code&gt; Bytes (ein Boolean in JavaScript ist üblicherweise ein Byte groß). Beim Vergleichen der Werte kann dann auf die vorgefertigte Tabelle zugegriffen werden. Der Zugriff auf die Tabelle hat eine Laufzeitkomplexität von &lt;code&gt;O(1)&lt;/code&gt;, somit hat das Filtern eine Komplexität von &lt;code&gt;O(n)&lt;/code&gt;. Diese Variante ist deutlich schneller als alle anderen Herangehensweisen. Allerdings mag der Code für einige Entwickler (die keinen Background in hardwarenaher Programmierung haben) etwas ungewöhnlich aussehen. Für performantes Vergleichen von zwei solcher Arrays ist diese Variante allerdings klar zu bevorzugen, solange genügend Arbeitsspeicher vorhanden ist.&lt;/p&gt;
&lt;h2 id=&quot;fazit-überall-footguns-auch-bei-mengenoperationen&quot;&gt;Fazit: Überall Footguns, auch bei Mengenoperationen&lt;/h2&gt;
&lt;p&gt;Wie so oft in der Informatik gilt auch hier: Vorteile in der Laufzeit bedeuten leider fast immer Nachteile im Speicherbedarf. JavaScript macht es mit seinen neuen Mengenoperationen sehr einfach, solche Operationen auszuführen. Oftmals ist diese “naive” Herangehensweise allerdings nicht sehr performant – es handelt sich also um eine sogenannte “Footgun” (etwas vermeintlich Einfaches, bei dem sich der Entwickler sprichwörtlich in den Fuß schießen kann). Bei großen Datenmengen lohnt sich definitiv der Blick auf andere Algorithmen, da die Zeiteinsparungen ein Vielfaches (in unserem Beispielfall: Faktor 170) betragen können.&lt;/p&gt;</content:encoded></item><item><title>Warum wir am liebsten Web-Apps bauen</title><link>https://bitbetter.de/blog/webapps/</link><guid isPermaLink="true">https://bitbetter.de/blog/webapps/</guid><description>Webanwendungen sind oft eine gute und kostengünstige Variante um individuelle Software zu realisieren. Benutzer brauchen lediglich einen aktuellen Browser. Damit sind Web-Apps klassischen, nativen Anwendungen in vielen Bereichen überlegen.</description><pubDate>Mon, 12 Aug 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Webanwendungen sind in den letzten Jahren (spätestens seit dem “Web 2.0”) immer beliebter geworden und haben sich in der in der IT-Welt fest etabliert. Sie bieten zahlreiche Vorteile, die sie für sehr viele Anwendungsbereiche sinnvoll machen. Wir beschreiben, warum wir Webanwendungen für eine gute und effiziente Lösung für viele Anwendungsfälle und Kunden (gerade für kleine und mittlere Unternehmen) halten – aber auch welche Risiken sie haben können.&lt;/p&gt;
&lt;h2 id=&quot;was-sind-überhaupt-webanwendungen&quot;&gt;Was sind überhaupt Webanwendungen?&lt;/h2&gt;
&lt;p&gt;Webanwendungen (auf Englisch auch Web-Apps genannt) sind Softwareanwendungen, die über einen Webbrowser aufgerufen und genutzt werden. Von einfachen Webseiten grenzen sich Webanwendungen durch eine höhere Komplexität ab. Bekannte Beispiele für Web-Apps sind zum Beispiel solche Anwendungen wie die Online-Office-Lösung &lt;a href=&quot;https://www.google.com/docs/about/&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;Google Docs&lt;/a&gt;, die Design-Programme &lt;a href=&quot;https://www.figma.com/&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;Figma&lt;/a&gt; oder &lt;a href=&quot;https://www.canva.com/&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;Canva&lt;/a&gt; oder das Videostreaming-Portal &lt;a href=&quot;https://www.netflix.com&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;Netflix&lt;/a&gt;. Im Gegensatz zu traditionellen Desktop- oder mobilen Anwendungen (auch “native” Anwendungen genannt) müssen sie meistens nicht auf dem Gerät installiert werden, sondern können einfach über eine Internetadresse (URL) aufgerufen und genutzt werden.&lt;/p&gt;
&lt;h2 id=&quot;️-zugänglichkeit-ein-browser-reicht-aus&quot;&gt;👁️ Zugänglichkeit: Ein Browser reicht aus&lt;/h2&gt;
&lt;p&gt;Webanwendungen werden über einen Webbrowser aufgerufen. Das bedeutet, dass jedes Gerät, auf welchem ein moderner Browser installiert werden kann (egal ob es sich um einen PC, ein Smartphone, ein Tablet, ein Auto oder einen Fernseher handelt) diese ausführen kann, unabhängig vom Betriebssystem oder der darunterliegenden Hardware. Mit einer einzigen Applikation können also wahnsinnig viele Gerätearten und -kategorien abgedeckt werden. Dank dem sogenannten Responsive Design können Webanwendungen auch auf alle Bildschirmgrößen zugeschnitten werden. Das reduziert den Entwicklungsaufwand deutlich gegenüber nativen Anwendungen und verringert die Abhängigkeit von speziellen Herstellern oder Plattformen. Entwickelt man z.B. eine native iOS-App, so ist man auch in Zukunft an Apple-Hardware gebunden – dies ist bei Web-Apps nicht der Fall. Die Web-Technologie erlaubt es außerdem relativ gut, Software auch für Menschen mit Beeinträchtigung (etwa Seh- oder Motorikbeeinträchtigungen) zugänglich zu machen.&lt;/p&gt;
&lt;h2 id=&quot;️-zentralisierung-geteilte-daten-keine-backups-und-offene-schnittstellen&quot;&gt;🕸️ Zentralisierung: Geteilte Daten, keine Backups und offene Schnittstellen&lt;/h2&gt;
&lt;p&gt;Webapplikationen bestehen häufig aus einer Server-Komponente (Backend) und einer Benutzeroberfläche (Frontend). Das Frontend ist die eigentliche Web-App und wird im Browser des Benutzers ausgeführt. Das Backend läuft meistens auf einem (oder mehreren) zentralisierten Server(n). Das Frontend kann dabei idealerweise sehr schlank gestaltet werden, so dass der Nutzer nur die Daten vom Server bekommt, welche er tatsächlich benötigt. Die Geschäftslogik (z.B. Berechnungen etc.) als auch die Daten liegen auf dem Server und können von vielen Nutzern geteilt werden. Eine gemeinsame Datenbasis zwischen allen Nutzern erleichtert die Kollaboration und macht das Austauschen von Dateien oder das Nutzen von Netzlaufwerken häufig unnötig.&lt;/p&gt;
&lt;figure class=&quot;&quot;&gt;p&gt;img]:mx-auto [&amp;#38;&gt;p&gt;img]:mb-0&quot;&gt;&lt;img src=&quot;https://bitbetter.de/images/blog/webapps/webapps_figma.png&quot; alt=&quot;&quot;&gt;&lt;figcaption&gt;Ein Screenshot aus der beliebten Design-Anwendung Figma, die komplett im Browser verwendet werden kann.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;Durch die Zentralisierung der Daten müssen auch kaum Daten beim Nutzer gespeichert werden, wodurch dem Nutzer auch keine Daten verloren gehen können (Laptop geklaut, Wasserschaden, etc.). Am wichtigsten finden wir allerdings, dass es im Kontext von Web-Apps gängig und einfach ist, eine generische Programmier-Schnittstelle zur Verfügung zu stellen, die auch von anderen Programmen genutzt werden kann. Programme sollten heutzutage immer eine Möglichkeit bieten, im Zusammenspiel mit anderen Anwendungen zu funktionieren, sonst ist eine Automatisierung eines Prozesses, in welchem dieses Programm vorkommt, nicht möglich. Dadurch, dass bei einer Web-App meistens ein Server vorhanden ist, der eine Schnittstelle für das Frontend bereitstellt, kann diese Schnittstelle oft auch von anderen Programmen verwendet werden.&lt;/p&gt;
&lt;h2 id=&quot;-updates-passieren-automatisch&quot;&gt;📲 Updates passieren automatisch&lt;/h2&gt;
&lt;p&gt;Eine Software, die nicht gewartet wird, stirbt bekanntlich irgendwann aus. Updates sind essentiell, um Fehler zu beheben, neue Features einzubauen und die Software an neue Bedingungen anzupassen. Daher ist es von großem Vorteil, wenn jeder Benutzer immer die aktuelle Version einer Software benutzt. Bei nativen Anwendungen bedeutet das, dass auf jedem Gerät das Update installiert werden muss. Je nach Art der Nutzer, bedeutet das einen großen Administrationsaufwand (z.B. in einer Unternehmensstruktur) oder es dauert sehr lange, bis alle Nutzer das Update installiert haben, z.B. bei privaten Nutzern. Bei Webanwendungen dagegen laden die Nutzer die Anwendung meistens bei jedem Aufruf über das Netzwerk. Somit muss nur die Anwendung auf dem Server aktualisiert werden – die Clients updaten sich dann quasi von alleine. Der Administrationsaufwand wird dadurch drastisch reduziert.&lt;/p&gt;
&lt;h2 id=&quot;-web-apps-sind-oft-günstiger-zu-erstellen-als-native-anwendungen&quot;&gt;💸 Web-Apps sind oft günstiger zu erstellen als native Anwendungen&lt;/h2&gt;
&lt;p&gt;Alle oben beschriebenen Vorteile einer Webanwendung tragen erheblich dazu bei, die Kosten für den Betrieb und die Entwicklung einer Anwendung zu senken. Webanwendungen können nicht alles besser, aber sie sind oft ein sehr kosteneffizienter Weg, eine große Spanne an Funktionen abzudecken. Sowohl Frontend als auch Backend können zum Beispiel in einer gemeinsamen Programmiersprache (JavaScript/Typescript), geschrieben werden, wodurch eine schnelle und kosteneffiziente Entwicklung auch von einem kleinen Team möglich gemacht wird.&lt;/p&gt;
&lt;h2 id=&quot;nachteile-immer-online-wenig-hardware-support-und-tracking&quot;&gt;Nachteile: Immer online, wenig Hardware-Support und Tracking&lt;/h2&gt;
&lt;p&gt;Natürlich sind Web-Apps kein Allheilmittel. Die vielen Vorteile bringen auch einige Nachteile mit sich, die nicht verschwiegen werden sollten:&lt;/p&gt;
&lt;p&gt;Die Abhängigkeit von einer Internetverbindung ist ein konzeptionelles Problem. Zwar lassen sich Web-Anwendungen heute auch so entwickeln, dass sie eine Zeit lang gut ohne Internetverbindung auskommen, jedoch braucht es spätestens zum Abgleich von Daten irgendwann wieder eine Verbindung.&lt;/p&gt;
&lt;p&gt;Einen weiteren Nachteil haben die Web-Anwendungen wenn es um die enge Verknüpfung mit Hardware geht. Browser können nur auf einige, wenige Hardware-Komponenten zugreifen. Wer also unmittelbar mit Hardware-Komponenten interagieren muss, oder optimierte Hardware-Features (z.B. im 3D- und Grafikbereich oder bei maschinellem Lernen) verwenden möchte, für den sind Web-Apps oft keine Alternative.&lt;/p&gt;
&lt;p&gt;Web-Apps haben außerdem einen schlechten Ruf, da gerade große Beispiel oft sehr stark mit Tracking-Maßnahmen durchsetzt sind oder immer weiter aufgebläht werden, bis sie nicht mehr performant funktionieren können. Beide Aspekte sind aber eher organisatorische Probleme und sollten nicht der Technologie zugeschrieben werden.&lt;/p&gt;</content:encoded></item><item><title>Tolocar – Webseite für mobile Makerspaces in der Ukraine</title><link>https://bitbetter.de/blog/projektvorstellung-tolocar/</link><guid isPermaLink="true">https://bitbetter.de/blog/projektvorstellung-tolocar/</guid><description>Der russische Angriffskrieg auf die Ukraine hat das Land und die Menschen schwer gezeichnet. Die mobilen Makerspaces namens Tolocar versuchen vor Ort zu reparieren und neue Maker auszubilden. Wir durften die Webseite dafür bauen.</description><pubDate>Fri, 01 Mar 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Bereits kurz nach dem russischen Überfall auf die Ukraine formierte sich in der europäischen Maker-Szene eine pragmatische Hilfsmaßnahme: Mobile Makerspaces in Form von umgebauten LKWs und Transportern, die schnell und unabhängig dort helfen können, wo Hilfe dringend gebraucht wird – die sogenannten Tolocars.&lt;/p&gt;
&lt;p&gt;Tolocars sind mit jeder Menge Werkzeugen und modernen Produktionsmaschinen (3D-Drucker, Lasercutter, CNC-Fräsen und mehr) ausgestattet, um viele kleinere Kriegsschäden direkt vor Ort zu reparieren und humanitäre Hilfe (in Kooperation mit anderen Hilfsorganisationen) zu leisten. Außerdem dienen sie als Vermittler und Botschafter für die Maker-Szene und das Prinzip der Selbsthilfe, die gerade in der Ukraine wichtiger ist denn je.&lt;/p&gt;
&lt;p&gt;Durch unser privates Interesse und unsere langjährigen Verbindungen in die Hamburger Maker-Szene konnten wir im Sommer 2022 die Initiatoren (konkret: das &lt;a href=&quot;https://www.hiww.de/&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;Hamburger Institut für Wertschöpfungssystematik und Wissensmanagement&lt;/a&gt;) überzeugen, die Webseite für das Tolocar-Projekt von uns entwickeln und betreuen zu lassen. Im Laufe der Zeit durften wir dabei viele neue Projektpartner und beteiligte Organisationen kennenlernen.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://bitbetter.de/images/blog/tolocar/tolocar-map.jpg&quot; alt=&quot;Die interaktive Karte auf Mapbox-Basis zeigt die vergangenen Tolocar-Einsätze&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;was-wir-gemacht-haben&quot;&gt;Was wir gemacht haben&lt;/h2&gt;
&lt;p&gt;Wir kooperierten bei diesem Projekt mit unserem befreundeten Designer &lt;a href=&quot;https://moeritz.io/&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;Tobias Möritz&lt;/a&gt; und bekamen weitere Entwickler-Kapazität von &lt;a href=&quot;https://github.com/Agustina-Carrion&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;Agustina Carrion&lt;/a&gt;. Mit beiden Partnern haben wir in der Vergangenheit schon mehrere Projekte realisiert. Was wir genau geleistet haben:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;❓ Anforderungsanalyse und Technologieauswahl&lt;/li&gt;
&lt;li&gt;🎨 Hybride Webseite (größtenteils serverseitig gerendert) mit &lt;a class=&quot;tooltag inline-flex align-top items-center gap-1&quot; href=&quot;https://www.astro.build&quot; target=&quot;_blank&quot;&gt; &lt;span class=&quot;flex items-center justify-center h-5 [&amp;_svg]:h-full [&amp;_svg]:w-auto&quot;&gt;&lt;svg viewBox=&quot;0 0 63 79&quot; fill=&quot;none&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;&lt;title&gt;Astro Logo&lt;/title&gt;&lt;path d=&quot;M19.4924 65.9282C15.6165 62.432 14.4851 55.0859 16.0999 49.7638C18.8998 53.1193 22.7793 54.1822 26.7977 54.7822C33.0013 55.7081 39.0937 55.3618 44.8565 52.5637C45.5158 52.2434 46.125 51.8174 46.8454 51.386C47.3861 52.9341 47.5268 54.497 47.338 56.0877C46.8787 59.9617 44.9251 62.9542 41.8177 65.2227C40.5752 66.13 39.2604 66.9411 37.9771 67.7967C34.0346 70.4262 32.9679 73.5095 34.4494 77.9946C34.4846 78.1038 34.5161 78.2131 34.5957 78.4797C32.5828 77.5909 31.1124 76.2965 29.9921 74.5946C28.8088 72.7984 28.2458 70.8114 28.2162 68.6615C28.2014 67.6152 28.2014 66.5597 28.0588 65.5282C27.7107 63.0135 26.5144 61.8876 24.2608 61.8227C21.9479 61.7561 20.1183 63.1672 19.6331 65.3893C19.5961 65.5597 19.5424 65.7282 19.4887 65.9263L19.4924 65.9282Z&quot; fill=&quot;currentColor&quot; /&gt;&lt;path d=&quot;M0 51.3932C0 51.3932 10.5979 46.2433 21.2254 46.2433L29.2382 21.5069C29.5381 20.3106 30.4141 19.4977 31.4029 19.4977C32.3918 19.4977 33.2677 20.3106 33.5677 21.5069L41.5804 46.2433C54.1672 46.2433 62.8058 51.3932 62.8058 51.3932C62.8058 51.3932 44.8044 2.47586 44.7692 2.37772C44.2526 0.931458 43.3804 0 42.2045 0H20.6032C19.4273 0 18.5903 0.931458 18.0384 2.37772C17.9995 2.47401 0 51.3932 0 51.3932Z&quot; fill=&quot;currentColor&quot; /&gt;&lt;/svg&gt;&lt;/span&gt; Astro &lt;/a&gt; und &lt;a class=&quot;tooltag inline-flex align-top items-center gap-1&quot; href=&quot;https://bitbetter.de/leistungen/react/&quot;&gt; &lt;span class=&quot;flex items-center justify-center h-5 [&amp;_svg]:h-full [&amp;_svg]:w-auto&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;-11.5 -10.23174 23 20.46348&quot;&gt;&lt;title&gt;React Logo&lt;/title&gt;&lt;circle cx=&quot;0&quot; cy=&quot;0&quot; r=&quot;2.05&quot; fill=&quot;currentColor&quot; /&gt;&lt;g stroke=&quot;currentColor&quot; stroke-width=&quot;1&quot; fill=&quot;none&quot;&gt;&lt;ellipse rx=&quot;11&quot; ry=&quot;4.2&quot; /&gt;&lt;ellipse rx=&quot;11&quot; ry=&quot;4.2&quot; transform=&quot;rotate(60)&quot; /&gt;&lt;ellipse rx=&quot;11&quot; ry=&quot;4.2&quot; transform=&quot;rotate(120)&quot; /&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/span&gt; React &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;🗺️ Integration einer interaktiven Karte mit OpenStreetMap-Daten (über die &lt;a href=&quot;https://www.mapbox.com/&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;Mapbox&lt;/a&gt; API)&lt;/li&gt;
&lt;li&gt;🧮 Integration von dynamischen Daten via &lt;a href=&quot;https://www.airtable.com&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;AirTable&lt;/a&gt; API&lt;/li&gt;
&lt;li&gt;💬 GitHub-basierte Kommentarfunktion via &lt;a href=&quot;https://github.com/giscus/giscus&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;Giscus&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;💅 CSS mittels &lt;a class=&quot;tooltag inline-flex align-top items-center gap-1&quot; href=&quot;https://tailwindcss.com/&quot; target=&quot;_blank&quot;&gt; &lt;span class=&quot;flex items-center justify-center h-5 [&amp;_svg]:h-full [&amp;_svg]:w-auto&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; fill=&quot;none&quot; viewBox=&quot;0 0 54 33&quot;&gt;&lt;g clip-path=&quot;url(#prefix__clip0)&quot;&gt;&lt;title&gt;Tailwind CSS Logo&lt;/title&gt;&lt;path fill=&quot;currentColor&quot; fill-rule=&quot;evenodd&quot; d=&quot;M27 0c-7.2 0-11.7 3.6-13.5 10.8 2.7-3.6 5.85-4.95 9.45-4.05 2.054.513 3.522 2.004 5.147 3.653C30.744 13.09 33.808 16.2 40.5 16.2c7.2 0 11.7-3.6 13.5-10.8-2.7 3.6-5.85 4.95-9.45 4.05-2.054-.513-3.522-2.004-5.147-3.653C36.756 3.11 33.692 0 27 0zM13.5 16.2C6.3 16.2 1.8 19.8 0 27c2.7-3.6 5.85-4.95 9.45-4.05 2.054.514 3.522 2.004 5.147 3.653C17.244 29.29 20.308 32.4 27 32.4c7.2 0 11.7-3.6 13.5-10.8-2.7 3.6-5.85 4.95-9.45 4.05-2.054-.513-3.522-2.004-5.147-3.653C23.256 19.31 20.192 16.2 13.5 16.2z&quot; clip-rule=&quot;evenodd&quot; /&gt;&lt;/g&gt;&lt;defs&gt;&lt;clipPath id=&quot;prefix__clip0&quot;&gt;&lt;path fill=&quot;#fff&quot; d=&quot;M0 0h54v32.4H0z&quot; /&gt;&lt;/clipPath&gt;&lt;/defs&gt;&lt;/svg&gt;&lt;/span&gt; Tailwind CSS &lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Der Quelltext der Tolocar-Webseite ist einsehbar und steht unter einer Open-Source-Lizenz &lt;a href=&quot;https://github.com/tolocar-project/website&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;zur Verfügung&lt;/a&gt;.&lt;/p&gt;
&lt;a href=&quot;https://tolocar.org&quot; class=&quot;flex w-fit gap-2 py-4 px-5 rounded-full items-center font-medium hover:bg-opacity-75 bg-black text-white py-4 px-5 not-prose mx-auto my-8&quot;&gt;Zur Projekt-Webseite&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 256 256&quot; fill=&quot;currentColor&quot; class=&quot;h-6 w-6&quot;&gt;&lt;path d=&quot;M221.66,133.66l-72,72a8,8,0,0,1-11.32-11.32L196.69,136H40a8,8,0,0,1,0-16H196.69L138.34,61.66a8,8,0,0,1,11.32-11.32l72,72A8,8,0,0,1,221.66,133.66Z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;</content:encoded></item><item><title>Nextcloud und das große Freigabe-Chaos</title><link>https://bitbetter.de/blog/nextcloud-freigabe-chaos/</link><guid isPermaLink="true">https://bitbetter.de/blog/nextcloud-freigabe-chaos/</guid><description>Nextcloud gilt oft als Ersatz für kommerzielle Cloudspeicher wie Dropbox und Google Drive. Die ursprüngliche Kernaufgabe, das Teilen von Dateien und Ordnern, ist aber oft deutlich komplizierter und verwirrender als angenommen, da es drei unterschiedliche Freigabe-Mechanismen gibt.</description><pubDate>Wed, 07 Feb 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Die Verbreitung und Popularität von Open-Source-Software hat im Jahr 2023 noch einmal deutlich an Fahrt aufgenommen. Nicht zuletzt durch große Umwerfungen im Social-Media-Bereich waren Open-Source-Alternativen zu etablierten Software-Lösungen in 2023 populärer denn je. Dabei kommt man natürlich nicht um das &lt;a href=&quot;https://nextcloud.com/&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;Nextcloud-Projekt&lt;/a&gt; herum. Gerne und häufig wird die Software als Vorzeige-Alternative zu kommerziellen und zentralisierten Cloudspeicher-Anbietern wie Dropbox, Google Drive oder Microsoft One Drive und Sharepoint genannt. Auf den ersten Blick ist das eine tolle Idee, aber im Detail sorgt Nextcloud leider oft für mehr Probleme als es löst: Zum Beispiel bei der Kernaufgabe “Filesharing”.&lt;/p&gt;
&lt;h2 id=&quot;drei-unabhängige-mechanismen-zum-teilen-von-dateien-und-ordnern&quot;&gt;Drei unabhängige Mechanismen zum Teilen von Dateien und Ordnern&lt;/h2&gt;
&lt;p&gt;Die Motivation und Euphorie beim Start einer neuen Nextcloud-Instanz sind oft sehr groß. Endlich eigene, unabhängige Infrastruktur zu nutzen, fühlt sich zunächst toll an. Über die Web-Oberfläche oder die verschiedenen Client-Anwendungen füllt sich die Nextcloud schnell mit den ersten Dateien. Und irgendwann kommt der Moment, wo mindestens zwei Benutzer oder vielleicht sogar eine ganze Gruppe von Benutzern eine Datei oder einen Ordner miteinander teilen möchten. Klingt eigentlich simpel, denn dieses Szenario beschreibt ja schließlich die Gründungsidee von Nextcloud (bzw. OwnCloud). Doch diese Aufgabe (Teilen von Dateien in einer festen Gruppe von Personen) ist gar nicht so trivial zu bewerkstelligen. Nextcloud bietet inzwischen mindestens drei verschiedene Mechanismen dafür an.&lt;/p&gt;
&lt;h2 id=&quot;shares-der-naheliegendste-weg-mit-vielen-fallstricken&quot;&gt;Shares: Der naheliegendste Weg mit vielen Fallstricken&lt;/h2&gt;
&lt;p&gt;Die einfachste Lösung für dieses Problem ist also auch die Älteste: die eingebaute “Teilen”-Funktion von Nextcloud. Neben jeder Datei und jedem Ordner in Nextcloud gibt es ein kleines “Share”-Icon, welches einen Dialog öffnet, in dem sich eine Datei oder ein Ordner mit anderen Nextcloud-Nutzern oder Gruppen teilen lässt. Das ist einfach und leicht verständlich – und gefährlich. Diese Funktion sorgt gerade in Organisationen mit vielen Benutzern oft für viel Chaos: Technisch gesehen verhält sich eine solche Nextcloud-Freigabe eigentlich wie ein sogenannter &lt;a href=&quot;https://de.m.wikipedia.org/wiki/Symbolische_Verkn%C3%BCpfung&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;symbolischer Link&lt;/a&gt; in einem Dateisystem. Deswegen gibt es einige weniger bekannte Nebeneffekte:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;👤 &lt;strong&gt;Shares gehören immer einem spezifischen Nutzer:&lt;/strong&gt; Nur der teilende Nutzer kann den Share verwalten. Sollte der Nutzer zum Beispiel aus der betreffenden Organisation ausscheiden, und sein Account gelöscht oder gesperrt werden, dann ist der Share nicht mehr zugänglich für die anderen Empfänger des Shares (für Admins gibt es die Möglichkeit, über das &lt;code&gt;occ&lt;/code&gt;-Kommandozeilenprogramm die Shares auf einen anderen Nutzer zu übertragen). Genau so kann sich der teilende Nutzer einfach entscheiden, wichtige Dateien zu löschen, ohne dass es eine Möglichkeit zur Intervention für die Share-Empfänger gibt. Gerade für Firmen ist das oft ein kaum überschaubares Risiko.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;❓ &lt;strong&gt;Benennung und Ort des Shares sind nicht eindeutig:&lt;/strong&gt; Ein empfangener Share taucht zunächst auf dem obersten Level der eigenen Nextcloud auf. Der Empfänger einer Freigabe kann diese jedoch an einen beliebigen Ort in seiner persönlichen Ordnerstruktur verschieben. Und er kann die Freigabe auch beliebig umbenennen. Möchte man also (rein kommunikativ) ein gemeinsames Dokument referenzieren, dann wird es kompliziert: Eine Freigabe kann beim Benutzer A einen anderen Namen und einen anderen Ort haben als bei Benutzer B.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Für sehr kleine und private Instanzen ist die einfache Share-Funktion meistens noch ausreichend. Mit steigender Anzahl von Nutzern und festen Teams wird die Funktion allerdings sehr schnell unübersichtlich. Im professionellen Umfeld ist die Funktion kaum zu beherrschen und birgt die Gefahr, dass Mitarbeiter ihre Dokumente nicht mehr finden, wenn Nutzer ausscheiden. Wer eine Übersicht über die Menge an eingerichteten Shares haben möchte, kann dazu das &lt;a href=&quot;https://github.com/nextcloud/sharelisting&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;sharelisting&lt;/code&gt;-Plugin&lt;/a&gt; nutzen.&lt;/p&gt;
&lt;h2 id=&quot;gruppenordner-fast-perfekt-aber-wenig-gepflegt&quot;&gt;Gruppenordner: Fast perfekt aber wenig gepflegt&lt;/h2&gt;
&lt;p&gt;Um viele der genannten Probleme bei der Arbeit mit Gruppen zu beseitigen, gibt es bereits &lt;a href=&quot;https://nextcloud.com/blog/new-version-of-groupfolders-app-is-coming/&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;seit 2017&lt;/a&gt; das offizielle &lt;a href=&quot;https://github.com/nextcloud/groupfolders&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;“groupfolders”-Plugin&lt;/a&gt; für Nextcloud. Kurioserweise gehören Gruppen zwar zum Standard-Funktionsumfang von Nextcloud, aber nicht das Plugin für Gruppenordner. Die zugrundeliegenden Gruppen können nur von Admins in Nextcloud verwaltet werden. Gruppenbasierte Funktionalitäten eignen sich also tendenziell gut für hierarchisch strukturierte Organisationen und Firmen, wo klar definiert ist, wer über eine Gruppenzuordnung entscheiden kann.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;👥 &lt;strong&gt;Ein Gruppenordner gehört tatsächlich einer Gruppe und nicht einem Nutzer:&lt;/strong&gt; Das Ausscheiden einzelner Gruppen-Mitglieder hat also keinen Einfluss auf die Verfügbarkeit der Daten. Trotzdem kann ein Gruppenordner auch mehreren Gruppen gleichzeitig zugänglich gemacht werden.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;👑 &lt;strong&gt;Gruppenordner können nur von Nextcloud-Admins angelegt werden.&lt;/strong&gt; Ein bisschen Selbstorganisation innerhalb eines Gruppenordners kann trotzdem über sogenannte “Advanced Permissions” sichergestellt werden.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;🔐 &lt;strong&gt;Zugriffsrechte in Gruppenordnern können detailliert eingestellt werden:&lt;/strong&gt; Über die “Advanced Permissions” kann einzelnen Gruppen oder Nutzer Lesen/Schreiben/Löschen/Erstelln und Teilen erlaubt oder verboten werden.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;🎯 &lt;strong&gt;Einheitlicher Ort und Benennung:&lt;/strong&gt; Gruppenordner sind für alle Nutzer immer auf der obersten Verzeichnisebene ansässig und heißen für alle Nutzer gleich.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;🚪 &lt;strong&gt;Nur für interne Nutzer mit Nextcloud-Account:&lt;/strong&gt; Gruppenordner können nicht verwendet werden, um Objekte mit Externen (Menschen ohne einen Account auf der betreffenden Nextcloud-Instanz) über einen Link zu teilen – das geht nur mit “Shares”.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Für die Zusammenarbeit in einer festen und hierarchischen Gruppen-Konstellation (wie sie üblicherweise im Arbeitsumfeld vorkommt) scheinen Groupfolders also eine überlegene Lösung zu sein. Trotzdem fühlt sich das groupfolders-Plugin manchmal wie ungewollter Ballast im Nextcloud-Universum an. Obwohl das Plugin offiziell von der Nextcloud GmbH verwaltet wird, bekommt es augenscheinlich nur &lt;a href=&quot;https://github.com/nextcloud/groupfolders/issues/1215&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;wenig Priorität und Ressourcen&lt;/a&gt; zugewiesen und weist deswegen teilweise monatelang gravierende Fehler auf.&lt;/p&gt;
&lt;h2 id=&quot;teams-aufgebohrte-share-funktion&quot;&gt;Teams: Aufgebohrte Share-Funktion&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/nextcloud/circles&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;“Teams”&lt;/a&gt;(früher “Circles”) ist eine dritte Variante um mit einer Gruppe Dateien und Ordner zu teilen. Teams wirken aber eher wie eine kleine Erweiterung der einfachen Share-Funktionalität. Anstelle einzelner Nutzer und (durch Admins definierte) Gruppen, können Dateien und Ordner damit auch mit selbstverwalteten Teams geteilt werden. Teams können von jedem Nutzer erstellt werden (vorausgesetzt die zugehörige Nextcloud-App ist installiert), und benötigen keinerlei Admin-Intervention. Teams ermöglichen also Selbstorganisation in Strukturen, wo keine klare Hierarchie oder Zuständigkeit herrscht. Die so erstellten Freigaben leiden allerdings unter denselben Problemen wie die einfachen Dateifreigaben:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;👤 &lt;strong&gt;Shares gehören immer einem spezifischen Nutzer:&lt;/strong&gt; Auch mit einem Team geteilte Freigaben können vom Freigabe-Ersteller jederzeit widerrufen werden, so dass der Inhalt nicht mehr verfügbar ist.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;❓ &lt;strong&gt;Benennung und Ort des Shares sind nicht eindeutig:&lt;/strong&gt; Die Ordner können bei unterschiedlichen Nutzern unterschiedliche Namen haben und sich an unterschiedlichen Orten befinden.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Teams sind vor allem für Szenarien geeignet, in denen sich Nutzer selbst organisieren möchten, und Gruppen auch ohne Zutun eines Administrators einrichten möchten.&lt;/p&gt;
&lt;h2 id=&quot;problem-es-mangelt-an-dokumentation-und-aufklärung&quot;&gt;Problem: Es mangelt an Dokumentation und Aufklärung&lt;/h2&gt;
&lt;p&gt;Natürlich ist es eine Luxus-Situation wenn es ein großes Ökosystem rund um Nextcloud gibt, mit verschiedenen Ansätzen zum Teilen von Objekten. Das Problem ist also nicht die Existenz der einzelnen Methoden, sondern ihre Koexistenz. Viele Nutzer kennen die Alternativen zur einfachen Share-Funktion und ihre jeweiligen Nachteile gar nicht. Auf der anderen Seite wird es vor allem dann problematisch, wenn mehrere Methoden gleichzeitig aktiv sind. Denn ein Groupfolder kann zusätzlich auch noch von einer Person “geteilt” werden. Es ist also denkbar, dass zwei Personen auf den gleichen Ordner zugreifen – eine Person über einen Share, und eine andere über ihre Gruppenzugehörigkeit. Das Erkennen der jeweiligen Freigabe-Methode durch das passende Icon in der Weboberfläche ist also eine wichtige Fähigkeit für Nextcloud-Nutzer.&lt;/p&gt;
&lt;figure class=&quot;&quot;&gt;p&gt;img]:mx-auto&quot;&gt;&lt;p&gt;&lt;img src=&quot;https://bitbetter.de/images/blog/nextcloud-freigabe-chaos/nextcloud-shares.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;&lt;/figure&gt;
&lt;p&gt;Admins können die einzelnen Methoden zwar deaktivieren, aber das hat Auswirkungen auf die Funktionalität: Wer ausschließlich mit Groupfolders arbeitet kann die “Share”-Funktion komplett deaktivieren, und so Verwirrungen vorbeugen. Wer aber weiterhin gelegentlich Dateien und Ordner mit externen Personen über einen Link teilen möchte, der muss die Share-Funktion weiterhin aktiviert lassen und muss somit seinen Nutzern beibringen, für welche Situation welche Freigabe-Methode die beste ist.&lt;/p&gt;
&lt;p&gt;Es wäre daher wünschenswert, wenn das Nextcloud-Interface auf das Vorhandensein mehrerer Freigabe-Methoden eingehen würde. Sobald mehrere Methoden installiert sind, könnten die Nutzer mit entsprechenden Hinweisen oder Hilfetexten aufgeklärt werden, ob sie wirklich die jeweils richtige Methode gewählt haben. Die Basis dafür wäre aber zunächst eine ausführliche, gegenüberstellende Dokumentation der drei Methoden mit ihren einzelnen Vor- und Nachteilen. Eine solche Dokumentation durch die Nextcloud GmbH wäre sehr hilfreich – auch gerade im Bezug auf die Produktvision und die Zukunftspläne der GmbH. Bisher geht das Nextcloud User Manual aber &lt;a href=&quot;https://docs.nextcloud.com/server/latest/user_manual/en/files/sharing.html&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;ausschließlich auf die Share-Funktion ein&lt;/a&gt;, ohne Vergleiche zu Teams und Groupfolders zu ziehen. Auch im offiziellen Nextcloud-Blog kam das Thema leider noch nicht vor.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update (2.9.24)&lt;/strong&gt;: Mit dem Release von Nextcloud Hub 8 (aka Nextcloud 29) wurde die “Circles”-Funktionalität in “Teams” umbenannt. An der Funktionsweise hat sich allerdings nichts geändert. Wir haben den Artikel entsprechend angepasst.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update (14.10.24)&lt;/strong&gt;: Wir haben die verschiedenen Arten, Dateien mit einer Gruppe zu teilen, inzwischen in eine Infografik verpackt, die &lt;a href=&quot;https://bitbetter.de/blog/nextcloud-freigaben-infografik&quot;&gt;in einem zweiten Blogpost&lt;/a&gt; vorgestellt wird.&lt;/p&gt;</content:encoded></item><item><title>Warum Strings in Camunda komplizierter sind, als man denkt</title><link>https://bitbetter.de/blog/camunda-string-variablen/</link><guid isPermaLink="true">https://bitbetter.de/blog/camunda-string-variablen/</guid><description>In Camunda-Prozessen sind String-Variablen ein häufiges Werkzeug um Nutzereingaben oder Ähnliches zu speichern. Was erstmal simpel klingt kommt allerdings mit einigen unerwarteten Limitierungen in Bezug auf die Länge der Variablem daher. Wir zeigen mögliche Alternativen.</description><pubDate>Wed, 31 Jan 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://camunda.com/platform-7/&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;Camunda&lt;/a&gt; ist ein beliebtes Workflow-Management-System auf Java-Basis, welches aufgrund seiner Open-Source-Lizenz (in Version 7) schnell eine große Verbreitung erreichte. Es eignet sich gut, um interne Geschäftsprozesse zu entwerfen und zu automatisieren. Allerdings kommt Camunda auch mit einigen Fallstricken daher, die wir erst nach und nach kennengelernt haben. Einer davon: String-Variablen in einem Camunda-Prozess.&lt;/p&gt;
&lt;h2 id=&quot;warum-strings-in-camunda-nicht-verwendet-werden-sollten&quot;&gt;Warum Strings in Camunda nicht verwendet werden sollten&lt;/h2&gt;
&lt;p&gt;Wer einen Camunda-Prozess entwickelt, wird an vielen Stellen mit diversen benutzerdefinierten Variablen konfrontiert: Zum einem gibt es die Prozess-Variablen, welche über die gesamte Laufzeit des Prozesses existieren, aber auch “lokale Variablen” (zum Beispiel Input/Output-Variablen). Dabei müssen sämtliche Variablen einen der folgenden Typen haben:&lt;/p&gt;
&lt;figure class=&quot;&quot;&gt;p&gt;img]:mx-auto&quot;&gt;&lt;img src=&quot;https://bitbetter.de/images/blog/camunda-string-variablen/camunda-variablen-typen.png&quot; alt=&quot;Mögliche Variablentypen in Camunda&quot;&gt;&lt;/figure&gt;
&lt;p&gt;Der hier erwähnte &lt;code&gt;string&lt;/code&gt;-Typ ist allerdings innerhalb von Camunda etwas anders zu verwenden, als Strings in Java oder anderen Programmiersprachen und Frameworks: In der &lt;a href=&quot;https://docs.camunda.org/manual/7.20/user-guide/process-engine/variables/#supported-variable-values&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;API-Dokumentation zu Prozessvariablen&lt;/a&gt; findet sich auch schon die Erklärung, warum Strings nicht in Camunda verwendet werden sollten:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;string&lt;/code&gt; values are stored in the database in a column of type &lt;code&gt;(n)varchar&lt;/code&gt;, with a length restriction of 4000 (2000 for Oracle). Depending on the database in use and the configured charset, this length restriction can result in different quantities of real characters. Variable value length is not validated inside the Camunda engine, but the values are sent to the database ‘as is’ and, in case the length restriction is exceeded, a database level exception will be thrown.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Prozessvariablen des Typs String werden also unverändert in der Datenbank in einer Spalte gespeichert, welche eine begrenzte Größe von 4000 Zeichen (2000 bei Oracle-Datenbanken) aufweist. Dieses Limit ist erstaunlich klein und schnell erreicht – gerade im Kontext von Nutzer-Eingaben oder Nachrichten aus einem Usertask, wo die gewünschte Länge nur schwer vorhersehbar ist. Ein Brief, der Inhalt einer Webseite oder der Text einer E-Mail können diese Grenze sehr leicht sprengen.&lt;/p&gt;
&lt;h2 id=&quot;für-prozess-variablen&quot;&gt;Für Prozess-Variablen&lt;/h2&gt;
&lt;p&gt;Im Allgemeinen ist die Empfehlung daher, Variablen in Klassen zu verpacken und dann als JSON oder XML gemeinsam zu serialisieren und in Camunda zu speichern, denn im Gegensatz zu Strings haben JSON- oder XML-Objekte in Camunda keine maximale Größe und können daher auch für große Datenmengen genutzt werden.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;String potentiallyLongString &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &amp;quot;Hello World![...]&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// Don&amp;#39;t do this:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;execution.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;setVariable&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&amp;quot;longText&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, potentiallyLongString);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// Instead do this:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;MyProcessVariables pv &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; MyProcessVariables&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;pv.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;setLongText&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(potentiallyLongString);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;ObjectValue pvSerialized &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Variables.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;objectValue&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(pv)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;                                    .&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;serializationDataFormat&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(Variables.SerializationDataFormats.JSON)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;                                    .&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;create&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;execution.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;setVariable&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&amp;quot;myProcessVariables&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, pvSerialized);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Im weiteren Verlauf eines Prozesses kann trotzdem weiterhin über alle gängigen Expression-Formate wie JUEL, FEEL, Freemarker, Groovy, etc. auf diese Variablen innerhalb eines Objekts zugegriffen werden – entweder über selbst erstellte Getter-Funktionen oder direkt über das jeweilige Feld.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// Zugriff im weiteren Verlauf des Workflows&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;${myProcessVariables.longText.&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;length&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; &amp;gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 50&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;für-lokale-variablen&quot;&gt;Für lokale Variablen&lt;/h2&gt;
&lt;p&gt;Etwas komplizierter wird es, wenn man einen langen Text an einen externen Service Task als Input weitergeben will. Externe Service Tasks werden häufig von verschiedenen Camunda-Prozessen aufgerufen, daher müssen die Prozessvariablen des jeweiligen Prozesses auf das Interface des externen Service Task gemappt werden.&lt;/p&gt;
&lt;p&gt;Die naheliegendste Version wäre folgende Konfiguration:&lt;/p&gt;
&lt;figure class=&quot;&quot;&gt;p&gt;img]:mx-auto&quot;&gt;&lt;img src=&quot;https://bitbetter.de/images/blog/camunda-string-variablen/camunda-expression-field.png&quot; alt=&quot;Direkter Zugriff auf eine Input-Variable innerhalb eines Objekts&quot;&gt;&lt;/figure&gt;
&lt;p&gt;Das Problem hierbei ist der “Variable Assignment Type”: Denn die lokale Variable, welche von Camunda hier erzeugt wird, ist nun wieder vom Typ String, welcher wieder der bekannten Limitierung auf 4000 Zeichen unterliegt und im Falle der Überschreitung zu einem Laufzeitfehler der Datenbank führt.&lt;/p&gt;
&lt;p&gt;Wie bei den Prozess-Variablen liegt die Lösung also auch in der Serialisierung eines Objektes nach JSON. Damit die Serialisierung selber gestaltet werden kann, muss an der Stelle des Variablen-Mappings Code ausgeführt werden.&lt;/p&gt;
&lt;p&gt;Das könnte zum Beispiel so aussehen:&lt;/p&gt;
&lt;figure class=&quot;&quot;&gt;p&gt;img]:mx-auto&quot;&gt;&lt;img src=&quot;https://bitbetter.de/images/blog/camunda-string-variablen/camunda-expression-getter.png&quot; alt=&quot;Zugriff über eine Getter-Funktion auf eine Input-Variable innerhalb eines Objekts&quot;&gt;&lt;/figure&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ObjectValue &lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;getMailBody&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;() {  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  MailBody mailBody &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; MailBody&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;this&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.longText); &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Variables.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;objectValue&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(mailBody)  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;                  .&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;serializationDataFormat&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(Variables.SerializationDataFormats.JSON)  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;                  .&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;create&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;();  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;                }&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;zusammenfassung&quot;&gt;Zusammenfassung&lt;/h2&gt;
&lt;p&gt;Prozess-Variablen in Camunda als Objekte zu implementieren hat mehrere Vorteile: Durch die dazugehörige Java-Klasse entsteht eine Typsicherheit, es gibt keine Größenbegrenzung durch die Camunda Datenbank und es trägt zur Übersichtlichkeit bei. An manchen Stellen, wie bei den lokalen Variablen, ist der Aufwand allerdings höher, da Probleme, welche theoretisch ausschließlich im BPMN-Diagramm gelöst werden könnten, in eine Java-Klasse ausgelagert werden müssen. Nichtsdestotrotz lohnt sich der Mehraufwand und man sollte primitive Typen in den Prozess-Variablen nur benutzen, wenn man sich sehr sicher über den Inhalt der Variable sein kann.&lt;/p&gt;</content:encoded></item><item><title>Ein Frontend für Parameo, den parametrischen Möbelgenerator</title><link>https://bitbetter.de/blog/projektvorstellung-parameo/</link><guid isPermaLink="true">https://bitbetter.de/blog/projektvorstellung-parameo/</guid><description>Ganz wie wir es mögen: Eine tolle Idee, ein nettes Team und dazu auch noch Open Source. Für unseren Kunden regenholz haben wir in der zweiten Jahreshälfte 2023 ein modernes Frontend für parametrische Open-Source-Möbel gebaut.</description><pubDate>Mon, 01 Jan 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Als Selbstständige ist eine unserer größten Freiheiten die Tatsache, dass wir uns aussuchen können, welche Projekte wir umsetzen möchten, und welche nicht. Als Fynn und René von &lt;a href=&quot;https://regenholz.de&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;regenholz&lt;/a&gt; im März 2023 zum ersten Mal auf uns zukamen, mussten wir daher nicht lange überlegen: Mit ihrer Idee für eine Web-Plattform zur parametrischen Generierung und Bestellung von Möbeln haben sie bei uns offene Türen eingerannt.&lt;/p&gt;
&lt;p&gt;Die Themen Handwerk, Open Source und Nachhaltigkeit liegen uns schon lange am Herz und durch unsere Verbindung über den &lt;a href=&quot;https://fabcity.hamburg&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;Hamburger Verein Fab City Hamburg e.V.&lt;/a&gt; wussten wir auch, dass unsere Teams menschlich gut zusammenpassen. Außerdem konnten wir in dem Projekt zusätzlich auf die Hilfe von &lt;a href=&quot;https://github.com/Agustina-Carrion&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;Agustina Carrion&lt;/a&gt;, einer befreundeten Junior-Entwicklerin zurückgreifen. So konnten wir gemeinsam zwischen Mai und Dezember 2023 ein passendes Web-Frontend für das parameo-Projekt. Dabei konnten wir uns technologisch voll austoben, weil der Kunde unserer Expertise bei der Auswahl der passenden Technologien vertraute. So entschieden wir uns für den folgenden Frontend-Stack um mit dem bestehenden Python-Backend zu kommunizieren:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Single Page Application mit &lt;a class=&quot;tooltag inline-flex align-top items-center gap-1&quot; href=&quot;https://bitbetter.de/leistungen/react/&quot;&gt; &lt;span class=&quot;flex items-center justify-center h-5 [&amp;_svg]:h-full [&amp;_svg]:w-auto&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;-11.5 -10.23174 23 20.46348&quot;&gt;&lt;title&gt;React Logo&lt;/title&gt;&lt;circle cx=&quot;0&quot; cy=&quot;0&quot; r=&quot;2.05&quot; fill=&quot;currentColor&quot; /&gt;&lt;g stroke=&quot;currentColor&quot; stroke-width=&quot;1&quot; fill=&quot;none&quot;&gt;&lt;ellipse rx=&quot;11&quot; ry=&quot;4.2&quot; /&gt;&lt;ellipse rx=&quot;11&quot; ry=&quot;4.2&quot; transform=&quot;rotate(60)&quot; /&gt;&lt;ellipse rx=&quot;11&quot; ry=&quot;4.2&quot; transform=&quot;rotate(120)&quot; /&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/span&gt; React &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Globales State Management über &lt;a href=&quot;https://github.com/pmndrs/zustand&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;Zustand&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;CSS mittels &lt;a href=&quot;https://tailwindcss.com/&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;Tailwind&lt;/a&gt;, &lt;a href=&quot;https://headlessui.com/&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;Headless UI&lt;/a&gt; und &lt;a href=&quot;https://heroicons.com/&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;HeroIcons&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Kommunikation mit dem Python-Backend über eine Rest API&lt;/li&gt;
&lt;li&gt;Vorschau von 3D-Modellen mit dem &lt;a href=&quot;https://modelviewer.dev/&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;Model Viewer von Google&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
  
&lt;p&gt;Der Quelltext von Parameo (sowohl das &lt;a href=&quot;https://gitlab.regenholz.de/bat/parameo-frontend&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;Frontend&lt;/a&gt;, als auch das &lt;a href=&quot;https://gitlab.regenholz.de/bat/bat-dev&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;Backend&lt;/a&gt;) stehen unter einer Open-Source-Lizenz und können von allen eingesehen und verwendet werden. Die aktuelle Live-Version ist unter &lt;a href=&quot;https://parameo.de&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;parameo.de&lt;/a&gt; zu finden.&lt;/p&gt;</content:encoded></item></channel></rss>