Controlling-Systeme entwickeln: Von Excel zu Echtzeit-Dashboard
Case Studies

Controlling-Systeme entwickeln: Von Excel zu Echtzeit-Dashboard

Carola Schulte
Invalid Date
16 min

“Können Sie aus unseren 47 Excel-Dateien ein echtes Controlling-System bauen?” – Diese Frage höre ich regelmäßig. Die Antwort: Ja, aber nicht eins zu eins. Excel ist ein Symptom, kein Konzept. Dahinter stecken echte Business-Prozesse, die digitalisiert werden wollen.

In diesem Artikel zeige ich anhand eines realen Projekts, wie wir ein Controlling-System für 15.000 Mitarbeiter entwickelt haben – von der Anforderungsanalyse bis zum produktiven Echtzeit-Dashboard.

TL;DR – Die Kernpunkte

  • Vom Excel-Chaos zur strukturierten Datenbank: ETL-Pipelines statt manueller Copy-Paste-Orgien
  • Echtzeit-KPIs statt Monatsreports: PostgreSQL Materialized Views + REFRESH CONCURRENTLY
  • Self-Service statt IT-Ticket-Flut: Filterable Dashboards mit gespeicherten Ansichten pro Rolle

Das Ausgangsproblem: Excel-Hölle

Status Quo beim Kunden (Mittelständisches Unternehmen, 15.000 MA)

47 Excel-Dateien über 12 Abteilungen verteilt:

  • Controlling_2024_v3_final_WIRKLICH_final.xlsx
  • Umsatz_Q4_korrigiert_neue_Zahlen.xlsx
  • Personal_Kosten_Jan_Feb_Mär.xlsx
  • … you get the idea

Typischer Monatsabschluss:

  • Tag 1-3: Controlling-Abteilung verschickt E-Mails: “Bitte Zahlen eintragen”
  • Tag 4-8: Erinnerungs-E-Mails an säumige Abteilungen
  • Tag 9-12: Zusammenführen der Dateien, manuelles Copy-Paste
  • Tag 13-15: Fehlersuche (“Warum stimmen die Summen nicht?”)
  • Tag 16-18: Berichte erstellen, Präsentationen vorbereiten
  • Tag 19: Präsentation im Management-Meeting

Ergebnis: Zahlen sind 3 Wochen alt, wenn das Management sie sieht. Entscheidungen basieren auf veralteten Daten.

Die Schmerzpunkte

  • Keine Historisierung: “Wie war der Wert letzten Monat?” → Alte Datei suchen, hoffen dass niemand sie überschrieben hat
  • Keine Validierung: Tippfehler, Copy-Paste-Fehler, falsche Formeln
  • Keine Rechte-Verwaltung: Jeder kann alles sehen und ändern
  • Keine Automatisierung: Alles manuell, fehleranfällig, zeitintensiv
  • Keine Auswertungen: “Welche Abteilung hatte die höchsten Überstunden?” → 2h Excel-Fummelei
  • Keine Datengüte & Governance: “Welche Zahl ist die richtige?” – Endlose Diskussionen, weil keine Single Source of Truth & kein Datenkatalog (Definitionen der KPIs)

Die Anforderungsanalyse: Was wollen wir wirklich?

Workshop-Tag 1: Prozess-Mapping

Wir haben nicht gefragt: “Was steht in euren Excel-Dateien?”

Wir haben gefragt: “Welche Entscheidungen trefft ihr basierend auf den Zahlen?”

Erkenntnisse:

Management-Ebene braucht:

  • KPIs auf einen Blick (Umsatz, Kosten, Marge, Mitarbeiter)
  • Drill-Down nach Abteilung/Region/Produkt
  • Trendverläufe (YoY, MoM)
  • Export für Board-Präsentationen

Abteilungsleiter brauchen:

  • Detailansicht ihrer Abteilung
  • Vergleich mit Plan-Zahlen
  • Historische Verläufe
  • “Was-wäre-wenn”-Simulationen

Controlling-Team braucht:

  • Daten-Import aus Vorsystemen (SAP, HR-System, CRM)
  • Validierungsregeln
  • Freigabe-Workflows
  • Audit-Trail (Wer hat wann was geändert?)

Geschäftsführung braucht:

  • Ein Dashboard. Drei Zahlen. Grün/Gelb/Rot.
  • Fertig.

Workshop-Tag 2: Datenmodell

Wir haben die Excel-Struktur ignoriert und von den Business-Objekten her gedacht:

Core-Entities:

  • Organisationsstruktur: Unternehmen → Bereiche → Abteilungen → Teams
  • Kostenstellen: Hierarchisch, mit Budget-Zuordnung
  • Mitarbeiter: Mit Zuordnung zu Kostenstellen, Verträgen, Gehaltsbändern
  • Umsatz: Nach Kunde, Produkt, Region, Periode
  • Kosten: Nach Kostenart, Kostenstelle, Periode
  • KPIs: Berechnete Kennzahlen (Marge, Produktivität, Fluktuation, etc.)

Zeitliche Dimension:

  • Plan-Daten: Budget, Forecast
  • Ist-Daten: Actual, monatlich/wöchentlich/täglich je nach KPI
  • Historisierung: Alle Änderungen mit Timestamp + User

Die Architektur: Layers statt Spaghetti

Layer 1: Daten-Import (ETL)

Quellen:

  • SAP: Finanzdaten via SAP RFC (BC_ABAP_CALL_RFC)
  • HR-System: Mitarbeiterdaten via REST-API
  • CRM: Umsatzdaten via CSV-Export (täglich um 02:00 Uhr)
  • Zeiterfassung: Arbeitsstunden via DB-Replikation (PostgreSQL Foreign Data Wrapper)

Import-Prozess:

// Beispiel: CRM-CSV-Import
class CrmUmsatzImporter {
    private PDO $pdo;
    private ValidationService $validator;
    private AuditLogger $audit;

    public function importDaily(string $csvPath): ImportResult {
        $this->audit->log("Import gestartet: $csvPath");

        // 1. CSV parsen
        $rows = $this->parseCsv($csvPath);

        // 2. Validierung
        $validated = [];
        $errors = [];

        foreach ($rows as $idx => $row) {
            try {
                $this->validator->validate($row, [
                    'kunde_id' => 'required|exists:kunden,id',
                    'umsatz' => 'required|decimal|min:0',
                    'datum' => 'required|date',
                    'produkt_id' => 'required|exists:produkte,id'
                ]);
                $validated[] = $row;
            } catch (ValidationException $e) {
                $errors[] = "Zeile $idx: {$e->getMessage()}";
            }
        }

        if (!empty($errors)) {
            $this->audit->log("Import fehlgeschlagen", $errors);
            throw new ImportException("Validierung fehlgeschlagen", $errors);
        }

        // 3. Transaktion: Alles oder nichts
        $this->pdo->beginTransaction();
        try {
            // Staging-Tabelle leeren
            $this->pdo->exec("TRUNCATE TABLE umsatz_staging");

            // Daten in Staging-Tabelle
            $stmt = $this->pdo->prepare("
                INSERT INTO umsatz_staging
                (kunde_id, umsatz, datum, produkt_id, import_timestamp)
                VALUES (:kunde_id, :umsatz, :datum, :produkt_id, NOW())
            ");

            foreach ($validated as $row) {
                $stmt->execute($row);
            }

            // Duplikate erkennen (Geschäftslogik: Datum + Kunde + Produkt = unique)
            $dupes = $this->pdo->query("
                SELECT COUNT(*)
                FROM umsatz_staging s
                JOIN umsatz u USING (kunde_id, produkt_id, datum)
            ")->fetchColumn();

            if ($dupes > 0) {
                throw new ImportException("$dupes Duplikate gefunden");
            }

            // Staging → Production
            $this->pdo->exec("
                INSERT INTO umsatz SELECT * FROM umsatz_staging
            ");

            $this->pdo->commit();
            $this->audit->log("Import erfolgreich", ['rows' => count($validated)]);

            return new ImportResult(count($validated), 0);
        } catch (Exception $e) {
            $this->pdo->rollBack();
            throw $e;
        }
    }
}

Guardrails:

  • Staging-Tabellen: Import läuft nie direkt in Production-Tabellen
  • Validierung vor Insert: PDO Prepared Statements + Application-Layer-Validierung
  • Atomare Transaktionen: Alles oder nichts
  • Audit-Log: Jeder Import wird geloggt (Timestamp, User, Datei, Result)
  • Retry-Mechanik: Bei Fehlern E-Mail an Controlling + automatischer Retry nach 1h
  • Schema-Drift-Detection: Contract Tests gegen CSV/API + Quarantäne für fehlerhafte Records
  • PII-Minimierung: Hashing/Pseudonymisierung in Staging; Zugriff per Row-Level Security (RLS) pro Abteilung

Layer 2: Datenbank-Modell (PostgreSQL)

Warum PostgreSQL statt MySQL?

  • Bessere Aggregations-Performance: Window Functions, CTEs
  • Materialized Views: Pre-computed KPIs mit REFRESH CONCURRENTLY
  • Array-Types: Speichern von Historien direkt in einer Spalte
  • JSONB: Flexible Metadaten (unterschiedliche KPIs pro Abteilung)
  • Partitioning: Zeitbasierte Partitionierung für große Tabellen

Beispiel: Umsatz-Tabelle mit Partitionierung

-- Parent-Tabelle
CREATE TABLE umsatz (
    id BIGSERIAL,
    kunde_id INTEGER NOT NULL,
    produkt_id INTEGER NOT NULL,
    umsatz NUMERIC(12,2) NOT NULL,
    datum DATE NOT NULL,
    created_at TIMESTAMP DEFAULT NOW(),
    created_by INTEGER
) PARTITION BY RANGE (datum);

-- Partitionen pro Monat (automatisch via Cron-Job)
CREATE TABLE umsatz_2025_01 PARTITION OF umsatz
    FOR VALUES FROM ('2025-01-01') TO ('2025-02-01');

CREATE TABLE umsatz_2025_02 PARTITION OF umsatz
    FOR VALUES FROM ('2025-02-01') TO ('2025-03-01');

-- Index nur auf aktuelle Partition → schnellere Inserts
CREATE INDEX idx_umsatz_2025_01_kunde ON umsatz_2025_01(kunde_id);
CREATE INDEX idx_umsatz_2025_01_datum ON umsatz_2025_01(datum);

Materialized Views für KPIs

-- Monatliche Umsätze pro Abteilung
CREATE MATERIALIZED VIEW kpi_umsatz_monat AS
SELECT
    DATE_TRUNC('month', u.datum) AS monat,
    a.id AS abteilung_id,
    a.name AS abteilung,
    SUM(u.umsatz) AS umsatz_gesamt,
    COUNT(DISTINCT u.kunde_id) AS anzahl_kunden,
    AVG(u.umsatz) AS avg_umsatz_pro_transaktion,

    -- Vergleich zum Vormonat
    LAG(SUM(u.umsatz)) OVER (
        PARTITION BY a.id ORDER BY DATE_TRUNC('month', u.datum)
    ) AS umsatz_vormonat,

    -- Wachstum %
    ROUND(
        (SUM(u.umsatz) - LAG(SUM(u.umsatz)) OVER (
            PARTITION BY a.id ORDER BY DATE_TRUNC('month', u.datum)
        )) / NULLIF(LAG(SUM(u.umsatz)) OVER (
            PARTITION BY a.id ORDER BY DATE_TRUNC('month', u.datum)
        ), 0) * 100,
        2
    ) AS wachstum_prozent

FROM umsatz u
JOIN kunden k ON u.kunde_id = k.id
JOIN abteilungen a ON k.abteilung_id = a.id
GROUP BY DATE_TRUNC('month', u.datum), a.id, a.name;

-- Index für schnellen Zugriff
CREATE INDEX idx_kpi_umsatz_monat_abteilung ON kpi_umsatz_monat(abteilung_id, monat);

-- Refresh täglich um 03:00 Uhr (nach allen Imports)
-- CONCURRENTLY = ohne Lock, läuft parallel zu Queries
REFRESH MATERIALIZED VIEW CONCURRENTLY kpi_umsatz_monat;

Performance:

  • Ohne Materialized View: Query 2.800ms (Live-Aggregation über 12M Zeilen)
  • Mit Materialized View: Query 8ms (Read from Index)
  • Refresh-Dauer: 45 Sekunden (nachts, wenn niemand da ist)

Refresh-Strategie:

Inkrementelle Refresh-Strategie (monatliche Partitionen) reduziert Nightly-Window weiter: REFRESH MATERIALIZED VIEW CONCURRENTLY kpi_umsatz_monat WHERE monat >= CURRENT_DATE - INTERVAL '1 month' – nur aktuelle + Vormonat, statt volle 36 Monate.

Layer 3: Business-Logik (PHP)

Domain-Driven Design:

src/
  Domain/
    Controlling/
      Entity/
        Abteilung.php
        KPI.php
        Periode.php
      Service/
        KPIBerechnung.php
        TrendAnalyse.php
        BudgetVergleich.php
      Repository/
        KPIRepository.php
      ValueObject/
        Zeitraum.php
        Budget.php

  Infrastructure/
    Persistence/
      PostgresKPIRepository.php
    Import/
      CrmImporter.php
      SapImporter.php

Beispiel: KPI-Berechnung

class KPIBerechnung {
    private KPIRepository $repo;

    public function berechneMarge(Abteilung $abt, Zeitraum $zeitraum): KPI {
        $umsatz = $this->repo->getUmsatz($abt, $zeitraum);
        $kosten = $this->repo->getKosten($abt, $zeitraum);

        $margeAbsolut = $umsatz - $kosten;
        $margeProzent = ($umsatz > 0) ? ($margeAbsolut / $umsatz * 100) : 0;

        // Ampel-Logik
        $budgetMarge = $abt->getBudgetMarge($zeitraum);
        $status = match(true) {
            $margeProzent >= $budgetMarge => Status::GRUEN,
            $margeProzent >= $budgetMarge * 0.9 => Status::GELB,
            default => Status::ROT
        };

        return new KPI(
            name: 'Marge',
            wert: $margeProzent,
            einheit: '%',
            status: $status,
            vergleichswert: $budgetMarge,
            zeitraum: $zeitraum
        );
    }
}

Layer 4: Frontend (Dashboard)

Tech-Stack:

  • Backend: PHP 8.2 + REST-API
  • Frontend: Vue.js 3 (leichtgewichtig, kein Overkill)
  • Charts: Chart.js (simpel, funktioniert)
  • State: Pinia (statt Vuex)
  • Build: Vite (schnell)

Dashboard-Features:

  • Echtzeit-Updates: WebSocket-Push bei Daten-Refresh (via Soketi/Laravel Echo Server)
  • Drill-Down: Klick auf KPI → Detail-Ansicht
  • Zeitraum-Auswahl: Tag/Woche/Monat/Quartal/Jahr + Custom-Range
  • Vergleichsperioden: vs. Vormonat, vs. Vorjahr, vs. Budget
  • Export: PDF (Management-Report), Excel (Detail-Daten), CSV (Rohdaten)
  • Gespeicherte Ansichten: Pro User/Rolle eigene Dashboard-Konfiguration

Performance-Optimierung:

// Lazy Loading: Charts werden erst geladen, wenn sichtbar
<script setup>
import { defineAsyncComponent } from 'vue'

const UmsatzChart = defineAsyncComponent(() =>
  import('./components/UmsatzChart.vue')
)
</script>

// Virtuelles Scrolling für lange Listen (10.000+ Abteilungen)
<template>
  <RecycleScroller
    :items="abteilungen"
    :item-size="60"
    key-field="id"
  >
    <template #default="{ item }">
      <AbteilungRow :abteilung="item" />
    </template>
  </RecycleScroller>
</template>

// Debouncing bei Filter-Inputs
watch(filterText, debounce(() => {
  loadAbteilungen(filterText.value)
}, 300))

Der Rollout: Big Bang vs. Inkrementell

Entscheidung: Inkrementell

Phase 1 (Monat 1-2): Management-Dashboard

  • Nur die Top-10-KPIs
  • Nur Monats-Ansicht
  • Nur Geschäftsführung + Controlling-Leiter
  • Ziel: Quick Win, Feedback sammeln

Phase 2 (Monat 3-4): Abteilungsleiter-Dashboards

  • Drill-Down pro Abteilung
  • Wochen-Ansicht
  • Plan-vs-Ist-Vergleich
  • Ziel: Buy-in der mittleren Führungsebene

Phase 3 (Monat 5-6): Controlling-Team-Features

  • Daten-Import-UI
  • Validierungsregeln selbst pflegen
  • Freigabe-Workflows
  • Ziel: Excel-Ablösung

Phase 4 (Monat 7-8): Self-Service

  • Filter, Gruppierungen, Sortierungen
  • Gespeicherte Ansichten
  • Ad-hoc-Exporte
  • Ziel: IT-Ticket-Reduktion

Phase 5 (Monat 9-10): Advanced Analytics

  • Trend-Prognosen (Machine Learning)
  • Was-wäre-wenn-Simulationen
  • Automatische Anomalie-Erkennung
  • Ziel: Proaktives Controlling

Die Ergebnisse: Harte Zahlen

Vor dem System (Excel-Prozess)

  • Monatsabschluss-Dauer: 19 Tage
  • FTE im Controlling: 8 Personen
  • Fehlerquote: ~15% (manuelle Korrekturen)
  • Report-Aktualität: 3 Wochen alt
  • Management-Zufriedenheit: 4/10

Nach dem System (12 Monate produktiv)

  • Monatsabschluss-Dauer: 2 Tage (nur noch Freigabe-Workflow)
  • FTE im Controlling: 5 Personen (3 umgeschult zu Business Analysts)
  • Fehlerquote: unter 1% (automatische Validierung)
  • Report-Aktualität: Live (täglich aktualisiert)
  • Management-Zufriedenheit: 9/10
  • ROI: Break-Even nach 14 Monaten

Zusätzliche Benefits:

  • Schnellere Entscheidungen: “Wir können auf Marktveränderungen jetzt innerhalb von Tagen reagieren statt Wochen”
  • Bessere Planung: “Forecasts sind 40% genauer geworden”
  • Transparenz: “Jede Abteilung sieht ihre eigenen Zahlen in Echtzeit”
  • Compliance: “Audit-Trail für jeden Datenpunkt – DSGVO & SOX-ready”

Lessons Learned: Was würden wir anders machen?

✅ Gut gelaufen

  • Inkrementeller Rollout: Statt Big Bang → weniger Risiko, frühe Erfolge
  • User-Workshops: Wir haben die Excel-Nutzer gefragt, nicht nur das Management
  • Materialized Views: Performance-Game-Changer
  • Staging-Tabellen: Fehlerhafte Imports crashen nicht das System
  • Audit-Trail: Hat uns zweimal bei Diskussionen gerettet

❌ Hätten wir besser machen können

  • Excel-Export zu früh: User wollten sofort Excel-Export → haben dann wieder in Excel gearbeitet statt im System. Hätten wir 3 Monate später einbauen sollen. Governance-Regel heute: Export nur für Board-Decks; operative Arbeit im System (Audit-Trail).
  • Zu viele Filter: “Wir brauchen Filter nach allem!” → UI-Overload. Hätten wir auf Top-10-Filter beschränken sollen.
  • Change Management unterschätzt: 20% der User haben sich 6 Monate lang gegen das System gewehrt. Hätten wir mehr Schulungen machen sollen. Adoptions-KPI heute: Weekly Active Analysts (WAA) + Export-Quote (Ziel: unter 10% nach Monat 3).
  • Kein Dark Mode: Geschäftsführer arbeitet abends am Dashboard → Augen tun weh. War ein 2-Zeilen-CSS-Fix, aber erst nach 3 Monaten Gemecker.

Kostenrahmen: Was kostet so ein System?

Unser Projekt (15.000 MA, 12 Abteilungen):

  • Discovery & Konzept: 40h @ 120€ = 4.800€
  • Entwicklung: 800h @ 95€ = 76.000€
  • Design & Frontend: 120h @ 95€ = 11.400€
  • Testing & QA: 80h @ 95€ = 7.600€
  • Schulungen: 40h @ 120€ = 4.800€
  • Projekt-Management: 100h @ 120€ = 12.000€

Gesamt: ~116.600€

Laufende Kosten (pro Jahr):

  • Hosting: 3.600€ (dedicated Server + Backup; SLO: 99,5% Uptime, p95 KPI-Query unter 150ms, p95 Refresh unter 60s)
  • Wartung & Support: 12.000€ (SLA: 8h Response-Time)
  • Feature-Entwicklung: 24.000€ (ca. 2-3 neue Features pro Jahr)

Gesamt p.a.: ~39.600€

Kleinere Systeme (unter 500 MA):

  • Entwicklung: 200-400h = 19.000-38.000€
  • Laufend: ~12.000€/Jahr

Technische Highlights: Code-Snippets

PostgreSQL: Recursive CTE für Org-Hierarchie

-- Alle Unter-Abteilungen einer Abteilung finden
WITH RECURSIVE abteilung_tree AS (
    -- Start: Gewählte Abteilung
    SELECT id, name, parent_id, 0 AS ebene
    FROM abteilungen
    WHERE id = :abteilung_id

    UNION ALL

    -- Rekursion: Alle Kinder
    SELECT a.id, a.name, a.parent_id, t.ebene + 1
    FROM abteilungen a
    JOIN abteilung_tree t ON a.parent_id = t.id
)
SELECT * FROM abteilung_tree ORDER BY ebene, name;

PostgreSQL: Percentile-Aggregation

-- Welche Abteilungen liegen über dem 75%-Perzentil bei Umsatz?
SELECT
    abteilung,
    umsatz,
    PERCENTILE_CONT(0.75) WITHIN GROUP (ORDER BY umsatz)
        OVER () AS p75,
    CASE
        WHEN umsatz > PERCENTILE_CONT(0.75) WITHIN GROUP (ORDER BY umsatz) OVER ()
        THEN 'Top 25%'
        ELSE 'Bottom 75%'
    END AS kategorie
FROM kpi_umsatz_monat
WHERE monat = DATE_TRUNC('month', CURRENT_DATE);

PHP: Data Transfer Objects (DTOs) für API

readonly class KPIDashboardDTO {
    public function __construct(
        public string $abteilung,
        public Zeitraum $zeitraum,
        public array $kpis, // KPI[]
        public ?TrendDTO $trend = null
    ) {}

    public static function fromEntity(Abteilung $abt, Zeitraum $zeitraum, array $kpis): self {
        return new self(
            abteilung: $abt->name,
            zeitraum: $zeitraum,
            kpis: array_map(fn(KPI $k) => KPIDashboardKPI::from($k), $kpis),
            trend: TrendDTO::calculate($kpis, $zeitraum)
        );
    }

    public function toArray(): array {
        return [
            'abteilung' => $this->abteilung,
            'zeitraum' => [
                'von' => $this->zeitraum->von->format('Y-m-d'),
                'bis' => $this->zeitraum->bis->format('Y-m-d')
            ],
            'kpis' => array_map(fn($k) => $k->toArray(), $this->kpis),
            'trend' => $this->trend?->toArray()
        ];
    }
}

Fazit: Excel ist tot, lang lebe das Controlling-System

Wann lohnt sich ein Custom-System?

Ja, wenn:

  • Mehr als 50 Mitarbeiter im Controlling/Management
  • Viele manuelle Prozesse (Copy-Paste, Konsolidierung)
  • Mehrere Datenquellen (SAP, CRM, HR-System, etc.)
  • Compliance-Anforderungen (Audit-Trail, DSGVO)
  • Schnelle Entscheidungen nötig (Real-time statt Monatsabschluss)

Nein, wenn:

  • Weniger als 10 Mitarbeiter → Excel reicht, Power BI reicht
  • Keine IT-Ressourcen für Wartung
  • Standardprozesse → lieber Standard-Software kaufen
  • Budget unter 30.000€ → zu klein für Custom-Entwicklung

Mein Angebot:

Ich entwickle seit 1998 Controlling-Systeme für mittelständische Unternehmen:

  • Discovery-Workshop (2 Tage) → Kostenlose Erstanalyse
  • Proof-of-Concept (4 Wochen) → 1-2 KPIs live testen
  • MVP (3-4 Monate) → Core-Features produktiv
  • Rollout & Schulung → Sukzessive alle User

Kontakt:

📧 die@entwicklerin.net 🌐 www.entwicklerin.net 📍 Lüneburg & Remote


Über die Autorin: Carola Schulte ist Software-Architektin und entwickelt seit 1998 Business-Apps. Schwerpunkte: PHP/PostgreSQL, Security-by-Design, DSGVO-konforme Systeme. 1,8M+ Lines of Code in Produktion.


Hat Ihnen dieser Artikel geholfen? Teilen Sie ihn mit Kollegen im Controlling, die noch in der Excel-Hölle feststecken.

Weitere Artikel in dieser Serie:

Carola Schulte

Über Carola Schulte

Software-Architektin mit 25+ Jahren Erfahrung. Spezialisiert auf robuste Business-Apps mit PHP/PostgreSQL, Security-by-Design und DSGVO-konforme Systeme. 1,8M+ Lines of Code in Produktion.

Projekt im Kopf?

Lassen Sie uns besprechen, wie ich Ihre Anforderungen umsetzen kann – kostenlos und unverbindlich.

Kostenloses Erstgespräch