feat(site): rewrite public copy around the batch chain

Replace slogan-heavy bilingual copy with concrete industrial prose on home, about, work, notes, and contact, without invented metrics or em dashes.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Jan Wagner
2026-08-16 19:05:53 +02:00
co-authored by Cursor
parent 7e8313c96e
commit 042ea88d94
13 changed files with 383 additions and 199 deletions
+12 -1
View File
@@ -1,5 +1,7 @@
import Link from "next/link";
import { getPerson } from "@/content/person"; import { getPerson } from "@/content/person";
import { getDictionary } from "@/i18n/dictionaries"; import { getDictionary } from "@/i18n/dictionaries";
import { publicPath } from "@/i18n/routes";
import { pageMetadata } from "@/lib/metadata"; import { pageMetadata } from "@/lib/metadata";
import { requireLocale } from "@/lib/locale"; import { requireLocale } from "@/lib/locale";
@@ -22,7 +24,7 @@ export default async function AboutPage({ params }: PageProps<"/[lang]/about">)
<p className="mt-6 font-mono text-xs uppercase tracking-[0.16em] text-mute"> <p className="mt-6 font-mono text-xs uppercase tracking-[0.16em] text-mute">
{person.role} · {person.location} {person.role} · {person.location}
</p> </p>
<div className="mt-16 grid gap-8 border-t border-line pt-12 md:grid-cols-3"> <div className="mt-16 grid max-w-2xl gap-6 border-t border-line pt-12">
{person.story.map((paragraph) => ( {person.story.map((paragraph) => (
<p key={paragraph.slice(0, 24)} className="leading-8 text-mute-strong"> <p key={paragraph.slice(0, 24)} className="leading-8 text-mute-strong">
{paragraph} {paragraph}
@@ -40,6 +42,15 @@ export default async function AboutPage({ params }: PageProps<"/[lang]/about">)
</li> </li>
))} ))}
</ol> </ol>
<section className="mt-20 border-t border-line pt-12">
<p className="max-w-2xl leading-8 text-mute-strong">{dict.about.close}</p>
<Link
href={publicPath(locale, "contact")}
className="mt-8 inline-block border border-copper bg-copper px-6 py-3 text-sm font-medium text-ink"
>
{dict.actions.startConversation}
</Link>
</section>
</main> </main>
); );
} }
+11 -1
View File
@@ -31,7 +31,17 @@ export default async function ContactPage({
<p className="kicker">{dict.contact.kicker}</p> <p className="kicker">{dict.contact.kicker}</p>
<h1 className="display mt-5 text-5xl md:text-6xl">{dict.contact.title}</h1> <h1 className="display mt-5 text-5xl md:text-6xl">{dict.contact.title}</h1>
<p className="mt-8 max-w-xl text-lg leading-8 text-mute-strong">{dict.contact.lede}</p> <p className="mt-8 max-w-xl text-lg leading-8 text-mute-strong">{dict.contact.lede}</p>
<p className="mt-4 max-w-xl text-sm leading-7 text-mute">{dict.contact.reply}</p> <div className="mt-8 max-w-xl">
<p className="font-mono text-xs uppercase tracking-[0.16em] text-mute">{dict.contact.needTitle}</p>
<ul className="mt-4 grid gap-3 text-sm leading-7 text-mute-strong">
{dict.contact.need.map((item) => (
<li key={item} className="border-l border-line-strong pl-4">
{item}
</li>
))}
</ul>
</div>
<p className="mt-6 max-w-xl text-sm leading-7 text-mute">{dict.contact.reply}</p>
<p className="mt-3 text-sm"> <p className="mt-3 text-sm">
<a className="text-copper" href={`mailto:${site.email}`}> <a className="text-copper" href={`mailto:${site.email}`}>
{dict.contact.emailDirect}: {site.email} {dict.contact.emailDirect}: {site.email}
+13 -6
View File
@@ -17,8 +17,8 @@ export async function generateMetadata({ params }: PageProps<"/[lang]">) {
locale, locale,
"home", "home",
locale === "de" locale === "de"
? "Jan Wagner Software für anspruchsvolle Prozesse" ? "Jan Wagner · Software für Satzzettel, Silo und Schichtabschluss"
: "Jan Wagner — Software for demanding processes", : "Jan Wagner · software for batch tickets, silos and the shift close",
dict.home.lede, dict.home.lede,
); );
} }
@@ -29,7 +29,7 @@ export default async function HomePage({ params }: PageProps<"/[lang]">) {
const person = getPerson(locale); const person = getPerson(locale);
const [featured, ...rest] = getProjects(locale); const [featured, ...rest] = getProjects(locale);
const offers = getOffers(locale); const offers = getOffers(locale);
const notes = getNotes(locale).slice(0, 3); const notes = getNotes(locale).slice(0, 4);
return ( return (
<main> <main>
@@ -120,9 +120,16 @@ export default async function HomePage({ params }: PageProps<"/[lang]">) {
<section className="border-t border-line"> <section className="border-t border-line">
<div className="mx-auto w-full max-w-6xl px-6 py-20 md:px-10"> <div className="mx-auto w-full max-w-6xl px-6 py-20 md:px-10">
<p className="kicker">{dict.home.notesKicker}</p> <div className="flex flex-wrap items-end justify-between gap-6">
<h2 className="display mt-4 text-4xl md:text-5xl">{dict.home.notesTitle}</h2> <div>
<div className="mt-12 grid gap-10 md:grid-cols-3"> <p className="kicker">{dict.home.notesKicker}</p>
<h2 className="display mt-4 text-4xl md:text-5xl">{dict.home.notesTitle}</h2>
</div>
<Link href={publicPath(locale, "notes")} className="text-sm text-copper">
{dict.actions.allNotes}
</Link>
</div>
<div className="mt-12 grid gap-10 md:grid-cols-2">
{notes.map((note) => ( {notes.map((note) => (
<article key={note.slug}> <article key={note.slug}>
<time className="font-mono text-xs text-mute" dateTime={note.date}> <time className="font-mono text-xs text-mute" dateTime={note.date}>
+16 -6
View File
@@ -52,7 +52,13 @@ export default async function CaseStudyPage({ params }: PageProps<"/[lang]/work/
<section className="grid gap-10 border-b border-line py-16 md:grid-cols-2"> <section className="grid gap-10 border-b border-line py-16 md:grid-cols-2">
<h2 className="font-serif text-3xl">{dict.case.situation}</h2> <h2 className="font-serif text-3xl">{dict.case.situation}</h2>
<p className="leading-8 text-mute-strong">{project.situation}</p> <div className="grid gap-5">
{project.situation.map((paragraph) => (
<p key={paragraph.slice(0, 32)} className="leading-8 text-mute-strong">
{paragraph}
</p>
))}
</div>
</section> </section>
<section className="border-b border-line py-16"> <section className="border-b border-line py-16">
@@ -70,7 +76,7 @@ export default async function CaseStudyPage({ params }: PageProps<"/[lang]/work/
{project.images.length ? ( {project.images.length ? (
<section className="border-b border-line py-16"> <section className="border-b border-line py-16">
<p className="kicker">{dict.case.proof}</p> <p className="kicker">{dict.case.proof}</p>
<h2 className="mt-4 font-serif text-3xl md:text-4xl">{dict.home.workTitle}</h2> <h2 className="mt-4 font-serif text-3xl md:text-4xl">{dict.case.galleryTitle}</h2>
<div className="mt-10 grid gap-8"> <div className="mt-10 grid gap-8">
{project.images.map((image) => ( {project.images.map((image) => (
<figure key={image.src} className="overflow-hidden border border-line bg-ink-raised"> <figure key={image.src} className="overflow-hidden border border-line bg-ink-raised">
@@ -90,9 +96,7 @@ export default async function CaseStudyPage({ params }: PageProps<"/[lang]/work/
<section className="py-16"> <section className="py-16">
<p className="kicker">{dict.case.decisions}</p> <p className="kicker">{dict.case.decisions}</p>
<h2 className="mt-4 font-serif text-3xl md:text-4xl"> <h2 className="mt-4 font-serif text-3xl md:text-4xl">{dict.case.modelTitle}</h2>
{locale === "de" ? "Der fachliche Kern als Modell." : "The domain core as a model."}
</h2>
<div className="mt-10"> <div className="mt-10">
<ProcessRail stages={project.stages} /> <ProcessRail stages={project.stages} />
</div> </div>
@@ -101,7 +105,13 @@ export default async function CaseStudyPage({ params }: PageProps<"/[lang]/work/
<section className="grid gap-10 border-t border-line py-16 md:grid-cols-2"> <section className="grid gap-10 border-t border-line py-16 md:grid-cols-2">
<div> <div>
<h2 className="font-serif text-3xl">{dict.case.outcome}</h2> <h2 className="font-serif text-3xl">{dict.case.outcome}</h2>
<p className="mt-4 leading-8 text-mute-strong">{project.outcome}</p> <div className="mt-4 grid gap-5">
{project.outcome.map((paragraph) => (
<p key={paragraph.slice(0, 32)} className="leading-8 text-mute-strong">
{paragraph}
</p>
))}
</div>
</div> </div>
<div> <div>
<h2 className="font-serif text-3xl">{dict.case.lesson}</h2> <h2 className="font-serif text-3xl">{dict.case.lesson}</h2>
+2 -2
View File
@@ -24,11 +24,11 @@ const newsreader = Newsreader({
export const metadata: Metadata = { export const metadata: Metadata = {
metadataBase: new URL(site.url), metadataBase: new URL(site.url),
title: { title: {
default: "Jan Wagner Software für anspruchsvolle Prozesse", default: "Jan Wagner · Software für Satzzettel, Silo und Schichtabschluss",
template: "%s · W-MAKE", template: "%s · W-MAKE",
}, },
description: description:
"Jan Wagner entwickelt robuste digitale Systeme für industrielle Prozesse — mit Domänenverständnis, klarer Architektur und Verantwortung für den Betrieb.", "Jan Wagner entwickelt Software für Rezept, Satzzettel, Siloabzug und Tagesabschluss in der Behälterglas-Produktion. Deutschland, remote und vor Ort.",
}; };
export default async function RootLayout({ export default async function RootLayout({
+1 -1
View File
@@ -13,7 +13,7 @@ export function HomeJsonLd() {
}, },
areaServed: "DE", areaServed: "DE",
description: description:
"Software für industrielle und anspruchsvolle Geschäftsprozesse — Domänenverständnis, Architektur, Betrieb.", "Software für Rezept, Satzzettel, Silo und Tagesabschluss in der Behälterglas-Produktion.",
}; };
return ( return (
+12 -10
View File
@@ -7,13 +7,14 @@ const copy = {
"E-Mail: eldov@w-make.de", "E-Mail: eldov@w-make.de",
"Website: https://w-make.com", "Website: https://w-make.com",
"Anschrift und Firmierung nach § 5 DDG werden ergänzt, sobald die Unternehmensdaten für den öffentlichen Auftritt feststehen. Bis dahin gilt die genannte E-Mail als Kontakt für redaktionelle und rechtliche Anliegen.", "Anschrift und Firmierung nach § 5 DDG werden ergänzt, sobald die Unternehmensdaten für den öffentlichen Auftritt feststehen. Bis dahin gilt die genannte E-Mail als Kontakt für redaktionelle und rechtliche Anliegen.",
"Inhalte zu Projekten enthalten keine vertraulichen Kundendaten. Externe Links, etwa zur Produkt-Demo unter batch.w-make.com, liegen außerhalb dieser Site.", "Diese Site stellt die öffentliche Arbeit von Jan Wagner dar: Positionierung, zwei Produkte, Notizen und ein Kontaktformular. Sie ist kein Kundenportal und enthält keine vertraulichen Hallendaten.",
"Inhalte zu Projekten enthalten keine Kundennamen, keine internen Mengen und keine Betriebsgeheimnisse. Externe Links, etwa zur Produkt-Demo unter batch.w-make.com, liegen außerhalb dieser Site. Für den Inhalt verlinkter Seiten sind deren Betreiber verantwortlich.",
], ],
privacy: [ privacy: [
"Diese Website speichert Anfragen aus dem Kontaktformular, um sie zu beantworten. Pflichtfelder sind Name, E-Mail und Nachricht. Das optionale Feld Unternehmen hilft bei der Einordnung.", "Diese Website speichert Anfragen aus dem Kontaktformular, um sie zu beantworten. Pflichtfelder sind Name, E-Mail und Nachricht. Das optionale Feld Unternehmen hilft bei der Einordnung. Das optionale Feld Einstieg ordnet die Anfrage einer der drei Zusammenarbeitsformen zu, falls Sie eine gewählt haben.",
"Die Daten werden in einer lokalen SQLite-Datei auf dem Server abgelegt und per E-Mail an eldov@w-make.de weitergeleitet. Es gibt kein Tracking-Pixel, kein Marketing-Cookie und keine Weitergabe an Werbenetzwerke.", "Die Daten werden in einer lokalen SQLite-Datei auf dem Server abgelegt und per E-Mail an eldov@w-make.de weitergeleitet. Es gibt kein Tracking-Pixel, kein Marketing-Cookie und keine Weitergabe an Werbenetzwerke. Es findet keine Profilbildung und keine Reichweitenmessung statt.",
"Server-Logs des Hosters können IP-Adresse und Zeitpunkt einer Anfrage enthalten. Das ist Betrieb, nicht Analyse.", "Server-Logs des Hosters können IP-Adresse, Zeitpunkt und aufgerufenen Pfad einer Anfrage enthalten. Das ist Betrieb, nicht Analyse. Die Logs dienen der Störungssuche und der Absicherung, nicht der Werbung.",
"Sie können Auskunft, Berichtigung oder Löschung Ihrer Anfrage verlangen, indem Sie an eldov@w-make.de schreiben. Eine vollständige Datenschutzerklärung nach DSGVO folgt, sobald Anschrift und Firmierung feststehen.", "Sie können Auskunft, Berichtigung oder Löschung Ihrer Anfrage verlangen, indem Sie an eldov@w-make.de schreiben. Nennen Sie dafür bitte die verwendete E-Mail-Adresse und den ungefähren Zeitpunkt. Eine vollständige Datenschutzerklärung nach DSGVO folgt, sobald Anschrift und Firmierung feststehen.",
], ],
}, },
en: { en: {
@@ -22,13 +23,14 @@ const copy = {
"Email: eldov@w-make.de", "Email: eldov@w-make.de",
"Website: https://w-make.com", "Website: https://w-make.com",
"Street address and legal entity under German law will be added once the public company data is final. Until then the email above is the contact for editorial and legal matters.", "Street address and legal entity under German law will be added once the public company data is final. Until then the email above is the contact for editorial and legal matters.",
"Project pages do not include confidential client data. External links, such as the product demo at batch.w-make.com, live outside this site.", "This site presents Jan Wagners public work: positioning, two products, notes and a contact form. It is not a client portal and does not hold confidential plant data.",
"Project pages do not include client names, internal volumes or trade secrets. External links, such as the product demo at batch.w-make.com, live outside this site. The operators of linked pages are responsible for their content.",
], ],
privacy: [ privacy: [
"This website stores inquiries from the contact form in order to reply. Required fields are name, email, and message. The optional company field helps with context.", "This website stores inquiries from the contact form in order to reply. Required fields are name, email and message. The optional company field helps with context. The optional starting-point field maps the inquiry to one of the three ways of working together, if you chose one.",
"Data is written to a local SQLite file on the server and forwarded by email to eldov@w-make.de. There is no tracking pixel, no marketing cookie, and no sharing with advertising networks.", "Data is written to a local SQLite file on the server and forwarded by email to eldov@w-make.de. There is no tracking pixel, no marketing cookie and no sharing with advertising networks. There is no profiling and no audience measurement.",
"The hosts server logs may include IP address and request time. That is operations, not analytics.", "The hosts server logs may include IP address, request time and the path requested. That is operations, not analytics. Logs are used for incident response and hardening, not for advertising.",
"You can request access, correction, or deletion of your inquiry by writing to eldov@w-make.de. A full GDPR privacy notice will follow once street address and legal entity are final.", "You can request access, correction or deletion of your inquiry by writing to eldov@w-make.de. Please include the email address you used and the approximate time. A full GDPR privacy notice will follow once street address and legal entity are final.",
], ],
}, },
} as const; } as const;
+96 -30
View File
@@ -1,6 +1,6 @@
import type { Locale } from "@/i18n/routes"; import type { Locale } from "@/i18n/routes";
export type NoteSlug = "buchungen" | "satzzettel" | "domaene"; export type NoteSlug = "buchungen" | "satzzettel" | "domaene" | "betrieb" | "grenzen";
export type LocalizedNote = { export type LocalizedNote = {
slug: NoteSlug; slug: NoteSlug;
@@ -11,26 +11,57 @@ export type LocalizedNote = {
}; };
const notes: Record<NoteSlug, { date: string; de: Omit<LocalizedNote, "slug" | "date">; en: Omit<LocalizedNote, "slug" | "date"> }> = { const notes: Record<NoteSlug, { date: string; de: Omit<LocalizedNote, "slug" | "date">; en: Omit<LocalizedNote, "slug" | "date"> }> = {
betrieb: {
date: "2026-07-22",
de: {
title: "Migration, Sicherung, Schichtbeginn: Betrieb ist Teil des Produkts",
summary: "Ein Abschluss, den man nur in der Demo hinbekommt, hält der Halle nicht stand. Die gemeinsame Uhr und die Wiederholbarkeit gehören in die Anwendung.",
body: [
"In vielen Projekten kommt der Betrieb hinterher. Zuerst die Masken, dann irgendwann eine Sicherung, irgendwann eine Migration, irgendwann die Frage, warum der Schichtbeginn auf jedem Rechner anders ist. In der Behälterglas-Produktion ist das keine Nachsorge. Der Tag hat eine Uhr. Der Bestand hat eine Datei. Der Abschluss muss sich wiederholen lassen, wenn um sechs jemand denselben Knopf noch einmal drückt.",
"Schichtbeginn als gemeinsamer Zeitpunkt klingt klein. Er ist es nicht. Ohne ihn schreibt die eine Station auf gestern, die andere auf heute, das Protokoll auf eine dritte Variante. Alarme, Satzzettel und Übergabe beziehen sich dann auf verschiedene Tage, obwohl alle im selben Raum stehen. Die Uhr gehört in das System, nicht in die lokale Zeitzone des Browsers und nicht in ein Formularfeld, das jemand einmal gesetzt hat.",
"Dasselbe gilt für den Tagesabschluss. Ein Export als CSV ist kein Abschluss. Ein Abschluss ist ein Zustand: welche Sätze gelten, welcher Silostand steht, welche Übergabe die nächste Schicht vorfindet. Wenn dieser Zustand nur unter Laborbedingungen entsteht, weil drei Felder zufällig gefüllt waren, ist er in der Halle wertlos. Man muss denselben Abschluss am nächsten Tag wieder erzeugen können, mit denselben Regeln.",
"Migrationen sind der unauffällige Teil. Eine Rezeptversion, ein Buchungstyp, ein neues Feld am Silo: das ändert das, was gestern schon gebucht war. Eine Migration, die alte Sätze umschreibt, macht die Geschichte weich. Eine Migration, die vorwärts gilt und die Vergangenheit in Ruhe lässt, hält den Tag erklärbar. Das ist langsamer zu schreiben. Es spart die Nacht, in der niemand mehr sagen kann, welche Zahl zur Wanne gehört.",
"Sicherung ist verwandt. Eine SQLite-Datei, die den Bestand trägt, ist kein Anhängsel. Sie ist der Bestand. Wer sie nicht regelmäßig und prüfbar sichert, hat den Betrieb an die Festplatte eines einzelnen Rechners gebunden. Wiederherstellung muss geübt sein, nicht nur dokumentiert. Ein Wiederanlauf um Schichtbeginn ist der Test, nicht ein Restore am Sonntagnachmittag.",
"Für technische Entscheider: nehmen Sie Migration, Sicherung und die gemeinsame Uhr in den Umfang des Produkts auf, bevor die erste Maske festgezogen wird. Was danach nachgerüstet wird, sitzt daneben und wird im Ernstfall umgangen.",
],
},
en: {
title: "Migration, backup, shift start: operations are part of the product",
summary: "A close that only works in the demo will not hold on the floor. The shared clock and the ability to repeat the close belong in the application.",
body: [
"In many projects operations come afterwards. Screens first, then a backup at some point, a migration at some point, and eventually the question why shift start differs on every machine. In container-glass production that is not aftercare. The day has a clock. Stock has a file. The close must be repeatable if someone presses the same control again at six.",
"Shift start as a shared instant sounds small. It is not. Without it one station writes to yesterday, another to today, the log to a third variant. Alarms, tickets and handover then refer to different days while everyone stands in the same room. The clock belongs in the system, not in the browsers local timezone and not in a form field someone set once.",
"The same holds for the day close. A CSV export is not a close. A close is a state: which batches hold, where the silo stands, what handover the next shift will find. If that state only appears under lab conditions because three fields happened to be filled, it is worthless on the floor. You must be able to produce the same close the next day, with the same rules.",
"Migrations are the quiet part. A recipe version, a booking type, a new field on the silo: that changes what was already booked yesterday. A migration that rewrites old batches softens history. A migration that applies forwards and leaves the past alone keeps the day explainable. It is slower to write. It saves the night in which nobody can say which figure belongs to the furnace.",
"Backup is related. A SQLite file that carries stock is not an accessory. It is the stock. If you do not back it up regularly and in a way you can check, you have tied operations to one machines disk. Restore has to be practised, not only documented. A restart at shift start is the test, not a restore on Sunday afternoon.",
"For technical decision-makers: put migration, backup and the shared clock into the product scope before the first screen is locked. What is bolted on later sits beside the flow and will be bypassed when it matters.",
],
},
},
buchungen: { buchungen: {
date: "2026-08-01", date: "2026-08-01",
de: { de: {
title: "Warum fachliche Buchungen nicht einfach gelöscht werden dürfen", title: "Warum fachliche Buchungen nicht einfach gelöscht werden dürfen",
summary: "In einem Produktionsmodell ist Löschen selten die richtige Korrektur. Meist braucht es einen Gegenbeleg.", summary: "Nach dem Wiegen hat eine Buchung Silo und Protokoll schon verändert. Korrektur heißt Gegenbuchung, Storno oder Abgleich, nicht DELETE.",
body: [ body: [
"In vielen Anwendungen ist Delete der schnellste Weg, einen Fehler zu korrigieren. In einem Batch- oder Materialfluss ist das gefährlich: Eine Buchung hat bereits Mengen, Zeiten und nachgelagerte Zustände verändert.", "In vielen Anwendungen ist Delete der schnellste Weg, einen Fehler zu korrigieren. Der Datensatz war falsch, also verschwindet er. In einem Batch- oder Materialfluss ist das gefährlich. Eine Buchung hat bereits Mengen, Zeiten und nachgelagerte Zustände verändert. Der Silo ist leichter, das Gemengebuch hat eine Zeile, das Tagesprotokoll rechnet mit diesem Satz. Die Zeile zu löschen macht den Fehler unsichtbar. Den Effekt beseitigt sie nicht.",
"Die bessere Operation ist eine Gegenbuchung. Sie erhält die Geschichte, macht die Korrektur sichtbar und hält Auswertungen ehrlich. Soft-Delete allein reicht nicht, wenn der fachliche Effekt schon eingetreten ist.", "Ein praktisches Bild: jemand hat den falschen Rohstoff gewählt, die Waage hat gewogen, der Abzug ist gelaufen. Soft-Delete auf dieser Zeile lässt den Silostand stehen, wo er nach dem Abzug ist, während die Maske so tut, als sei nichts geschehen. Die nächste Schicht erbt eine Differenz, die niemand mehr an einem Beleg festmachen kann. Auswertungen werden höflich. Der Bestand wird es nicht.",
"Das klingt nach mehr Aufwand. Es spart den größeren: nächtliche Differenzen zwischen Silo, Rezept und Protokoll, die niemand mehr erklären kann.", "Die bessere Operation ist eine Gegenbuchung. Sie erhält die Geschichte, macht die Korrektur sichtbar und hält Auswertungen ehrlich. Storno ist der verwandte Fall, wenn der Satz nicht stattfinden soll, der Abzug aber schon geschrieben hat. Abgleich ist der Fall, in dem Halle und System auseinandergelaufen sind und jemand den Unterschied benennen muss, statt ihn zu überschreiben.",
"Für technische Entscheider: Modellieren Sie Irreversibilität dort, wo die Domäne sie verlangt — nicht dort, wo das ORM eine Cascade anbietet.", "Das klingt nach mehr Aufwand in der Anwendung. Es spart den größeren: nächtliche Differenzen zwischen Silo, Rezept und Protokoll, die niemand mehr erklären kann. Eine Gegenbuchung kostet eine Maske und eine Regel. Eine unsichtbare Korrektur kostet eine Schichtsuche.",
"CRUD hilft hier wenig als Denkmodell. Create und Read passen. Update nach dem Wiegen ist oft schon zu viel. Delete ist fast immer falsch, sobald ein nachgelagerter Zustand geschrieben hat. Wer das ORM eine Cascade anbieten lässt, hat die fachliche Frage an die Bibliothek abgegeben.",
"Für technische Entscheider: modellieren Sie Unumkehrbarkeit dort, wo die Halle sie verlangt. Nicht dort, wo das Formular einen Papierkorb-Knopf hergibt. Wenn Sie unsicher sind, fragen Sie, was um sechs noch stimmen muss, wenn jemand um halb drei den falschen Satz angelegt hat.",
], ],
}, },
en: { en: {
title: "Why domain bookings must not simply be deleted", title: "Why domain bookings must not simply be deleted",
summary: "In a production model, delete is rarely the right correction. Usually you need a reversing entry.", summary: "After weighing, a booking has already changed silo and log. Correction means a reversing entry, a cancellation or a reconcile, not DELETE.",
body: [ body: [
"In many applications, delete is the fastest way to fix a mistake. In a batch or material flow that is dangerous: a booking has already changed quantities, times, and downstream state.", "In many applications, delete is the fastest way to fix a mistake. The record was wrong, so it disappears. In a batch or material flow that is dangerous. A booking has already changed quantities, times and downstream state. The silo is lighter, the batch book has a line, the daily log is counting on that batch. Deleting the row hides the mistake. It does not undo the effect.",
"The better operation is a reversing entry. It keeps history, makes the correction visible, and keeps reports honest. Soft-delete alone is not enough once the domain effect has occurred.", "A practical picture: someone picked the wrong raw material, the scale weighed, the deduction ran. Soft-delete on that row leaves the silo where it is after the deduction, while the screen pretends nothing happened. The next shift inherits a gap that nobody can pin to a voucher. Reports become polite. Stock does not.",
"It looks like more work. It avoids the larger kind: overnight gaps between silo, recipe, and log that nobody can explain.", "The better operation is a reversing entry. It keeps history, makes the correction visible and keeps reports honest. Cancellation is the related case when the batch should not take place but the deduction has already been written. Reconcile is the case in which hall and system have drifted and someone has to name the difference instead of overwriting it.",
"For technical decision-makers: model irreversibility where the domain requires it — not where the ORM offers a cascade.", "It looks like more work in the application. It avoids the larger kind: overnight gaps between silo, recipe and log that nobody can explain. A reversing entry costs a screen and a rule. An invisible correction costs a shift spent searching.",
"CRUD is a weak model here. Create and read still fit. Update after weighing is often already too much. Delete is almost always wrong once a downstream state has been written. If you let the ORM offer a cascade, you have handed the domain question to the library.",
"For technical decision-makers: model irreversibility where the hall requires it. Not where the form offers a bin button. If you are unsure, ask what still has to be true at six if someone opened the wrong batch at half past two.",
], ],
}, },
}, },
@@ -38,22 +69,53 @@ const notes: Record<NoteSlug, { date: string; de: Omit<LocalizedNote, "slug" | "
date: "2026-08-10", date: "2026-08-10",
de: { de: {
title: "Vom Satzzettel zum Tagesprotokoll", title: "Vom Satzzettel zum Tagesprotokoll",
summary: "Zwei Dokumente, ein Modell. Wenn sie getrennt leben, entsteht der Bruch genau dort, wo die Schicht übergibt.", summary: "Zwei Dokumente, ein Modell. Leben sie getrennt, entsteht der Bruch genau dort, wo die Schicht übergibt.",
body: [ body: [
"Der Satzzettel sagt, was in die Wanne soll. Das Tagesprotokoll sagt, was der Tag daraus gemacht hat. In Papierstapeln sind das zwei Welten. In Software dürfen sie das nicht bleiben.", "Der Satzzettel sagt, was in die Wanne soll: welche Rezeptversion, welcher Zielsatz, welche Redox-Lage. Das Tagesprotokoll sagt, was der Tag daraus gemacht hat: welche Sätze gelaufen sind, was die Schicht der nächsten hinterlässt, ob der Tag geschlossen ist. In Papierstapeln sind das zwei Welten. In Software dürfen sie das nicht bleiben.",
"Ein durchgängiges Modell verbindet Rezept, Satz, Verbrauch und Abschluss. Nicht, weil Integration modern ist, sondern weil die Schichtübergabe sonst wieder in Kopien und Nebenrechnungen stattfindet.", "Der Bruch sieht harmlos aus. Labor oder Arbeitsvorbereitung druckt den Zettel. Die Waage arbeitet ihn ab. Jemand überträgt Verbrauch in eine zweite Liste. Schichtende wird in einem dritten Blatt festgehalten, oft mit Zahlen, die unterwegs gerundet oder mündlich korrigiert wurden. Am nächsten Morgen streiten drei Blätter, und niemand hat Unrecht in seinem System. Sie haben nur kein gemeinsames.",
"Die Oberfläche kann schlank sein. Die Verbindung im Datenmodell darf es nicht. Wer nur Screens liefert, liefert den Bruch mit.", "Ein durchgängiges Modell verbindet Rezept, Satz, Verbrauch und Abschluss. Nicht weil Integration modern ist, sondern weil die Schichtübergabe sonst wieder in Kopien und Nebenrechnungen stattfindet. Der Zettel ist dann der Auftrag, das Gemengebuch die Ausführung, das Protokoll der Zustand am Abend. Dieselbe Rezeptversion steht an allen drei Stellen. Ändert sich die Version, gilt das für neue Sätze, nicht für die, die schon gewogen sind.",
"Lernpunkt: Zeichnen Sie zuerst den Tag eines Schmelzers, nicht die Sitemap der Anwendung.", "Die Oberfläche kann schlank sein. Der Schmelzer braucht keine Architekturzeichnung. Er braucht den nächsten Satz, die Lage von Oxid und Redox, und am Abend eine Übergabe, die ohne Zusatzzettel auskommt. Die Verbindung darf trotzdem nicht nur in der Oberfläche liegen. Wer nur Screens liefert und die Daten getrennt hält, liefert den Bruch mit. Er sieht nur ordentlicher aus.",
"Ein Test für das Modell: kann die nächste Schicht den Tag erklären, ohne den Vorgänger anzurufen. Wenn dafür noch eine Nebenrechnung in einem Heft nötig ist, ist die Kette nicht geschlossen. Wenn der Abschluss denselben Tag morgen noch einmal erzeugen lässt, ist sie es eher.",
"Lernpunkt für die Architektur: zeichnen Sie zuerst den Tag eines Schmelzers, nicht die Sitemap der Anwendung. Welche Zahl um sechs, welche nach dem Wiegen, welche um Schichtende. Die Masken folgen daraus. Umgekehrt entstehen hübsche Formulare und dieselbe Übergabe wie auf Papier.",
], ],
}, },
en: { en: {
title: "From batch ticket to daily log", title: "From batch ticket to daily log",
summary: "Two documents, one model. If they live apart, the fracture appears exactly where the shift hands over.", summary: "Two documents, one model. If they live apart, the fracture appears exactly where the shift hands over.",
body: [ body: [
"The batch ticket says what should go into the furnace. The daily log says what the day made of it. On paper these are two worlds. In software they must not stay that way.", "The batch ticket says what should go into the furnace: which recipe version, which target weight, which redox state. The daily log says what the day made of it: which batches ran, what the shift leaves the next, whether the day is closed. On paper these are two worlds. In software they must not stay that way.",
"A continuous model connects recipe, batch, consumption, and close. Not because integration is fashionable, but because otherwise the handover happens again in copies and side calculations.", "The fracture looks harmless. Lab or planning prints the ticket. The scale works it off. Someone copies consumption into a second list. End of shift is recorded on a third sheet, often with figures rounded or corrected by mouth along the way. The next morning three sheets disagree, and nobody is wrong inside their own system. They simply do not share one.",
"The interface can stay lean. The connection in the data model cannot. Screens alone ship the fracture with them.", "A continuous model connects recipe, batch, consumption and close. Not because integration is fashionable, but because otherwise the handover happens again in copies and side calculations. The ticket is then the order, the batch book the execution, the log the state in the evening. The same recipe version stands at all three points. If the version changes, that applies to new batches, not to those already weighed.",
"Takeaway: draw a melters day first, not the application sitemap.", "The interface can stay lean. A melter does not need an architecture diagram. He needs the next batch, the oxide and redox position, and in the evening a handover that does not need an extra sheet. The connection still must not live only in the interface. Screens alone, with the data kept apart, ship the fracture with them. It only looks tidier.",
"A test for the model: can the next shift explain the day without calling the previous one. If a side calculation in a notebook is still required, the chain is not closed. If the close can produce the same day again tomorrow, it is closer.",
"Takeaway for architecture: draw a melters day first, not the application sitemap. Which figure at six, which after weighing, which at the end of the shift. The screens follow from that. The other way round you get handsome forms and the same handover as on paper.",
],
},
},
grenzen: {
date: "2026-08-14",
de: {
title: "Warum Batch und Studio zwei Produkte sind",
summary: "Derselbe Fachkern, zwei Zuschnitte. Wer Rezeptarbeit und Hallenbetrieb in eine Anwendung zwingt, trägt Entwürfe in den Bestand oder den Bestand an den Schreibtisch.",
body: [
"W-Make Batch und Batchmaker Studio teilen den Kern für Rezept, Rohstoff und Glaschemie. Sie teilen nicht den Zuschnitt. Batch trägt Silos, Schichten, Buchungen und den Tagesabschluss. Studio trägt Versionen, Validierung und den druckfertigen Satzzettel. Die Trennung ist keine Marketinglinie. Sie folgt aus zwei verschiedenen Tagen.",
"Der Tag in der Halle beginnt mit einer Uhr, einem Bestand und Sätzen, die nach dem Wiegen nicht mehr Entwurf sind. Der Tag am Schreibtisch beginnt mit einer Rezeptversion, die sich noch ändern darf, einer Oxidrechnung, die man prüfen will, und einem Zettel, der noch nicht gebucht ist. In ein Produkt gezwungen, gelten Hallenregeln für Entwürfe oder Entwurfsregeln für den Bestand. Beides ist falsch.",
"Ein Beispiel: in Studio soll eine Rezeptversion scheitern dürfen. Die Validierung ist ein Schritt, den jemand sieht. In Batch darf dieselbe Version, sobald Sätze darauf laufen, nicht still durch eine neue ersetzt werden. Die Kostenrechnung hängt an der Version, die gewogen wurde, nicht an der, die gerade in der Maske offen ist. Vermischt man die Produkte, landet genau diese stille Ersetzung in der Halle.",
"Umgekehrt: wer Studio mit Silos und Schichten vollpackt, zwingt die Rezeptarbeit in einen Betriebsmodus, den niemand am Schreibtisch braucht. Man eröffnet eine Schicht, um einen Zettel zu drucken. Man pflegt Bestände, die dort keine sind. Das Werkzeug wird schwer, und der Fachkern verschwindet hinter Masken, die zur Halle gehören.",
"Technisch heißt die Grenze: eigener Server, eigene Datei, eigene Oberfläche. Der Kern für Oxid, Redox und Satzgröße kann verwandt sein. Die Buchung darf es nicht. Ein Satzzettel aus Studio ist Entwurf und Druckvorlage. Gebucht wird er erst in Batch, wenn die Waage und der Silo dazukommen.",
"Für technische Entscheider: Produktgrenzen sind Architektur. Fragen Sie, welcher Tag in der Anwendung stattfinden soll, bevor Sie Module zusammenlegen, weil der Kern ähnlich aussieht. Ähnliche Rechnung ist kein Grund für dasselbe Betriebsmodell.",
],
},
en: {
title: "Why Batch and Studio are two products",
summary: "The same domain core, two cuts. Force recipe work and plant operations into one application and you carry drafts into stock, or stock onto the desk.",
body: [
"W-Make Batch and Batchmaker Studio share the core for recipe, raw material and glass chemistry. They do not share the cut. Batch carries silos, shifts, bookings and the day close. Studio carries versions, validation and the print-ready batch ticket. The split is not a marketing line. It follows from two different days.",
"The day in the hall begins with a clock, a stock position and batches that are no longer drafts after weighing. The day at the desk begins with a recipe version that may still change, an oxide calculation you want to inspect, and a ticket that is not yet booked. Forced into one product, hall rules apply to drafts or draft rules apply to stock. Both are wrong.",
"An example: in Studio a recipe version is allowed to fail. Validation is a step someone can see. In Batch, once batches are running on that version, it must not be silently replaced by a new one. Cost hangs on the version that was weighed, not on the one currently open in the screen. Mix the products and that silent replacement lands in the hall.",
"The other way round: pack Studio with silos and shifts and you force recipe work into an operations mode nobody needs at the desk. You open a shift in order to print a ticket. You maintain stock that is not stock there. The tool gets heavy, and the domain core disappears behind screens that belong to the hall.",
"Technically the boundary means: its own server, its own file, its own interface. The core for oxide, redox and batch size can be related. The booking must not. A ticket from Studio is a draft and a print-ready sheet. It is booked in Batch only when the scale and the silo join in.",
"For technical decision-makers: product boundaries are architecture. Ask which day is supposed to take place in the application before you merge modules because the core looks similar. Similar calculation is not a reason for the same operating model.",
], ],
}, },
}, },
@@ -61,22 +123,26 @@ const notes: Record<NoteSlug, { date: string; de: Omit<LocalizedNote, "slug" | "
date: "2026-08-16", date: "2026-08-16",
de: { de: {
title: "Was Domänenwissen in der Produktentwicklung verändert", title: "Was Domänenwissen in der Produktentwicklung verändert",
summary: "Wer den Prozess kennt, baut andere Invarianten. Nicht mehr Features — andere Fehler, die man sich nicht leisten kann.", summary: "Wer den Prozess kennt, stellt andere Fragen. Es entstehen andere Regeln, nicht mehr Felder auf derselben Maske.",
body: [ body: [
"In der Behälterglas-Produktion ist ein Rezept keine Liste von Zutaten. Es ist eine Entscheidung über Temperatur, Redox, Kosten und das, was die nächste Schicht vorfindet. Wer das nur als Formularfelder modelliert, verliert die Reihenfolge, in der die Realität zuschlägt.", "In der Behälterglas-Produktion ist ein Rezept keine Liste von Zutaten. Es ist eine Entscheidung über Temperatur, Redox, Kosten und das, was die nächste Schicht vorfindet. Wer das nur als Formularfelder modelliert, verliert die Reihenfolge, in der die Realität zuschlägt. Zuerst gilt eine Version. Dann wird gewogen. Dann ist der Silo leichter. Dann muss die Wanne zur Rechnung passen. Dann übergibt die Schicht. Die Maske, die alle Felder gleichzeitig anbietet, kennt diese Folge nicht.",
"Domänenwissen ändert deshalb zuerst die Fragen. Nicht: Welche Maske fehlt? Sondern: Was darf nach dem Wiegen nicht mehr stillschweigend verschwinden? Was muss eine Schicht der nächsten hinterlassen, ohne mündliche Zusatzrechnung?", "Domänenwissen ändert deshalb zuerst die Fragen. Nicht welche Maske fehlt, sondern was nach dem Wiegen nicht mehr still verschwinden darf. Was eine Schicht der nächsten hinterlassen muss, ohne mündliche Zusatzrechnung. Welche Zahl am Abend noch zur Halle passt, wenn drei Stationen geschrieben haben. Aus diesen Fragen folgen Invarianten. Aus Invarianten folgt das Modell. Aus dem Modell folgen Masken, nicht umgekehrt.",
"Daraus folgen unspektakuläre technische Konsequenzen. Buchungen werden Zustände. Berechnungen bleiben an Versionen gebunden. Validierung ist ein Schritt, den jemand sehen kann. Migrationen und Backups gehören zum Produkt, nicht zur Nachsorge.", "Die technischen Konsequenzen sind selten spektakulär. Buchungen werden Zustände. Berechnungen bleiben an Versionen gebunden. Validierung ist ein Schritt, den jemand sehen kann. Migrationen und Sicherungen gehören zum Produkt. Schichtbeginn ist eine Uhr. Nichts davon gewinnt einen Architekturpreis. Alles davon entscheidet, ob um sechs noch jemand den Vortag erklären kann.",
"Für technische Entscheider: Stellen Sie jemanden, der den Prozess erklären kann, an den Anfang der Architektur — nicht an das Ende der Abnahme. Der teuerste Fehler ist der, den das System erlaubt, weil niemand ihn für fachlich gehalten hat.", "Ohne Domäne entstehen andere Fehler, oft spät. Ein Soft-Delete, das den Silo nicht zurückdreht. Eine Rezeptänderung, die in bereits gewogene Sätze hineinschreibt. Eine Kostenrechnung, die die offene Maske nimmt statt der Version an der Waage. Eine Schichtübergabe, die nur als Kommentarfeld existiert. Jeder einzelne Punkt sieht in der Demo unauffällig aus. In der Halle addieren sie sich zu einer Nacht.",
"Deshalb gehört jemand, der den Prozess erklären kann, an den Anfang der Architektur, nicht an das Ende der Abnahme. Abnahme findet den fehlenden Knopf. Sie findet selten die fehlende Unumkehrbarkeit. Der teuerste Fehler ist der, den das System erlaubt, weil niemand ihn für fachlich gehalten hat.",
"Für Auftraggeber und Teams: verlangen Sie die Erklärung des Tages, bevor Sie die Erklärung des Stacks verlangen. Wenn der Tag nicht erzählbar ist, wird die Software ihn auch nicht tragen. Wenn er erzählbar ist, wird der Stack vergleichsweise langweilig. Das ist in diesem Feld ein gutes Zeichen.",
], ],
}, },
en: { en: {
title: "What domain knowledge changes in product development", title: "What domain knowledge changes in product development",
summary: "Knowing the process produces different invariants. Not more features — different failures you cannot afford.", summary: "Knowing the process changes the questions. You get different rules, not more fields on the same screen.",
body: [ body: [
"In container-glass production a recipe is not a list of ingredients. It is a decision about temperature, redox, cost, and what the next shift will find. Model that as form fields and you lose the order in which reality hits.", "In container-glass production a recipe is not a list of ingredients. It is a decision about temperature, redox, cost and what the next shift will find. Model that as form fields and you lose the order in which reality hits. First a version holds. Then weighing happens. Then the silo is lighter. Then the furnace has to match the calculation. Then the shift hands over. The screen that offers every field at once does not know that sequence.",
"Domain knowledge therefore changes the questions first. Not: which screen is missing? But: what must not silently disappear after weighing? What must one shift leave the next without a side calculation in someones head?", "Domain knowledge therefore changes the questions first. Not which screen is missing, but what must not silently disappear after weighing. What one shift must leave the next without a side calculation in someones head. Which figure still has to match the hall in the evening after three stations have written. Invariants follow from those questions. The model follows from the invariants. Screens follow from the model, not the other way round.",
"The technical consequences are unspectacular. Bookings become states. Calculations stay bound to versions. Validation is a step someone can see. Migrations and backups belong to the product, not to aftercare.", "The technical consequences are rarely spectacular. Bookings become states. Calculations stay bound to versions. Validation is a step someone can see. Migrations and backups belong to the product. Shift start is a clock. None of that wins an architecture prize. All of it decides whether someone can still explain yesterday at six.",
"For technical decision-makers: put someone who can explain the process at the start of the architecture — not at the end of acceptance. The most expensive defect is the one the system allows because nobody treated it as domain.", "Without the domain, different defects appear, often late. A soft-delete that does not reverse the silo. A recipe change that writes into batches already weighed. A cost calculation that takes the open screen instead of the version at the scale. A shift handover that exists only as a comment field. Each point looks quiet in the demo. On the floor they add up to a night.",
"That is why someone who can explain the process belongs at the start of the architecture, not at the end of acceptance. Acceptance finds the missing button. It rarely finds the missing irreversibility. The most expensive defect is the one the system allows because nobody treated it as domain.",
"For clients and teams: ask for the explanation of the day before you ask for the explanation of the stack. If the day cannot be told, the software will not carry it either. If it can be told, the stack will look comparatively dull. In this field that is a good sign.",
], ],
}, },
}, },
+10 -10
View File
@@ -7,34 +7,34 @@ const copy = {
{ {
id: "process", id: "process",
title: "Prozessanalyse", title: "Prozessanalyse",
text: "Ich analysiere einen bestehenden digitalen Prozess, identifiziere fachliche Bruchstellen und übersetze die Ergebnisse in eine priorisierte technische Roadmap.", text: "Wir gehen den Ablauf durch, der heute in Kopien, Nebenrechnungen oder stillen Korrekturen endet. Das Ergebnis ist eine Karte der Bruchstellen und eine Reihenfolge, in der sich der Aufwand lohnt. Der Gang durch die Halle kommt zuerst. Ein Workshop oder ein Tool-Vergleich ersetzt ihn nicht.",
}, },
{ {
id: "product", id: "product",
title: "Produkt- und Backend-Entwicklung", title: "Produkt und Backend",
text: "Ich entwickle oder stabilisiere Anwendungen, bei denen Datenmodell, Geschäftslogik und Betrieb wichtiger sind als ein schneller Prototyp.", text: "Ich entwickle oder stabilisiere die Anwendung, in der Datenmodell, Fachlogik und Betrieb zusammenfallen. Typisch sind Buchungsketten, Stammdaten mit Versionen, Berechnungen an der Rezeptversion und ein Abschluss, den eine Schicht wiederholen kann.",
}, },
{ {
id: "collaboration", id: "collaboration",
title: "Technische Zusammenarbeit", title: "Mitarbeit im Team",
text: "Ich unterstütze Teams bei Architekturentscheidungen, schwierigen Integrationen und der Überführung von gewachsenem Code in ein wartbares System.", text: "Ich sitze an der Stelle, an der Fachbereich und Engineering sich sonst verfehlen: Integrationen, gewachsener Code, Entscheidungen, die nach dem Go-Live teuer werden. Ich liefere nicht nebenher eine zweite Architektur.",
}, },
], ],
en: [ en: [
{ {
id: "process", id: "process",
title: "Process analysis", title: "Process analysis",
text: "I analyse an existing digital process, make domain fractures visible, and turn the findings into a prioritised technical roadmap.", text: "We walk the flow that currently ends in copies, side calculations or quiet corrections. You get a map of the breaks and an order in which the work is worth doing. The walk through the hall comes first. A workshop or a tool comparison does not replace it.",
}, },
{ {
id: "product", id: "product",
title: "Product and backend development", title: "Product and backend",
text: "I build or stabilise applications where the data model, domain logic, and operations matter more than a fast prototype.", text: "I build or steady the application where the data model, domain logic and operations meet. Typical work is booking chains, versioned master data, calculations bound to the recipe version, and a close a shift can repeat.",
}, },
{ {
id: "collaboration", id: "collaboration",
title: "Technical collaboration", title: "Work inside a team",
text: "I help teams with architecture decisions, difficult integrations, and turning accumulated code into a maintainable system.", text: "I sit where operations and engineering otherwise miss each other: integrations, accumulated code, decisions that get expensive after go-live. I do not deliver a second architecture on the side.",
}, },
], ],
} as const; } as const;
+34 -20
View File
@@ -3,47 +3,61 @@ import type { Locale } from "@/i18n/routes";
const copy = { const copy = {
de: { de: {
role: "Software Engineer für industrielle Prozesse", role: "Software Engineer für industrielle Prozesse",
location: "Deutschland · remote und vor Ort", location: "Deutschland, remote und vor Ort",
story: [ story: [
"Mein Ausgangspunkt ist die Behälterglas-Produktion: Rezepte, Rohstoffe, Silos, Schichten, Satzzettel, Tagesprotokolle. Das sind keine abstrakten Entities. Es sind Zustände, die Geld, Material und Sicherheit berühren.", "Ich komme aus der Behälterglas-Produktion. Dort habe ich gelernt, wo ein Rezept aufhört, Zutatenliste zu sein: an der Wanne. Temperatur, Redox, Kosten und das, was die nächste Schicht vorfindet, hängen an derselben Entscheidung. Wer das in voneinander unabhängige Masken zerlegt, baut den Bruch in die Software ein.",
"Daraus folgt, wie ich Software baue. Fachliche Buchungen werden nicht einfach gelöscht. Berechnungen bleiben nachvollziehbar. Fehlerpfade sind Teil des Modells, nicht ein nachträglicher Dialog.", "Deshalb beginne ich nicht beim Framework. Ich frage, welche Buchung nach dem Wiegen nicht mehr still verschwinden darf, was eine Schicht der nächsten hinterlassen muss, und welche Zahl am Abend noch zur Halle passt. Erst daraus entsteht ein Datenmodell.",
"Für Kunden heißt das: jemand, der den Prozess zuerst versteht und dann ein System hinterlässt, das ein anderer Engineer erklären kann. Für Arbeitgeber heißt das: Ownership statt Ticket-Abarbeitung.", "In der Praxis heißt das oft: Satzzettel, Gemengebuch, Siloabzug, Oxidrechnung und Tagesprotokoll gehören in eine Kette. Eine Rezeptversion, die am Morgen galt, darf am Abend nicht still durch eine andere ersetzt werden, nur weil jemand in der Maske gespeichert hat. Änderungen gelten vorwärts, nicht in die bereits gebuchte Vergangenheit.",
"W-Make Batch ist der öffentliche Beleg für diese Haltung. Die Demo unter batch.w-make.com zeigt den Ablauf, nicht einen Kundenstand. Vertrauliche Mengen, Rezepte und Hallendaten bleiben dort, wo sie hingehören. Batchmaker Studio ist die Werkbank daneben: derselbe Fachkern für Rezept und Glaschemie, ohne Silos, ohne Schichten, ohne laufende Buchungen.",
"Für Auftraggeber bedeutet das jemanden, der den Prozess erklären kann, bevor er eine Architektur vorschlägt. Für ein Team bedeutet das, dass ich den Teil übernehme, an dem Fachlichkeit, Daten und Betrieb zusammenfallen, und ihn in einem Zustand lasse, den ein anderer Engineer lesen kann.",
"Ich arbeite aus Deutschland, remote und vor Ort. Ich nehme Fälle an, bei denen der Ablauf wichtiger ist als die Demo. Casino, Trading und interne Infrastruktur gehören nicht auf diese Site und nicht in ein Erstgespräch mit einem Industriekunden.",
], ],
principles: [ principles: [
{ {
title: "Fachlichkeit zuerst", title: "Die Halle vor der Sitemap",
text: "Ich beginne beim realen Prozess, nicht beim Framework. Regeln werden sichtbar, prüfbar und nachvollziehbar.", text: "Ich zeichne den Tag eines Schmelzers, eines Mischers, eines Schichtführers. Welche Zahl braucht er um sechs, welche um Schichtende, welche darf er nicht mehr überschreiben. Daraus folgt die Anwendung.",
}, },
{ {
title: "Verlässliche Zustände", title: "Buchungen als Zustände",
text: "Buchungen, Migrationen und Fehlerpfade so, dass das System unter realen Bedingungen verständlich bleibt.", text: "Ein Verbrauch, der Silo und Protokoll verändert hat, ist kein Datensatz zum Löschen. Korrektur heißt Gegenbuchung, Storno oder Abgleich. Soft-Delete kaschiert den Effekt, beseitigt ihn nicht.",
}, },
{ {
title: "Kleine, tragfähige Lösungen", title: "Rechnung dort, wo man sie prüfen kann",
text: "Keine Architektur um ihrer selbst willen. Erst die kleinste Lösung, die den Prozess dauerhaft besser macht.", text: "Oxid, Redox, Satzgröße und Kosten gehören in eigene Module, nicht in die Oberfläche und nicht in den Router. Wenn die Zahl später streitet, muss der Weg zur Formel ohne Umweg über drei Schichten Code liegen.",
},
{
title: "Betrieb ist Teil des Produkts",
text: "Migrationen, Sicherung, Schichtbeginn als gemeinsame Uhr, ein Abschluss, den man wiederholen kann: das ist keine Nachsorge. Ein System, das nur in der Demo schließt, schließt in der Halle nicht.",
}, },
], ],
}, },
en: { en: {
role: "Software engineer for industrial processes", role: "Software engineer for industrial processes",
location: "Germany · remote and on site", location: "Germany, remote and on site",
story: [ story: [
"I start from container-glass production: recipes, raw materials, silos, shifts, batch tickets, daily logs. These are not abstract entities. They are states that touch money, material, and safety.", "I come from container-glass production. That is where I learned where a recipe stops being a list of ingredients: at the furnace. Temperature, redox, cost and what the next shift finds all hang on the same decision. Split that into unrelated screens and you ship the fracture with the software.",
"That shapes how I build software. Domain bookings are not simply deleted. Calculations stay traceable. Failure paths belong in the model, not in an afterthought dialog.", "So I do not start with the framework. I ask which booking must not vanish quietly after weighing, what one shift must leave the next, and which figure still has to match the hall in the evening. Only then does a data model appear.",
"For clients that means someone who understands the process first and leaves a system another engineer can explain. For employers it means ownership, not ticket throughput.", "In practice that often means: batch ticket, batch book, silo deduction, oxide calculation and daily log belong in one chain. A recipe version that held in the morning must not be silently replaced in the evening just because someone hit save. Changes apply forwards, not into the booked past.",
"W-Make Batch is the public evidence for that stance. The demo at batch.w-make.com shows the flow, not a client installation. Confidential volumes, recipes and plant data stay where they belong. Batchmaker Studio is the workbench beside it: the same domain core for recipes and glass chemistry, without silos, without shifts, without live bookings.",
"For a client that means someone who can explain the process before proposing an architecture. For a team it means I take the part where domain, data and operations meet, and leave it in a state another engineer can read.",
"I work from Germany, remotely and on site. I take cases where the flow matters more than the demo. Casino, trading and internal infrastructure do not belong on this site, and not in a first conversation with an industrial customer.",
], ],
principles: [ principles: [
{ {
title: "Domain first", title: "The hall before the sitemap",
text: "I start from the real process, not the framework. Rules become visible, testable, and traceable.", text: "I draw the day of a melter, a mixer, a shift lead. Which figure they need at six, which at the end of the shift, which they must no longer overwrite. The application follows from that.",
}, },
{ {
title: "Reliable states", title: "Bookings as states",
text: "Bookings, migrations, and failure paths designed so the system stays intelligible under real conditions.", text: "A consumption that has already changed silo and log is not a row you delete. Correction means a reversing entry, a cancellation or a reconcile. Soft-delete hides the effect. It does not undo it.",
}, },
{ {
title: "Small, durable solutions", title: "Put the calculation where it can be checked",
text: "No architecture for its own sake. The smallest design that lastingly improves the process.", text: "Oxide, redox, batch size and cost belong in their own modules, not in the interface and not in the router. If the figure is later disputed, the path to the formula must not run through three layers of code.",
},
{
title: "Operations are part of the product",
text: "Migrations, backup, a shared clock for shift start, a close you can repeat: that is not aftercare. A system that only closes in the demo will not close on the floor.",
}, },
], ],
}, },
+92 -48
View File
@@ -20,9 +20,9 @@ export type LocalizedProject = {
stance: string; stance: string;
stack: string; stack: string;
role: string; role: string;
situation: string; situation: string[];
decisions: string[]; decisions: string[];
outcome: string; outcome: string[];
proof: string; proof: string;
lesson: string; lesson: string;
stages: { title: string; text: string }[]; stages: { title: string; text: string }[];
@@ -42,46 +42,70 @@ const projects: Record<ProjectSlug, { number: string; href?: string; images: Pro
status: "Öffentliche Produkt-Demo", status: "Öffentliche Produkt-Demo",
label: "Industrie-Software · Produktentwicklung", label: "Industrie-Software · Produktentwicklung",
title: "W-Make Batch", title: "W-Make Batch",
summary: "Batchmanagement für die Behälterglas-Produktion vom Satzzettel bis zum Tagesprotokoll.", summary: "Batchmanagement für die Behälterglas-Produktion: vom Satzzettel über den Siloabzug bis zum Tagesprotokoll.",
stance: "Das Produktionsprodukt. Eine öffentliche Demo zeigt den Ablauf, nicht den Kundenstand.", stance: "Das Betriebsprodukt. Die öffentliche Demo zeigt den Ablauf, nicht den Kundenstand.",
stack: "Node.js · Express · SQLite · Web Components · native Tests · Playwright", stack: "Node.js · Express · SQLite · Web Components · native Tests · Playwright",
role: "Domänenmodell, Full-Stack-Entwicklung und technische Qualität.", role: "Domänenmodell, Full-Stack-Entwicklung und technische Qualität.",
situation: "Ein Produktionsprozess lebt von Rezepten, Materialverbrauch, Silos, Schichten und Berechnungen. Wird das als lose Sammlung von Masken gebaut, gehen fachliche Zusammenhänge verloren — und Buchungen werden zu editierbaren Tabellenzeilen.", situation: [
"In der Behälterglas-Produktion ist der Tag eine Kette. Eine Rezeptversion gilt, ein Satzzettel geht an die Waage, der Verbrauch geht vom Silo ab, Oxid und Redox müssen zur Wanne passen, die Schicht übergibt, der Tag schließt. Bricht eines dieser Glieder, stehen am nächsten Morgen drei Zahlen im Raum, die nicht zusammenpassen.",
"Wird das als lose Sammlung von Masken gebaut, kopiert jemand die Werte von einem Blatt auf das nächste. Der Satzzettel sagt etwas anderes als der Silostand, das Protokoll etwas anderes als beides. Die schnellste Korrektur in Software ist dann das Löschen oder Überschreiben einer Zeile. Nach dem Wiegen ist das bereits eine Lüge gegenüber Bestand und Schicht.",
"Dazu kommt der Druck der Schichtübergabe. Was mündlich nachgereicht wird, steht nirgendwo. Wer morgens übernimmt, erbt eine Halle und eine Tabelle, die sich widersprechen.",
],
decisions: [ decisions: [
"Fachliche Buchungen sind Zustände, keine CRUD-Datensätze zum beliebigen Löschen.", "Fachliche Buchungen sind Zustände. Eine Korrektur nach dem Siloabzug ist eine Gegenbuchung, ein Storno oder ein Abgleich, kein DELETE auf der Zeile.",
"Der Satzzettel und das Tagesprotokoll gehören in dasselbe Modell, nicht in getrennte Tools.", "Satzzettel, Gemengebuch und Tagesprotokoll gehören in dasselbe Modell. Zwei Tools an dieser Naht erzeugen denselben Papierbruch, den die Software ersetzen sollte.",
"Berechnungen und Materialflüsse bleiben nachvollziehbar, auch wenn die Oberfläche schlank bleibt.", "Oxid, Redox, Satzgröße und Kosten bleiben an der Rezeptversion hängen und liegen in eigenen Modulen. Die Oberfläche zeigt die Zahl, sie erfindet sie nicht.",
"Schichtbeginn ist eine gemeinsame Uhr, kein voreingestelltes Feld in einem Formular. Ohne diese Uhr driftet der Tag zwischen Stationen.",
"Migrationen, Sicherung und ein Abschluss, den man wiederholen kann, gehören zum Produkt. Ein System, das nur in der Demo schließt, schließt in der Halle nicht.",
"Die öffentliche Demo zeigt das Modell. Kundendaten, Mengen und interne Kennzahlen bleiben außen vor.",
],
outcome: [
"Ein betreibbares System mit öffentlicher Demo unter batch.w-make.com. Man kann den Weg von der Rezeptversion über den Satzzettel in das Tagesprotokoll nachgehen, ohne einen Kundenstand zu sehen.",
"Vertrauliche Hallendaten, Rezepte und Mengen stehen nicht auf dieser Site. Es gibt keine veröffentlichten Durchsatzzahlen, keine Verfügbarkeitsversprechen und keine Kundennamen.",
], ],
outcome: "Ein betreibbares System mit öffentlicher Demo. Vertrauliche Kundendaten, Mengen und interne Kennzahlen bleiben bewusst außen vor.",
proof: "Öffentliche Produkt-Demo unter batch.w-make.com. Die Case Study beschreibt das Modell, nicht den Kundenstand.", proof: "Öffentliche Produkt-Demo unter batch.w-make.com. Die Case Study beschreibt das Modell, nicht den Kundenstand.",
lesson: "Ein Produktionssystem wird besser, wenn die fachliche Irreversibilität Teil der Architektur ist — nicht eine Validierung im Formular.", lesson: "Ein Produktionssystem wird besser, wenn die fachliche Unumkehrbarkeit im Datenmodell steckt. Eine Validierung im Formular kommt zu spät, sobald Silo und Protokoll schon geschrieben haben.",
stages: [ stages: [
{ title: "Rezept", text: "Zusammensetzung und Version bleiben mit dem späteren Verbrauch verbunden." }, { title: "Rezept", text: "Zusammensetzung, Version, Temperatur und Kosten bleiben mit dem späteren Verbrauch verbunden." },
{ title: "Satz", text: "Der Satzzettel ist ein fachlicher Auftrag, kein Ausdruck einer Tabelle." }, { title: "Satzzettel", text: "Der Zettel ist der Auftrag an die Waage, kein Ausdruck einer Tabelle." },
{ title: "Protokoll", text: "Der Tag schließt als nachvollziehbarer Zustand, nicht als exportierte Liste." }, { title: "Silo", text: "Der Verbrauch geht vom Bestand ab. Danach ist die Buchung Teil der Halle." },
{ title: "Gemengebuch", text: "Was eingewogen wurde, bleibt an Satz und Rezeptversion gebunden." },
{ title: "Protokoll", text: "Der Tag schließt als Zustand, den die nächste Schicht vorfindet, nicht als exportierte Liste." },
], ],
}, },
en: { en: {
status: "Public product demo", status: "Public product demo",
label: "Industrial software · product development", label: "Industrial software · product development",
title: "W-Make Batch", title: "W-Make Batch",
summary: "Batch management for container-glass production from the batch ticket to the daily log.", summary: "Batch management for container-glass production: from the batch ticket through silo deduction to the daily log.",
stance: "The production product. A public demo shows the flow, not a client installation.", stance: "The operations product. The public demo shows the flow, not a client installation.",
stack: "Node.js · Express · SQLite · Web Components · native tests · Playwright", stack: "Node.js · Express · SQLite · Web Components · native tests · Playwright",
role: "Domain model, full-stack development, and technical quality.", role: "Domain model, full-stack development, and technical quality.",
situation: "A production process lives on recipes, material use, silos, shifts, and calculations. Built as a loose set of screens, the domain relationships disappear — and bookings become editable table rows.", situation: [
"In container-glass production the day is a chain. A recipe version holds, a batch ticket goes to the scale, consumption leaves the silo, oxide and redox have to match the furnace, the shift hands over, the day closes. If one link breaks, three figures that do not agree are waiting the next morning.",
"Built as a loose set of screens, someone copies values from one sheet to the next. The ticket says one thing, the silo another, the log a third. The fastest correction in software is then to delete or overwrite a row. After weighing, that is already a lie to stock and to the shift.",
"Shift handover adds pressure. What is passed on verbally is written nowhere. Whoever takes over in the morning inherits a hall and a table that contradict each other.",
],
decisions: [ decisions: [
"Domain bookings are states, not CRUD records that can be deleted at will.", "Domain bookings are states. A correction after silo deduction is a reversing entry, a cancellation or a reconcile, not a DELETE on the row.",
"The batch ticket and the daily log belong in one model, not in separate tools.", "Batch ticket, batch book and daily log belong in one model. Two tools at that seam recreate the paper fracture the software was meant to replace.",
"Calculations and material flows stay traceable even when the interface stays lean.", "Oxide, redox, batch size and cost stay bound to the recipe version and live in their own modules. The interface shows the figure. It does not invent it.",
"Shift start is a shared clock, not a default in a form field. Without that clock the day drifts between stations.",
"Migrations, backup and a close you can repeat belong to the product. A system that only closes in the demo will not close on the floor.",
"The public demo shows the model. Client data, volumes and internal metrics stay off the site.",
],
outcome: [
"An operable system with a public demo at batch.w-make.com. You can follow the path from recipe version through the batch ticket into the daily log without seeing a client installation.",
"Confidential plant data, recipes and volumes are not on this site. There are no published throughput figures, no uptime claims and no client names.",
], ],
outcome: "An operable system with a public demo. Confidential client data, volumes, and internal metrics stay unpublished.",
proof: "Public product demo at batch.w-make.com. This case study describes the model, not a client installation.", proof: "Public product demo at batch.w-make.com. This case study describes the model, not a client installation.",
lesson: "A production system improves when domain irreversibility is part of the architecture — not a form validation.", lesson: "A production system improves when domain irreversibility lives in the data model. A form validation comes too late once silo and log have already been written.",
stages: [ stages: [
{ title: "Recipe", text: "Composition and version stay tied to later consumption." }, { title: "Recipe", text: "Composition, version, temperature and cost stay tied to later consumption." },
{ title: "Batch", text: "The ticket is a domain order, not a printout of a table." }, { title: "Ticket", text: "The ticket is the order to the scale, not a printout of a table." },
{ title: "Log", text: "The day closes as a traceable state, not an exported list." }, { title: "Silo", text: "Consumption leaves the stock. After that the booking is part of the hall." },
{ title: "Batch book", text: "What was weighed stays bound to the batch and the recipe version." },
{ title: "Log", text: "The day closes as a state the next shift will find, not as an exported list." },
], ],
}, },
}, },
@@ -96,46 +120,66 @@ const projects: Record<ProjectSlug, { number: string; href?: string; images: Pro
status: "Eigenständig betreibbares Produkt", status: "Eigenständig betreibbares Produkt",
label: "Standalone · Recipe Engineering", label: "Standalone · Recipe Engineering",
title: "Batchmaker Studio", title: "Batchmaker Studio",
summary: "Schlankes Recipe-Studio für Rezepte, Rohstoffe, Satzzettel und Glaschemie.", summary: "Schlankes Recipe-Studio für Rezepte, Rohstoffe, Satzzettel und Glaschemie, ohne Silos und ohne laufende Schichten.",
stance: "Die Werkbank am Schreibtisch. Derselbe Fachkern, bewusst ohne Silos, Schichten und laufende Buchungen.", stance: "Die Werkbank am Schreibtisch. Derselbe Fachkern wie W-Make Batch, ohne den Betrieb der Halle.",
stack: "Node.js · Express · node:sqlite · Vanilla JS · Vite · native Tests", stack: "Node.js · Express · node:sqlite · Vanilla JS · Vite · native Tests",
role: "Produktarchitektur, Full-Stack-Entwicklung und Calculation Engine.", role: "Produktarchitektur, Full-Stack-Entwicklung und Calculation Engine.",
situation: "Rezeptarbeit braucht denselben fachlichen Kern wie die Produktion — aber nicht Silos, Schichten und laufende Buchungen. Ein volles Betriebssystem ist für die Entwicklung am Schreibtisch zu schwer.", situation: [
decisions: [ "Rezeptarbeit braucht denselben fachlichen Kern wie die Produktion: Rohstoffe, Oxidrechnung, Redox, Satzgröße, Kosten, Versionen. Sie braucht nicht Silos, Schichten und laufende Buchungen. Ein volles Betriebssystem ist für die Entwicklung am Schreibtisch zu schwer. Man will eine Rezeptversion ändern, die Chemie prüfen und einen Satzzettel drucken, ohne eine Schicht zu eröffnen.",
"Derselbe Domänenkern, bewusst kleinerer Zuschnitt.", "Wer beides in ein Produkt zwingt, belastet die Werkbank mit Hallenregeln und die Halle mit Entwurfszuständen. Entwürfe geraten in den Bestand, oder der Bestand wird am Schreibtisch nachgebaut. Beides ist teuer.",
"Eigener Server, eigene SQLite-Datei, eigene schlanke Oberfläche.",
"Validierung als sichtbarer Schritt, nicht als versteckte Nebenwirkung.",
], ],
outcome: "Ein eigenständig betreibbares Werkzeug für Rezeptentwicklung, Glaschemie und druckfertige Satzzettel. Keine unbelegten Effizienzversprechen.", decisions: [
proof: "Öffentliche Case Study und der bewusst getrennte Produktzuschnitt gegenüber W-Make Batch.", "Derselbe Fachkern für Rezept, Rohstoff und Glaschemie, kleinerer Zuschnitt: keine Silos, keine Schichten, keine laufenden Buchungen.",
lesson: "Produktgrenzen sind Architektur. Ein gutes Werkzeug muss den Nachbarprozess nicht vollständig abbilden.", "Eigener Server, eigene SQLite-Datei, eigene schlanke Oberfläche. Studio hängt nicht am Betriebsprodukt.",
"Validierung ist ein sichtbarer Schritt. Eine Rechnung, die still im Speichern mitläuft, wird später niemand mehr finden.",
"Satzzettel entstehen hier als Entwurf und Druckvorlage. Gebucht wird erst in W-Make Batch.",
"Keine unbelegten Effizienzversprechen. Das Produkt ist die Werkbank, nicht ein Ersatz für den Betrieb.",
],
outcome: [
"Ein eigenständig betreibbares Werkzeug für Rezeptentwicklung, Glaschemie und druckfertige Satzzettel. Man kann am Schreibtisch arbeiten, ohne eine Schicht zu simulieren.",
"Die Grenze zu W-Make Batch bleibt sichtbar. Was in Studio entworfen wird, wird in Batch zum Auftrag. Die Case Study belegt den Zuschnitt, nicht einen Kundenstand.",
],
proof: "Öffentliche Case Study und der getrennte Produktzuschnitt gegenüber W-Make Batch.",
lesson: "Produktgrenzen sind Architektur. Ein gutes Werkzeug muss den Nachbarprozess nicht vollständig abbilden. Wer Studio und Betrieb vermischt, trägt Entwürfe in den Bestand oder den Bestand an den Schreibtisch.",
stages: [ stages: [
{ title: "Rezepte", text: "Versionieren, vergleichen, importieren und exportieren." }, { title: "Rezepte", text: "Versionieren, vergleichen, importieren und exportieren, gebunden an Rohstoffe und Kosten." },
{ title: "Glaschemie", text: "Oxide, Redox, Physik und nachvollziehbare Berechnungen." }, { title: "Rohstoffe", text: "Stammdaten, aus denen Oxid und Satz später gerechnet werden." },
{ title: "Glaschemie", text: "Oxide, Redox, Physik und Rechnungen, die man prüfen kann." },
{ title: "Satzzettel", text: "Entwurf, Skalierung, Vorschau, druckfertige Ausgabe." }, { title: "Satzzettel", text: "Entwurf, Skalierung, Vorschau, druckfertige Ausgabe." },
{ title: "Validierung", text: "Ein eigener Schritt, bevor eine Version als brauchbar gilt." },
], ],
}, },
en: { en: {
status: "Independently operable product", status: "Independently operable product",
label: "Standalone · recipe engineering", label: "Standalone · recipe engineering",
title: "Batchmaker Studio", title: "Batchmaker Studio",
summary: "A lean recipe studio for recipes, raw materials, batch tickets, and glass chemistry.", summary: "A lean recipe studio for recipes, raw materials, batch tickets and glass chemistry, without silos and without live shifts.",
stance: "The desk-side workbench. The same domain core, deliberately without silos, shifts, and live bookings.", stance: "The desk-side workbench. The same domain core as W-Make Batch, without the halls operations.",
stack: "Node.js · Express · node:sqlite · Vanilla JS · Vite · native tests", stack: "Node.js · Express · node:sqlite · Vanilla JS · Vite · native tests",
role: "Product architecture, full-stack development, and calculation engine.", role: "Product architecture, full-stack development, and calculation engine.",
situation: "Recipe work needs the same domain core as production — but not silos, shifts, and live bookings. A full operations system is too heavy for desk-side development.", situation: [
decisions: [ "Recipe work needs the same domain core as production: raw materials, oxide calculation, redox, batch size, cost, versions. It does not need silos, shifts and live bookings. A full operations system is too heavy for desk-side development. You want to change a recipe version, check the chemistry and print a ticket without opening a shift.",
"The same domain core, deliberately smaller scope.", "Force both into one product and the workbench inherits hall rules while the hall inherits draft states. Drafts leak into stock, or stock is rebuilt at the desk. Both are expensive.",
"Its own server, SQLite file, and lean browser interface.",
"Validation as a visible step, not a hidden side effect.",
], ],
outcome: "An independently operable tool for recipe work, glass chemistry, and print-ready batch tickets. No unverifiable efficiency claims.", decisions: [
proof: "This public case study and the deliberate product boundary against W-Make Batch.", "The same domain core for recipe, raw material and glass chemistry, smaller scope: no silos, no shifts, no live bookings.",
lesson: "Product boundaries are architecture. A good tool does not have to model the neighbouring process in full.", "Its own server, SQLite file and lean browser interface. Studio does not hang off the operations product.",
"Validation is a visible step. A calculation that runs quietly on save will not be found later.",
"Batch tickets originate here as draft and print-ready output. Booking happens in W-Make Batch.",
"No unverifiable efficiency claims. The product is the workbench, not a substitute for operations.",
],
outcome: [
"An independently operable tool for recipe work, glass chemistry and print-ready batch tickets. You can work at the desk without simulating a shift.",
"The boundary with W-Make Batch stays visible. What is drafted in Studio becomes an order in Batch. This case study documents the cut, not a client installation.",
],
proof: "This public case study and the separate product boundary against W-Make Batch.",
lesson: "Product boundaries are architecture. A good tool does not have to model the neighbouring process in full. Mix studio and operations and you carry drafts into stock, or stock onto the desk.",
stages: [ stages: [
{ title: "Recipes", text: "Version, compare, import, and export." }, { title: "Recipes", text: "Version, compare, import and export, bound to raw materials and cost." },
{ title: "Chemistry", text: "Oxides, redox, physics, and traceable calculations." }, { title: "Materials", text: "Master data from which oxide and batch size are later calculated." },
{ title: "Chemistry", text: "Oxides, redox, physics, and calculations you can inspect." },
{ title: "Tickets", text: "Draft, scale, preview, print-ready output." }, { title: "Tickets", text: "Draft, scale, preview, print-ready output." },
{ title: "Validation", text: "A dedicated step before a version counts as usable." },
], ],
}, },
}, },
+82 -62
View File
@@ -11,7 +11,7 @@ export const dictionaries = {
actions: { actions: {
viewWork: "Arbeit ansehen", viewWork: "Arbeit ansehen",
startConversation: "Gespräch beginnen", startConversation: "Gespräch beginnen",
openCase: "Case Study", openCase: "Fall lesen",
openNote: "Notiz lesen", openNote: "Notiz lesen",
send: "Anfrage senden", send: "Anfrage senden",
sending: "Wird gesendet…", sending: "Wird gesendet…",
@@ -19,45 +19,53 @@ export const dictionaries = {
allNotes: "Alle Notizen", allNotes: "Alle Notizen",
}, },
home: { home: {
kicker: "Jan Wagner · Software Engineering", kicker: "Jan Wagner · Behälterglas und Software",
title: "Software für Prozesse, auf die man sich verlassen muss.", title: "Software für Satzzettel, Silo und Schichtabschluss.",
lede: "Ich entwickle robuste digitale Systeme für industrielle und anspruchsvolle Geschäftsprozesse — mit Domänenverständnis, klarer Architektur und Verantwortung für den Betrieb.", lede: "Ich baue Systeme, in denen Rezept, Satzzettel, Siloabzug und Tagesabschluss zusammengehören. Der Ausgangspunkt ist die Behälterglas-Produktion. Derselbe Anspruch gilt überall dort, wo Buchungen Geld, Material und die nächste Schicht berühren.",
asideLabel: "Was ich verbinde", asideLabel: "Herkunft",
asideTitle: "Glass Technology, Full-Stack Engineering und Systems Thinking.", asideTitle: "Die Reihenfolge kommt aus der Halle.",
asideBody: "Der gemeinsame Nenner: reale Abläufe in Software übersetzen, ohne die fachliche Wahrheit zu verlieren.", asideBody: "Rohstoffe, Redox, Schichtbeginn, Gemengebuch. Wer das als Formularstapel behandelt, verliert die Folge, in der die Halle tatsächlich arbeitet.",
workKicker: "Ausgewählte Arbeit", workKicker: "Veröffentlichbare Arbeit",
workTitle: "Nicht Features. Systeme.", workTitle: "W-Make Batch und Batchmaker Studio.",
workLede: "Jeder Beleg zeigt dieselbe Haltung: Domäne verstehen, Entscheidungen explizit machen, ein System im Alltag betreiben.", workLede: "W-Make Batch ist das Betriebsprodukt mit öffentlicher Demo unter batch.w-make.com. Batchmaker Studio ist die Werkbank für Rezept und Glaschemie, ohne laufende Schichten und ohne Silos.",
methodKicker: "Arbeitsweise", methodKicker: "Arbeitsweise",
methodTitle: "Senior heißt: Entscheidungen verantworten.", methodTitle: "Zuerst der Schichttag, dann die Maske.",
methodLede: "Technologie ist Mittel. Entscheidend ist, ob ein System die richtigen fachlichen Zustände abbildet und erklärbar bleibt.", methodLede: "Ich kläre, was um Schichtbeginn gelten muss und was am Abend noch stimmen darf. Erst dann folgt die Technik. Wenn Silo, Rezept und Protokoll nachts auseinanderlaufen, hilft keine neue Oberfläche.",
offerKicker: "Zusammenarbeit", offerKicker: "Zusammenarbeit",
offerTitle: "Wo ich am meisten beitragen kann.", offerTitle: "Drei Formen der Zusammenarbeit.",
notesKicker: "Engineering Notes", notesKicker: "Notizen",
notesTitle: "Entscheidungen, nicht News.", notesTitle: "Entscheidungen aus dem Betrieb.",
ctaKicker: "Gespräch", ctaKicker: "Kontakt",
ctaTitle: "Sie haben einen Prozess, der bessere Software verdient?", ctaTitle: "Schreiben Sie, wenn der Ablauf in der Software bricht.",
ctaBody: "Lassen Sie uns klären, wo der größte Hebel liegt — in einem neuen Produkt, einer bestehenden Anwendung oder einer technischen Entscheidung.", ctaBody: "Schildern Sie den Ablauf, der heute in Kopien, Nebenrechnungen oder stillen Korrekturen endet. Ich antworte in der Regel innerhalb von zwei Werktagen, sofern ich den Fall führen kann.",
}, },
about: { about: {
kicker: "Über mich", kicker: "Über mich",
title: "Von der Glaswanne in die Architektur.", title: "Aus der Behälterglas-Produktion.",
lede: "Ich komme aus der Behälterglas-Produktion und baue Software so, wie ein guter Prozess gedacht ist: nachvollziehbar, irreversibel wo nötig, und im Betrieb erklärbar.", lede: "Ich bin Jan Wagner. Ich komme aus der Schmelze und entwickle Software für Abläufe, die man am nächsten Morgen noch erklären können muss.",
close:
"Wenn Sie einen Ablauf haben, der in Kopien oder stillen Korrekturen endet, schreiben Sie den Prozess auf. Ich sage Ihnen, ob ich den Fall führen kann.",
}, },
work: { work: {
kicker: "Arbeit", kicker: "Arbeit",
title: "Veröffentlichbare Referenzen.", title: "W-Make Batch und Batchmaker Studio.",
lede: "Nur das, was öffentlich stehen darf. Keine Kundennamen, keine unbelegten Kennzahlen, keine internen Systeme.", lede: "Keine Kundennamen, keine internen Mengen, keine Verfügbarkeitsversprechen. Die Demo unter batch.w-make.com zeigt das Modell. Ein Kundenstand bleibt beim Kunden.",
}, },
notes: { notes: {
kicker: "Notizen", kicker: "Notizen",
title: "Was im Betrieb zählt.", title: "Texte für Leute, die den Betrieb verantworten.",
lede: "Kurze, substanzielle Texte zu Entscheidungen. Kein Feed, keine Ankündigungen.", lede: "Keine Ankündigungen und keine Tool-Listen. Es geht um Buchungen, Schichtübergabe, Produktgrenzen und das, was nach dem Go-Live übrig bleibt.",
}, },
contact: { contact: {
kicker: "Kontakt", kicker: "Kontakt",
title: "Ein Prozess. Eine klare nächste Frage.", title: "Beschreiben Sie den Ablauf, der heute reibt.",
lede: "Beschreiben Sie den Ablauf, der heute reibt. Ich antworte, wenn ich wirklich helfen kann.", lede: "Nützlich sind der Prozess im Alltag, wer ihn trägt, und ob es um eine bestehende Anwendung oder um einen Neubau geht. Eine Folienlage brauche ich nicht.",
needTitle: "Was in der ersten Nachricht helfen sollte",
need: [
"Welcher Ablauf heute in Kopien, Nebenrechnungen oder stillen Korrekturen endet.",
"Wer ihn im Alltag trägt: Schmelzer, Mischer, Schichtführer, Labor, IT.",
"Ob es um eine bestehende Anwendung geht oder um einen Neubau.",
],
name: "Name", name: "Name",
email: "E-Mail", email: "E-Mail",
company: "Unternehmen (optional)", company: "Unternehmen (optional)",
@@ -70,7 +78,7 @@ export const dictionaries = {
network: "Senden gerade nicht möglich. Bitte später erneut versuchen oder direkt schreiben.", network: "Senden gerade nicht möglich. Bitte später erneut versuchen oder direkt schreiben.",
delivery: "Die Anfrage wurde gespeichert, die E-Mail ist fehlgeschlagen. Bitte schreiben Sie direkt an", delivery: "Die Anfrage wurde gespeichert, die E-Mail ist fehlgeschlagen. Bitte schreiben Sie direkt an",
rate: "Zu viele Anfragen in kurzer Zeit. Bitte schreiben Sie direkt per E-Mail.", rate: "Zu viele Anfragen in kurzer Zeit. Bitte schreiben Sie direkt per E-Mail.",
reply: "Ich antworte in der Regel innerhalb von zwei Werktagen und nur, wenn ich wirklich helfen kann.", reply: "Ich antworte in der Regel innerhalb von zwei Werktagen, und nur wenn ich den Fall wirklich führen kann.",
emailDirect: "Direkt per E-Mail", emailDirect: "Direkt per E-Mail",
}, },
legal: { legal: {
@@ -84,11 +92,13 @@ export const dictionaries = {
role: "Verantwortung", role: "Verantwortung",
decisions: "Entscheidungen", decisions: "Entscheidungen",
outcome: "Ergebnis", outcome: "Ergebnis",
proof: "Beweis", proof: "Was man prüfen kann",
lesson: "Lernpunkt", lesson: "Was bleibt",
modelTitle: "Der Ablauf im Modell.",
galleryTitle: "Blick in die Anwendung.",
}, },
footer: { footer: {
line: "Software Engineering · Glass Technology · Systems Thinking", line: "Software für industrielle Prozesse",
imprint: "Impressum", imprint: "Impressum",
privacy: "Datenschutz", privacy: "Datenschutz",
}, },
@@ -112,53 +122,61 @@ export const dictionaries = {
actions: { actions: {
viewWork: "See the work", viewWork: "See the work",
startConversation: "Start a conversation", startConversation: "Start a conversation",
openCase: "Case study", openCase: "Read the case",
openNote: "Read note", openNote: "Read the note",
send: "Send inquiry", send: "Send inquiry",
sending: "Sending…", sending: "Sending…",
backToWork: "All work", backToWork: "All work",
allNotes: "All notes", allNotes: "All notes",
}, },
home: { home: {
kicker: "Jan Wagner · Software Engineering", kicker: "Jan Wagner · container glass and software",
title: "Software for processes that have to hold.", title: "Software for batch tickets, silos and the shift close.",
lede: "I build durable digital systems for industrial and demanding business processes — with domain understanding, explicit architecture, and ownership of operations.", lede: "I build systems in which recipe, batch ticket, silo deduction and day close belong together. The starting point is container-glass production. The same standard applies wherever bookings touch money, material and the next shift.",
asideLabel: "What I connect", asideLabel: "Background",
asideTitle: "Glass technology, full-stack engineering, and systems thinking.", asideTitle: "The order comes from the hall.",
asideBody: "The common thread: translate real operations into software without losing the domain truth.", asideBody: "Raw materials, redox, shift start, batch book. Treat that as a pile of forms and you lose the sequence in which the hall actually works.",
workKicker: "Selected work", workKicker: "Work I can publish",
workTitle: "Not features. Systems.", workTitle: "W-Make Batch and Batchmaker Studio.",
workLede: "Each reference shows the same stance: understand the domain, make decisions explicit, run the system in real conditions.", workLede: "W-Make Batch is the operations product with a public demo at batch.w-make.com. Batchmaker Studio is the workbench for recipes and glass chemistry, without live shifts and without silos.",
methodKicker: "How I work", methodKicker: "How I work",
methodTitle: "Senior means owning the decision.", methodTitle: "The shift day first, then the screen.",
methodLede: "Technology is a means. What matters is whether a system models the right domain states and stays explainable.", methodLede: "I settle what must hold at shift start and what may still be true in the evening. The stack comes after that. If silo, recipe and log drift apart overnight, a new interface will not help.",
offerKicker: "Working together", offerKicker: "Working together",
offerTitle: "Where I contribute most.", offerTitle: "Three ways of working together.",
notesKicker: "Engineering notes", notesKicker: "Notes",
notesTitle: "Decisions, not news.", notesTitle: "Decisions from operations.",
ctaKicker: "Conversation", ctaKicker: "Contact",
ctaTitle: "A process that deserves better software?", ctaTitle: "Write if the process is breaking in the software.",
ctaBody: "Lets find the highest-leverage point — a new product, an existing system, or a technical decision.", ctaBody: "Describe the flow that currently ends in copies, side calculations or quiet corrections. I usually reply within two working days, if I can take the case.",
}, },
about: { about: {
kicker: "About", kicker: "About",
title: "From the glass furnace to the architecture.", title: "From container-glass production.",
lede: "I come from container-glass production and design software the way a good process is designed: traceable, irreversible where it must be, and explainable in operations.", lede: "I am Jan Wagner. I come from the melt and build software for flows that still have to make sense the next morning.",
close:
"If you have a flow that currently ends in copies or quiet corrections, write the process down. I will tell you whether I can take the case.",
}, },
work: { work: {
kicker: "Work", kicker: "Work",
title: "References I can publish.", title: "W-Make Batch and Batchmaker Studio.",
lede: "Only what is cleared for the public web. No client names, no unverifiable metrics, no internal systems.", lede: "No client names, no internal volumes, no uptime claims. The demo at batch.w-make.com shows the model. A client installation stays with the client.",
}, },
notes: { notes: {
kicker: "Notes", kicker: "Notes",
title: "What matters in operations.", title: "Writing for people who own the plant day.",
lede: "Short, substantial writing about decisions. Not a feed, not announcements.", lede: "No announcements and no tool lists. The subject is bookings, shift handover, product boundaries, and what remains after go-live.",
}, },
contact: { contact: {
kicker: "Contact", kicker: "Contact",
title: "One process. One clear next question.", title: "Describe the flow that currently rubs.",
lede: "Describe the flow that is fraying. I reply when I can actually help.", lede: "What helps: the process in daily use, who carries it, and whether this is an existing system or a new build. I do not need a slide deck.",
needTitle: "What helps in the first message",
need: [
"Which flow currently ends in copies, side calculations or quiet corrections.",
"Who carries it day to day: melter, mixer, shift lead, lab, IT.",
"Whether this is an existing application or a new build.",
],
name: "Name", name: "Name",
email: "Email", email: "Email",
company: "Company (optional)", company: "Company (optional)",
@@ -171,7 +189,7 @@ export const dictionaries = {
network: "Sending is unavailable right now. Please try again later or write directly.", network: "Sending is unavailable right now. Please try again later or write directly.",
delivery: "The inquiry was stored, but email delivery failed. Please write directly to", delivery: "The inquiry was stored, but email delivery failed. Please write directly to",
rate: "Too many inquiries in a short time. Please write directly by email.", rate: "Too many inquiries in a short time. Please write directly by email.",
reply: "I usually reply within two working days and only when I can actually help.", reply: "I usually reply within two working days, and only if I can actually take the case.",
emailDirect: "Email directly", emailDirect: "Email directly",
}, },
legal: { legal: {
@@ -184,12 +202,14 @@ export const dictionaries = {
situation: "Situation", situation: "Situation",
role: "Responsibility", role: "Responsibility",
decisions: "Decisions", decisions: "Decisions",
outcome: "Outcome", outcome: "Result",
proof: "Evidence", proof: "What you can inspect",
lesson: "Takeaway", lesson: "What remains",
modelTitle: "The flow as a model.",
galleryTitle: "A look at the application.",
}, },
footer: { footer: {
line: "Software Engineering · Glass Technology · Systems Thinking", line: "Software for industrial processes",
imprint: "Legal notice", imprint: "Legal notice",
privacy: "Privacy", privacy: "Privacy",
}, },
+2 -2
View File
@@ -35,8 +35,8 @@ async function smtpSend(config: SmtpConfig, inquiry: Inquiry): Promise<void> {
const body = [ const body = [
`Name: ${inquiry.name}`, `Name: ${inquiry.name}`,
`E-Mail: ${inquiry.email}`, `E-Mail: ${inquiry.email}`,
`Unternehmen: ${inquiry.company || ""}`, `Unternehmen: ${inquiry.company || "-"}`,
`Thema: ${inquiry.topic || ""}`, `Thema: ${inquiry.topic || "-"}`,
`Sprache: ${inquiry.locale}`, `Sprache: ${inquiry.locale}`,
"", "",
inquiry.message, inquiry.message,