Fünf Jahre Softwareentwicklung: Die wichtigsten Lektionen
Fünf Jahre Softwareentwicklung verdichtet: Grundlagen vor Framework-Hypes, Vertrauen durch verlässliche Lieferung, Code für die Nächsten, Einfachheit.

Grundlagen überdauern Frameworks
Jedes Jahr bringt ein neues Framework. Die Ingenieurinnen und Ingenieure, die sich durchsetzen, sind die, die die Muster darunter verstehen. Frameworks kommen und gehen; Datenstrukturen, Algorithmen, Netzwerktechnik und das Denken in Systemarchitekturen bleiben jahrzehntelang relevant.
// The framework changes. The pattern doesn't.
// 2020: Express middleware
app.use((req, res, next) => {
const start = Date.now();
res.on("finish", () => {
console.log(`${req.method} ${req.path} - ${Date.now() - start}ms`);
});
next();
});
// 2023: Next.js middleware
export function middleware(request: NextRequest) {
const start = Date.now();
const response = NextResponse.next();
response.headers.set("X-Response-Time", `${Date.now() - start}ms`);
return response;
}
// 2025: Whatever comes next — still request interception,
// still wrapping the handler, still measuring duration.
// The framework is syntax. The pattern is knowledge.Code wird öfter gelesen als geschrieben
Cleverer Code beeindruckt niemanden mehr, wenn er sechs Monate später um drei Uhr morgens debuggt werden muss. Für Lesbarkeit zu schreiben ist kein Zeichen von Schwäche, sondern eines von Erfahrung.
// ❌ Clever — what does this do at a glance?
const r = d.filter((x) => x.s === "a" && x.t > Date.now() - 864e5).reduce(
(a, c) => ({ ...a, [c.c]: (a[c.c] || 0) + c.v }),
{} as Record<string, number>
);
// ✅ Clear — intention is obvious without comments
const oneDayAgo = Date.now() - 24 * 60 * 60 * 1000;
const activeTransactions = deposits.filter(
(deposit) =>
deposit.status === "active" && deposit.timestamp > oneDayAgo
);
const totalsByCategory = activeTransactions.reduce<Record<string, number>>(
(totals, transaction) => ({
...totals,
[transaction.category]:
(totals[transaction.category] ?? 0) + transaction.value,
}),
{}
);Klein und häufig ausliefern
Große Pull Requests liegen tagelang im Review, sammeln Merge-Konflikte an und gehen am Ende mit versteckten Fehlern live. Kleine, fokussierte Änderungen werden in Minuten geprüft und lassen sich mit Zuversicht deployen.
// Deployment metrics that tell the story
interface DeliveryMetrics {
// Small PRs: reviewed faster, fewer bugs
avgPRSize: { lines: number; reviewTimeHours: number; bugRate: number };
// Deployment frequency: more deploys = smaller blast radius
deploysPerWeek: number;
// Lead time: commit to production
leadTimeHours: number;
// Recovery: how fast you fix things when they break
meanTimeToRecoveryMinutes: number;
}
// What I've seen across teams:
const highPerformingTeam: DeliveryMetrics = {
avgPRSize: { lines: 150, reviewTimeHours: 2, bugRate: 0.02 },
deploysPerWeek: 25,
leadTimeHours: 4,
meanTimeToRecoveryMinutes: 30,
};
const strugglingTeam: DeliveryMetrics = {
avgPRSize: { lines: 2000, reviewTimeHours: 72, bugRate: 0.15 },
deploysPerWeek: 1,
leadTimeHours: 336, // 2 weeks
meanTimeToRecoveryMinutes: 480, // 8 hours
};
// The difference is not talent. It's workflow.Tests sparen Zeit — irgendwann
Tests zu schreiben fühlt sich langsam an, bis ein Test zum ersten Mal eine Regression abfängt, die sonst in Produktion gelandet wäre. Der Ertrag zeigt sich nicht sofort, aber er summiert sich.
// The test that pays for itself
describe("checkout price calculation", () => {
it("applies percentage discount before tax", () => {
const result = calculateTotal({
items: [{ price: 100, quantity: 2 }],
discount: { type: "percentage", value: 10 },
taxRate: 0.08,
});
// Without this test, a refactor swapped tax and discount order.
// That bug would have overcharged every customer by ~1%.
// This test caught it in CI, saved a production incident,
// and justified every minute spent writing it.
expect(result.subtotal).toBe(200);
expect(result.discountAmount).toBe(20);
expect(result.tax).toBe(14.4); // 8% of $180
expect(result.total).toBe(194.4);
});
});Die beste Architektur ist die einfachste, die funktioniert
Over-Engineering ist der häufigste Fehler in der Mitte der Karriere. Ein Monolith verkraftet mehr Traffic, als man denkt. Microservices lösen organisatorische Probleme, keine technischen — jedenfalls nicht, solange die Teamgröße den operativen Mehraufwand nicht rechtfertigt.
// Year 1 thinking: "We need microservices for scalability"
// Year 3 reality: "We have 40 services, 3 engineers, and
// no one understands the full system"
// ❌ Premature microservices for a small team
// services/user-service/
// services/order-service/
// services/payment-service/
// services/notification-service/
// services/inventory-service/
// + Kubernetes, service mesh, distributed tracing, 5 databases
// ✅ Modular monolith — same boundaries, fraction of the complexity
// src/modules/users/
// src/modules/orders/
// src/modules/payments/
// src/modules/notifications/
// src/modules/inventory/
// + One database, one deployment, clear module boundaries
// Extract to services WHEN you have the team and traffic to justify itKommunikation skaliert; Code nicht
Die Wirkungsgrenze von reinem Programmieren liegt niedriger, als man denkt. Die Ingenieurinnen und Ingenieure, die Produkte prägen, Architektur beeinflussen und ihre Teams voranbringen, sind die, die gut kommunizieren — in Design-Dokumenten, Code-Reviews, Incident-Retrospektiven und im Gespräch des Alltags.
// A pull request description that accelerates review
interface EffectivePRDescription {
what: string; // What changed
why: string; // Why this approach
how: string; // Key implementation decisions
testing: string; // How you verified it works
risks: string; // What could go wrong
rollback: string; // How to undo if needed
}
// vs. the PR description we've all seen:
// Title: "fix stuff"
// Description: ""
// Files changed: 47
// The first one gets reviewed in 20 minutes.
// The second one sits for 3 days.In die Developer Experience investieren
Die Tools, Skripte und Workflows, die man sich selbst baut, zahlen sich täglich aus. Eine Verbesserung von 30 Sekunden im Entwicklungs-Loop spart über ein Jahr gerechnet Stunden.
// Small investments that compound
const developerExperienceWins = [
{
investment: "Hot reload configured properly",
timePerOccurrence: "10 seconds saved",
frequency: "200 times/day",
annualSavings: "~110 hours/year",
},
{
investment: "One-command database reset with seed data",
timePerOccurrence: "5 minutes saved",
frequency: "3 times/week",
annualSavings: "~13 hours/year",
},
{
investment: "Pre-commit hooks catching lint/type errors",
timePerOccurrence: "15 minutes saved (no CI round-trip)",
frequency: "5 times/week",
annualSavings: "~65 hours/year",
},
{
investment: "Automated PR template with checklist",
timePerOccurrence: "3 minutes saved",
frequency: "10 times/week",
annualSavings: "~26 hours/year",
},
];Die Lektion unter all den Lektionen
Bei Softwareentwicklung geht es nicht darum, perfekten Code zu schreiben. Es geht darum, Probleme für Menschen zu lösen — für Nutzerinnen und Nutzer, Teammitglieder und das eigene zukünftige Ich —, während man Grenzen bei Zeit, Wissen und Komplexität navigiert. Die besten Ingenieurinnen und Ingenieure sind nicht die, die die meisten Technologien kennen. Es sind die, die beständig gute Abwägungen treffen, klar kommunizieren und die Codebasis besser hinterlassen, als sie sie vorgefunden haben.
Fünf Jahre, Hunderte Pull Requests, Dutzende Vorfälle in Produktion — und die wichtigste Fähigkeit war am Ende das Urteilsvermögen: zu wissen, wann man selbst baut, wann man einkauft, wann man vereinfacht und wann man ausliefert. Der Code ist nur das Artefakt. Was zählt, ist das Denken dahinter.


