DEV Community

Marco
Marco

Posted on • Originally published at blog.disane.dev

Best Practices beim Programmieren: Sauberer Code fĂŒr Dich und Dein Team 🚀

Entdecke die besten Praktiken beim Programmieren! Lerne, wie Du lesbaren, wartbaren und sauberen Code schreibst, der nicht nur fĂŒr Dich, sondern auch fĂŒr Dein Team verstĂ€ndlich ist. 🚀


In der Softwareentwicklung ist es entscheidend, Code zu schreiben, der nicht nur funktioniert, sondern auch gut strukturiert, leserlich und wartbar ist. Dies gilt nicht nur fĂŒr die Zusammenarbeit im Team, sondern auch fĂŒr den Fall, dass Du Monate spĂ€ter auf Deinen eigenen Code zurĂŒckkommst. In diesem Artikel werde ich Dir die besten Praktiken und Prinzipien vorstellen, die Du beim Programmieren beachten solltest.

Anhand von JavaScript-Beispielen zeige ich Dir, wie man schlechten Code in lesbaren Code umwandelt und welche Vorteile funktionale Programmierung hier bieten.

Außerdem gehe ich auf die wichtigsten Soft Skills ein, die fĂŒr einen Entwickler unerlĂ€sslich sind. Programmieren ist ein Handwerk und genauso viel Liebe sollte auch in den Code gesteckt werden ❀

Zu dem Thema gibt es ein sehr gutes Buch von Robert C. Martin:

Preview image

Warum guter Code wichtig ist đŸ€”

Lesbarkeit

Lesbarer Code ermöglicht es Dir und anderen Entwicklern, den Code schnell zu verstehen und zu nutzen. Wenn Du nach einigen Monaten Deinen eigenen Code wieder anschaust, möchtest Du nicht stundenlang ĂŒberlegen mĂŒssen, was dieser oder jener Teil des Codes tut. Lesbarer Code spart Zeit und Nerven. Ein gut lesbarer Code hilft auch dabei, Fehler schneller zu finden und zu beheben, da die Logik und Struktur klar ersichtlich sind.

Wartbarkeit

Wartbarer Code ist entscheidend, um Fehler zu beheben und neue Funktionen hinzuzufĂŒgen. Unstrukturierter und unklarer Code erschwert die Wartung und erhöht die Wahrscheinlichkeit, dass beim HinzufĂŒgen neuer Features Bugs entstehen. Wartbarer Code sollte modular aufgebaut sein, sodass Änderungen in einem Modul keine unvorhergesehenen Auswirkungen auf andere Teile des Codes haben.

Confused Cbs GIF by Wolf Entertainment

Zusammenarbeit

In den meisten Projekten arbeitest Du nicht allein. Klarer und gut strukturierter Code erleichtert die Zusammenarbeit im Team. Andere Entwickler können Deinen Code lesen, verstehen und weiterentwickeln. Eine einheitliche Codebasis mit konsistenten Namenskonventionen und Formatierungen erleichtert die Einarbeitung neuer Teammitglieder und fördert eine produktive Arbeitsumgebung.

Wiederverwendbarkeit

Gut geschriebener Code ist oft wiederverwendbar. Durch die Anwendung von Prinzipien wie DRY (Don't Repeat Yourself) und KISS (Keep It Simple, Stupid) kannst Du sicherstellen, dass Deine Codebausteine in verschiedenen Teilen Deines Projekts oder sogar in anderen Projekten wiederverwendet werden können.

Code fĂŒr weniger erfahrene Entwickler

Ein guter Entwickler schreibt Code so, dass er auch fĂŒr Entwickler mit geringerem Kenntnisstand verstĂ€ndlich ist. Dies bedeutet, dass der Code gut dokumentiert, klar strukturiert und frei von unnötiger KomplexitĂ€t ist.

Prinzipien guten Codes đŸ› ïž

Klarheit vor Cleverness

Es ist verlockend, cleveren und komplizierten Code zu schreiben, der auf den ersten Blick beeindruckend wirkt. Doch Klarheit sollte immer Vorrang haben. Einfacher und klarer Code ist oft der bessere Weg. Ein gut verstÀndlicher Code erleichtert das Debugging und die Weiterentwicklung.

// Clever, aber möglicherweise schwer verstÀndlich
const isValid = (str) => !/[^\w]/.test(str);

// Klar und verstÀndlich
const isValid = (str) => {
  const regex = /[^\w]/;
  return !regex.test(str);
};

Enter fullscreen mode Exit fullscreen mode

Versetze dich immer in die Lage eines anderen Programmierers und stelle dir drei Fragen:

  • WĂŒrde ein AnfĂ€nger mein Code verstehen?
  • Verstehe ich meinen Code in 6 Monaten noch?
  • Kann ich jemanden in den Code einarbeiten ohne in schulen zu mĂŒssen?

Kannst du davon eine Frage mit nein beantworten, dann ist dein Code vermutlich zu kompliziert.

Coffee Wow GIF by Starbucks

Konsistenz

Ein konsistenter Stil macht den Code lesbarer. Verwende einheitliche Namenskonventionen, EinrĂŒckungen und Formatierungen. Dies erleichtert das VerstĂ€ndnis und die Wartung des Codes. Tools wie ESLint oder Prettier können helfen, den Code automatisch zu formatieren und konsistent zu halten.

// Inkonsistent
const userName = "John";
const UserAge = 30;

// Konsistent
const userName = "John";
const userAge = 30;

Enter fullscreen mode Exit fullscreen mode

Work Demanding GIF by HannahWitton

DRY (Don't Repeat Yourself) 🔂

Vermeide Redundanzen im Code. Wiederholter Code sollte in Funktionen oder Klassen ausgelagert werden. Dies reduziert Fehler und erleichtert Änderungen. Wenn sich eine Änderung im Code ergibt, muss diese nur an einer Stelle vorgenommen werden.

// Redundanter Code
function getUserName(user) {
  return user.firstName + ' ' + user.lastName;
}

function getUserAddress(user) {
  return user.street + ', ' + user.city;
}

// DRY Prinzip angewendet
function getFullName(user) {
  return `${user.firstName} ${user.lastName}`;
}

function getAddress(user) {
  return `${user.street}, ${user.city}`;
}

Enter fullscreen mode Exit fullscreen mode

Say It Again Jason Sudeikis GIF by Saturday Night Live

KISS (Keep It Simple, Stupid) đŸ€Ż

Halte Deinen Code so einfach wie möglich. KomplexitÀt erhöht die FehleranfÀlligkeit und erschwert das VerstÀndnis. Einfacher Code ist oft der robustere und effizientere Code. Verwende einfache und klare Logiken anstatt komplexer und verschachtelter Strukturen.

// Komplex und schwer verstÀndlich
function getDiscount(price, isMember) {
  return isMember ? (price > 100 ? price * 0.9 : price * 0.95) : price;
}

// Einfach und klar
function getDiscount(price, isMember) {
  if (isMember) {
    if (price > 100) {
      return price * 0.9;
    } else {
      return price * 0.95;
    }
  }
  return price;
}

Enter fullscreen mode Exit fullscreen mode

TV gif. Mario Lopez as Slater on Saved by the Bell runs down his high school steps with football in hand, points at us, and says, “stupid.”

YAGNI (You Aren't Gonna Need It) ❌

Implementiere nur die FunktionalitĂ€ten, die Du aktuell benötigst. ÜberflĂŒssige Features erhöhen die KomplexitĂ€t und Wartungskosten. Dieser Grundsatz hilft, den Code schlank und fokussiert zu halten.

// Overengineering
function calculatePrice(price, tax, discount, isMember) {
  let finalPrice = price + (price * tax);
  if (isMember) {
    finalPrice -= discount;
  }
  return finalPrice;
}

// YAGNI Prinzip angewendet
function calculatePrice(price, tax) {
  return price + (price * tax);
}

Enter fullscreen mode Exit fullscreen mode

Season 7 No GIF by Chicago Fire

Code ist wie ein gutes Buch 📚

Die richtige Benennung von Variablen, Funktionen und Klassen ist essenziell fĂŒr die Lesbarkeit und VerstĂ€ndlichkeit des Codes, Ă€hnlich wie bei einem gut geschriebenen Buch. Klare und prĂ€zise Namen spiegeln die Absicht des Codebausteins wider und erleichtern Wartung und Zusammenarbeit im Team. Zum Beispiel sollte eine Funktion, die das Quadrat einer Zahl berechnet, calculateSquare heißen, nicht doSomething.

Eine Klasse fĂŒr die Berechnung von Quadraten könnte SquareCalculator heißen. Gut benannter und strukturierter Code spart Zeit und Nerven und macht die Arbeit im Team angenehmer und produktiver. Indem Du Deinen Code so schreibst, dass er auch fĂŒr weniger erfahrene Entwickler verstĂ€ndlich ist, vermeidest Du technische Schulden und verbesserst nachhaltig die QualitĂ€t Deines Projekts.

Benennungen folgen oft den Regeln der Sprache, Àhnlich wie Adjektive, Substantive und Verben, was sie intuitiver und verstÀndlicher macht.

Unimpressed Sea GIF by SpongeBob SquarePants

Adjektive

Adjektive beschreiben Eigenschaften oder ZustÀnde. In der Programmierung werden sie oft verwendet, um Variablen zu benennen, die bestimmte ZustÀnde oder Eigenschaften speichern.

let isActive = true;
let userName = "JohnDoe";
Enter fullscreen mode Exit fullscreen mode

In diesem Beispiel beschreibt isActive den Zustand eines Objekts (ob es aktiv ist oder nicht) und userName speichert eine Eigenschaft eines Benutzers.

Verben

Verben beschreiben Aktionen oder VorgĂ€nge. In der Programmierung werden Verben hĂ€ufig verwendet, um Funktionen zu benennen, die bestimmte Aktionen ausfĂŒhren.

function calculateArea(width, height) {
  return width * height;
}

function fetchData(url) {
  // Daten von einer URL abrufen
}

Enter fullscreen mode Exit fullscreen mode

Hier beschreiben calculateArea und fetchData die Aktionen, die die jeweiligen Funktionen ausfĂŒhren (Berechnung einer FlĂ€che bzw. Abrufen von Daten).

Substantive

Substantive bezeichnen Personen, Orte oder Dinge. In der Programmierung werden Substantive oft verwendet, um Klassen und Objekte zu benennen, die bestimmte EntitÀten oder Konzepte darstellen.

class User {
  constructor(name, email) {
    this.name = name;
    this.email = email;
  }
}

class Product {
  constructor(id, price) {
    this.id = id;
    this.price = price;
  }
}

Enter fullscreen mode Exit fullscreen mode

In diesem Beispiel bezeichnen User und Product konkrete EntitÀten in einem System, Àhnlich wie Substantive in der Sprache.

Adverbien

Adverbien modifizieren Verben und beschreiben, wie eine Aktion ausgefĂŒhrt wird. In der Programmierung können Adverbien dazu beitragen, Funktionsnamen zu spezifizieren und zu verdeutlichen, wie eine Aktion ausgefĂŒhrt wird.

function calculateAreaQuickly(width, height) {
  // Schnelle Berechnung der FlÀche
  return width * height;
}

function fetchDataSafely(url) {
  // Sichere Datenabruf von einer URL
}

Enter fullscreen mode Exit fullscreen mode

Hier modifizieren quickly und safely die Aktionen und geben zusĂ€tzliche Informationen darĂŒber, wie die Aktionen ausgefĂŒhrt werden sollen.

Schlechter vs. Guter Code in JavaScript đŸ’»

Beispiel fĂŒr schlechten Code

function processData(input) {
  let output = [];
  for (let i = 0; i < input.length; i++) {
    let processedData = input[i] * 2;
    output.push(processedData);
  }
  console.log(output);
  return output;
}

Enter fullscreen mode Exit fullscreen mode

Beispiel fĂŒr guten Code

function processData(input) {
  return input.map(item => item * 2);
}

const input = [1, 2, 3, 4, 5];
const output = processData(input);
console.log(output); // [2, 4, 6, 8, 10]

Enter fullscreen mode Exit fullscreen mode

In diesem Beispiel wurde der Code durch die Verwendung von Array.prototype.map vereinfacht, was die Lesbarkeit und Wartbarkeit erhöht.

Technische Schulden 🏩

Technische Schulden entstehen, wenn kurzfristige Lösungen oder Kompromisse beim Programmieren eingegangen werden, um schnelle Ergebnisse zu erzielen, anstatt langfristige, nachhaltige Lösungen zu implementieren. Dies kann durch Zeitdruck, fehlende Ressourcen oder mangelnde Planung verursacht werden. Wie finanzielle Schulden mĂŒssen auch technische Schulden "zurĂŒckgezahlt" werden, was in Form von zusĂ€tzlicher Arbeit fĂŒr Wartung und Refaktorierung geschieht. Technische Schulden können die CodequalitĂ€t beeintrĂ€chtigen, die Entwicklung verlangsamen und die EinfĂŒhrung neuer Features erschweren. Daher ist es wichtig, bewusste Entscheidungen zu treffen und, wenn möglich, langfristige und saubere Lösungen zu bevorzugen, um die Ansammlung technischer Schulden zu vermeiden.

season 7 episode 10 GIF

Hardcodierte Werte

Schlechte Lösung

function calculateTotalPrice(quantity) {
  return quantity * 9.99;
}

Enter fullscreen mode Exit fullscreen mode

In diesem Beispiel ist der Preis des Produkts im Code hardcodiert. Dies ist eine schnelle Lösung, aber sie fĂŒhrt zu technischen Schulden, weil jede Änderung des Preises eine Änderung im Code erfordert.

Nachhaltige Lösung

const PRODUCT_PRICE = 9.99;

function calculateTotalPrice(quantity) {
  return quantity * PRODUCT_PRICE;
}

Enter fullscreen mode Exit fullscreen mode

Durch die Verwendung einer Konstanten fĂŒr den Produktpreis wird der Code flexibler und leichter zu warten. Änderungen am Preis mĂŒssen nur an einer Stelle vorgenommen werden, was die technische Schuld reduziert.

Duplizierter Code

Technische Schulden

function calculateRectangleArea(width, height) {
  return width * height;
}

function calculateTriangleArea(base, height) {
  return (base * height) / 2;
}

Enter fullscreen mode Exit fullscreen mode

Hier gibt es duplizierten Code, der die Berechnung der FlĂ€che beinhaltet. Diese Wiederholung fĂŒhrt zu technischen Schulden, da Änderungen an der Logik an mehreren Stellen vorgenommen werden mĂŒssen.

function calculateArea(shape, ...dimensions) {
  switch (shape) {
    case 'rectangle':
      return dimensions[0] * dimensions[1];
    case 'triangle':
      return (dimensions[0] * dimensions[1]) / 2;
    default:
      throw new Error('Unknown shape');
  }
}

Enter fullscreen mode Exit fullscreen mode

Durch die ZusammenfĂŒhrung der Berechnungslogik in eine einzige Funktion wird der Code DRY (Don't Repeat Yourself). Dies reduziert die technische Schuld und macht den Code einfacher zu warten und zu erweitern.

Nachteile von kilometerlangen Einzeilern đŸš«

Kilometerlange Einzeiler können schwer zu lesen und zu debuggen sein. Sie neigen dazu, komplex und undurchsichtig zu werden, was die Wartung erschwert.

Season 1 Bug GIF by Nanalan'

Beispiel fĂŒr einen schlechten Einzeiler

const result = array.map(x => x * 2).filter(x => x > 10).reduce((acc, x) => acc + x, 0);

Enter fullscreen mode Exit fullscreen mode

Aufgespaltener und lesbarer Code

const doubled = array.map(x => x * 2);
const filtered = doubled.filter(x => x > 10);
const result = filtered.reduce((acc, x) => acc + x, 0);

Enter fullscreen mode Exit fullscreen mode

Durch das Aufteilen des Codes in mehrere Zeilen wird er wesentlich lesbarer und einfacher zu debuggen.

Funktionale Programmierung đŸ§‘â€đŸ’»

Funktionale Programmierung kann helfen, sauberen und effizienten Code zu schreiben. Sie fördert die Verwendung von unverÀnderlichen Daten und reinen Funktionen.

Funktionale Programmierung in JavaScript

const numbers = [1, 2, 3, 4, 5];

const doubled = numbers.map(num => num * 2);
const even = doubled.filter(num => num % 2 === 0);

console.log(even); // [4, 8]

Enter fullscreen mode Exit fullscreen mode

In diesem Beispiel werden die ursprĂŒnglichen Daten nicht verĂ€ndert, und die Operationen sind klar und nachvollziehbar.

Vorteile der funktionalen Programmierung

UnverÀnderlichkeit

UnverÀnderlichkeit bedeutet, dass Daten nach ihrer Erstellung nicht mehr verÀndert werden. Stattdessen werden neue Datenstrukturen erstellt. Dies reduziert Fehler und macht den Code vorhersehbarer.

Reine Funktionen

Reine Funktionen sind Funktionen, die keine Seiteneffekte haben und immer das gleiche Ergebnis fĂŒr die gleichen Eingaben liefern. Dies macht den Code einfacher zu testen und zu debuggen. Die erwartete Funktionsweise ist deterministisch.

Negatives Beispiel ❌

Hier wird der Wert der globalen Variablen counter verĂ€ndert, was die Funktion nicht rein macht, da der Zustand außerhalb der Funktion beeinflusst wird und das Ergebnis sich bei gleichen Aufrufen unterscheidet.

let counter = 0;

function incrementCounter() {
    // Diese Funktion erhöht den Wert von counter und gibt den neuen Wert zurĂŒck
    counter++;
    return counter;
}

// Testen der Funktion mit Seiteneffekt
console.log(incrementCounter()); // Ausgabe: 1
console.log(incrementCounter()); // Ausgabe: 2 (nicht das gleiche Ergebnis bei gleichen Aufrufen)

Enter fullscreen mode Exit fullscreen mode

Positives Beispiel ✅

function add(a, b) {
    // Diese Funktion gibt die Summe von a und b zurĂŒck
    return a + b;
}

// Testen der reinen Funktion
console.log(add(2, 3)); // Ausgabe: 5
console.log(add(2, 3)); // Ausgabe: 5 (immer das gleiche Ergebnis)

Enter fullscreen mode Exit fullscreen mode

Höhere Ordnung

Funktionen höherer Ordnung sind Funktionen, die andere Funktionen als Argumente nehmen oder Funktionen zurĂŒckgeben. Dies ermöglicht flexible und wiederverwendbare Codebausteine.

const add = (a) => (b) => a + b;

const add5 = add(5);
console.log(add5(10)); // 15

Enter fullscreen mode Exit fullscreen mode

Soft Skills fĂŒr Entwickler đŸ€

Kommunikation

Gute Kommunikation ist unerlĂ€sslich. Du musst in der Lage sein, Deine Ideen und Lösungen klar und prĂ€zise zu vermitteln. Dies gilt sowohl fĂŒr die Zusammenarbeit mit anderen Entwicklern als auch fĂŒr die Kommunikation mit Nicht-Technikern.

Chicago Pd Nbc GIF by One Chicago

Teamarbeit

Die FĂ€higkeit, effektiv im Team zu arbeiten, ist entscheidend. Dies umfasst das Teilen von Wissen, das UnterstĂŒtzen von Kollegen und das gemeinsame Lösen von Problemen. Teamarbeit fördert auch die Weiterentwicklung und das Lernen innerhalb des Teams.

TV gif. Four casually dressed coworkers jump in the air and high-five each other as a group, clearly elated about a success. Rainbow colored sparkling text reads

Problemlösung

ProblemlösungsfĂ€higkeiten sind zentral fĂŒr die Arbeit eines Entwicklers. Du musst in der Lage sein, komplexe Probleme zu analysieren, zu zerlegen und effektive Lösungen zu finden. Eine strukturierte Herangehensweise an Problemlösungen hilft dabei, effizienter zu arbeiten.

The Daily Show Lol GIF by The Daily Show with Trevor Noah

AnpassungsfÀhigkeit

Die Technologie entwickelt sich stÀndig weiter. Als Entwickler musst Du bereit sein, neue Technologien und Methoden zu lernen und Dich an VerÀnderungen anzupassen. AnpassungsfÀhigkeit ermöglicht es Dir, auf neue Herausforderungen flexibel zu reagieren.

Agility Scrum GIF by Parade of Homes IG

Code-Reviews und Pair-Programming đŸ§‘â€đŸ€â€đŸ§‘

Code-Reviews

Code-Reviews sind ein wichtiger Bestandteil der Softwareentwicklung. Sie ermöglichen es, Fehler frĂŒhzeitig zu erkennen und den Code zu verbessern. Durch regelmĂ€ĂŸige Code-Reviews wird die CodequalitĂ€t erhöht und das Wissen im Team geteilt.

Pair-Programming

Pair-Programming ist eine Technik, bei der zwei Entwickler gemeinsam an einem Computer arbeiten. Einer schreibt den Code, wĂ€hrend der andere ihn ĂŒberprĂŒft. Diese Methode fördert den Wissensaustausch und die Zusammenarbeit im Team.

Test-Driven Development (TDD) đŸ§Ș

Vorteile von TDD

Test-Driven Development (TDD) ist eine Methode, bei der Tests geschrieben werden, bevor der eigentliche Code entwickelt wird. Dies hilft, klare Anforderungen zu definieren und sicherzustellen, dass der Code die erwarteten Ergebnisse liefert.

Beispiel fĂŒr TDD in JavaScript

// Test schreiben
const assert = require('assert');

function add(a, b) {
  return a + b;
}

assert.strictEqual(add(1, 2), 3);
assert.strictEqual(add(-1, 1), 0);

// Code implementieren
function add(a, b) {
  return a + b;
}

Enter fullscreen mode Exit fullscreen mode

TDD fĂŒhrt zu einem robusteren und weniger fehleranfĂ€lligen Code, da jede FunktionalitĂ€t durch Tests abgedeckt ist.

Weitere hilfreiche Prinzipien und Techniken

ModularitÀt

ModularitÀt bedeutet, den Code in kleine, unabhÀngige Module aufzuteilen. Jedes Modul sollte eine klar definierte Aufgabe haben. Dies erleichtert das Testen, Warten und Wiederverwenden von Code.

// Unmodularer Code
function processData(data) {
  // Datenvalidierung
  if (!Array.isArray(data)) {
    throw new Error('Invalid data');
  }

  // Datenverarbeitung
  const processed = data.map(item => item * 2);

  // Datenausgabe
  console.log(processed);
  return processed;
}

// Modularer Code
function validateData(data) {
  if (!Array.isArray(data)) {
    throw new Error('Invalid data');
  }
}

function processData(data) {
  return data.map(item => item * 2);
}

function logData(data) {
  console.log(data);
}

const inputData = [1, 2, 3, 4, 5];
validateData(inputData);
const processedData = processData(inputData);
logData(processedData);

Enter fullscreen mode Exit fullscreen mode

Automatisiertes Testen

Automatisiertes Testen ist unerlĂ€sslich, um sicherzustellen, dass der Code korrekt funktioniert und dass neue Änderungen keine bestehenden FunktionalitĂ€ten brechen. Unit-Tests, Integrationstests und End-to-End-Tests sind gĂ€ngige Testarten.

const { expect } = require('chai');

// Funktion zum Testen
function add(a, b) {
  return a + b;
}

// Unit-Test
describe('add', () => {
  it('should return the sum of two numbers', () => {
    expect(add(1, 2)).to.equal(3);
    expect(add(-1, 1)).to.equal(0);
  });
});

Enter fullscreen mode Exit fullscreen mode

Dokumentation

Gute Dokumentation ist entscheidend, um anderen Entwicklern (und Deinem zukĂŒnftigen Ich) zu helfen, den Code zu verstehen. Dies umfasst sowohl Kommentare im Code (z.B. bei JavaScript JSDoc) als auch externe Dokumentation wie README-Dateien.

/**
 * Addiert zwei Zahlen.
 *
 * @param {number} a - Die erste Zahl.
 * @param {number} b - Die zweite Zahl.
 * @return {number} Die Summe der beiden Zahlen.
 */
function add(a, b) {
  return a + b;
}

Enter fullscreen mode Exit fullscreen mode

Documentation - JSDoc ReferenceWhat JSDoc does TypeScript-powered JavaScript support?

Fazit 🎉

Guter Code ist klar, lesbar und wartbar. Er ermöglicht effiziente Zusammenarbeit und erleichtert die Wartung und Weiterentwicklung von Projekten.

Durch die Anwendung von Best Practices und Prinzipien wie Klarheit vor Cleverness, Konsistenz und DRY kannst Du die QualitÀt Deines Codes erheblich verbessern. Funktionale Programmierung und die Entwicklung wichtiger Soft Skills tragen ebenfalls dazu bei, ein erfolgreicher Entwickler zu werden.

Denke daran, dass Du Code nicht nur fĂŒr Dich selbst schreibst, sondern auch fĂŒr andere Entwickler und fĂŒr Dein zukĂŒnftiges Ich.

Tim And Eric Omg GIF


If you like my posts, it would be nice if you follow my Blog for more tech stuff.

Top comments (0)