
Teilen Sie:
Als ausgebildeter Schauspieler mit einer Dissertation in Standup-Comedy bin ich über die Meetup-Szene zur PHP-Entwicklung gekommen. Man findet mich, wenn ich über Technik spreche oder schreibe, oder wenn ich seltsame Platten aus meiner Vinylsammlung spiele oder kaufe.
Was ist also leistungsorientierte Entwicklung in PHP?
Lesedauer: 6 Minuten
Während meiner langjährigen Erfahrung in der Webentwicklung habe ich einige erstaunliche Codes gesehen. Obwohl ich mich selbst nicht gerade als eine Art Code-Zauberer betrachte, habe ich ein Verständnis für einige der Grundlagen, die wir wissen müssen, um saubere und effiziente Anwendungen zu schreiben.
Eine der größten Herausforderungen, mit denen wir in dieser Branche konfrontiert sind, ist es, Entwickler dazu zu bringen, mit Hilfe von Test-Driven Development (TDD) zu schreiben. Die gute Nachricht ist, dass sich die Dinge, soweit ich sehen kann, ändern - vor allem in Startup-Unternehmen und Agenturen.
Unsere Arbeit hier ist also getan, oder? (Offensichtlich nicht!)
In einem Vortrag, den ich über Xdebughabe ich auf die Notwendigkeit einer "leistungsorientierten Entwicklung" angespielt. Es ist an der Zeit, dies wieder aufzugreifen und mein Versprechen einzulösen, tiefer in das Thema einzusteigen.
Wenn wir Code schreiben, um ihn durch Testen robuster zu machen - warum tun wir nicht dasselbe, wenn es um die Leistung geht? Wer hat schon von PDD gehört? Das Verhalten in der professionellen Softwareentwicklung, das ich gesehen habe, geht immer auf das Klischee zurück, dass Entwickler
Sicherheit
Leistung
...zuletzt. Sie haben Fristen. Sie müssen es verschicken. Es funktioniert. Um diese beiden Aufzählungspunkte kümmern wir uns später. Es ist an der Zeit, sich ein Beispiel anzuschauen.
Einer der Aspekte der Entwicklung in PHP, den ich am häufigsten gesehen habe, ist das Hämmern von Entwicklern an ORMs in Symfony und Laravel "bis es tut, was ich will", ohne an die zugrunde liegende SQL-Abfrage zu denken und bei jeder Gelegenheit forEach-Schleifen einzubauen. Beziehungen können, wenn sie architektonisch nicht berücksichtigt werden, leicht zu n+1 Probleme bei der Verwendung von Abfragen. Man sollte meinen, dass dies nicht allzu häufig vorkommt, aber wie ich aus Erfahrung weiß, ist dies überall der Fall.
Ein Beispiel ist der folgende Laravel-Code:
public function getDashboardData()
{
$dashboardData = [];
foreach (Contact::all() as $contact) {
$settings = $contact->settings()->get();
foreach ($settings as $setting) {
if ($setting->setting_name === 'CanPhone') {
if ($setting->setting_value === '1') {
$dashboardData['canPhone']['contact'][] = $setting->contact()->with('settings')->get()->toArray();
}
}
}
}
return view('dashboard', ['data' => $dashboardData]);
Bevor wir uns mit den Mechanismen der ORM-Nutzung durch die Entwickler befassen können, ist unser Ausgangspunkt Contact::all(). Ich habe so etwas schon überall gesehen - in den Köpfen der Entwickler ist der einfachste Weg, die Daten, die dieses Dashboard ausgibt, zu kompilieren, eine Schleife über jede Entität in einer Tabelle, also alles zu holen. In Bezug auf Tabellenscans in MySql bedeutet dies, dass man bereits eine ganze Tabelle abruft und dann weitere Abfragen darauf ausführt, um die Einstellungsbeziehung zu erhalten. In diesem Fall (und das ist ein anonymisiertes Stück Code aus dem wirklichen Leben, das verspreche ich Ihnen) macht die Einstellungsbeziehung dann einen Aufruf, um den Kontakt wieder herauszuholen, obwohl er bereits im Kontext der Schleife existiert.
Beispiele wie diese sind vielleicht nicht diejenigen, die ein erfahrener Entwickler schreiben würde - aber das spielt keine Rolle. Tatsache ist, dass Sie vielleicht einen kompletten Monolithen, den jemand anderes geschrieben hat, erben oder daran mitarbeiten. Wenn Sie keine Erfahrung haben, ist es möglich, dass Sie direkt in die Anwendungsentwicklung eingestiegen sind, ohne vorher genau zu verstehen, wie SQL eigentlich funktioniert (bei komplexeren Abfragen weiß ich das, um ehrlich zu sein, nicht). Es werden also n+1 Probleme auftreten, wenn Sie einen ORM nicht so programmieren können, dass er SQL-Konzepte wie LEFT JOIN und INNER JOIN effektiv nutzt.
Wir haben viele Tools, die Ihnen helfen, solche Probleme zu entdecken - Symfony hat seine ausgezeichnete DebugbarLaravel hat auch eine, Xdebug kommt mit einem leistungsstarken Profiler, und Tideways besitzt jetzt Xhprof. Das Problem, das ich sehe, ist, dass Entwickler von unten nach oben dazu neigen, an die Performance als letztes zu denken, wenn es um das alltägliche Geschäft des Schreibens von Features geht, anstatt diese Tools als Teil des Prozesses des Schreibens von Code und des Einreichens für Pull Requests zu verwenden. In gewisser Weise ähnelt dies dem Klischee, dass Entwickler zuletzt an die Sicherheit denken - obwohl eine idealere Lösung darin bestünde, dass das gesamte Team beim Schreiben von PHP-Code sowohl die Sicherheit als auch die Leistung berücksichtigt.
Ich sprach mit Blackfire und Plattform.sh Ingenieur für Entwicklerbeziehungen Thomas di Luccio darüber, was wir beim Schreiben von Anwendungen beachten sollten, die Leistung als "erstklassiges" Anliegen betrachten. Das Hauptargument war, so di Luccio, dass wir als Entwickler beim Schreiben von Code die Leistung von der absoluten Basisebene aus berücksichtigen müssen, und dass wir mehr davon tun müssen.
Dies wurde deutlich, als ich vor vielen Jahren an einer Anwendung arbeitete, die einen nur allzu bekannten Ansatz verfolgte. Anstatt sich Gedanken über SQL-Anweisungen zu machen und darüber, wie viel Arbeit verschiedene Klassen, die SPL-Funktionen wie foreach() und array_map() verwendeten, machten, und ohne Kontrolle über das zugrundeliegende Framework (das proprietär war, also mit wenig Kontrolle über das ORM), wurde Fastly auf das Frontend aufgeschraubt. Ja, das Problem ist gelöst. Aber die zugrundeliegende Codebasis war immer noch voll mit technischen Schulden.
Es ist Zeit für Lösungen. Nehmen wir unser obiges getDashboardData()-Beispiel, so müssen wir es beheben. Bevor wir es jedoch beheben, müssen wir ein Profil erstellen, was passiert, wenn wir auf die Dashboard-Seite zugreifen. Das Ausführen der Laravel-Debugbar gibt Folgendes aus:
Zweihundertsechsundachtzig SQL-Abfragen, um das Dashboard zu laden - in 11,92 Sekunden. Wie ist das möglich?
Nun, es ist ein klassisches n+1.
Auf dem Rechner des Entwicklers dauert das Laden des Dashboards 1 Sekunde, da sich eine Handvoll Fixtures in der Datenbank befinden. Sobald dieser Code in die Produktion geht, geschieht dies.
Wenn Sie diese Tools für die Entwicklungsumgebungen Ihrer Teams aktiviert haben, gehören solche Vorkommnisse der Vergangenheit an. Der erste Schritt zur Kontrolle solcher Probleme ist die Festlegung von Schwellenwerten. Hilfsmittel wie Blackfire und Tideways ermöglichen es Ihnen, Schwellenwerte festzulegen und sogar in Ihre Pipelines zu integrieren. Unabhängig davon, welchen Ansatz Sie wählen (d. h. kostenloses Tooling oder Unternehmensprodukte), müssen Sie jedoch immer noch Erwartungen daran haben, was für Ihre Anwendung akzeptabel ist, wenn die Entwickler sie programmieren.
Nehmen wir an, das Ziel sind weniger als 5 Abfragen und eine Ladezeit von unter einer Sekunde.
Ein erfahrener Entwickler prüft den Code, erkennt die n+1-Probleme und erklärt dem Junior, wie er die Daten auf einen Schlag erhält. Hier ist das Ergebnis:

Zweihundertsechsundachtzig SQL-Abfragen, um das Dashboard zu laden - in 11,92 Sekunden. Wie ist das möglich?
Nun, es ist ein klassisches n+1.
Auf dem Rechner des Entwicklers dauert das Laden des Dashboards 1 Sekunde, da sich eine Handvoll Fixtures in der Datenbank befinden. Sobald dieser Code in die Produktion geht, geschieht dies.
Wenn Sie diese Tools für die Entwicklungsumgebungen Ihrer Teams aktiviert haben, gehören solche Vorkommnisse der Vergangenheit an. Der erste Schritt zur Kontrolle derartiger Probleme ist die Festlegung von Schwellenwerten. Hilfsmittel wie Blackfire und Tideways ermöglichen es Ihnen, Schwellenwerte festzulegen und sogar in Ihre Pipelines zu integrieren. Unabhängig davon, welchen Ansatz Sie wählen (d. h. kostenloses Tooling oder Unternehmensprodukte), müssen Sie jedoch immer noch Erwartungen daran haben, was für Ihre Anwendung akzeptabel ist, wenn die Entwickler sie programmieren.
Nehmen wir an, das Ziel sind weniger als 5 Abfragen und eine Ladezeit von unter einer Sekunde.
Ein erfahrener Entwickler prüft den Code, erkennt die n+1-Probleme und erklärt dem Junior, wie er die Daten auf einen Schlag erhält. Hier ist das Ergebnis:
public function getDashboardData()
{
$dashboardData = Contact::with('settings')
->whereRelation('settings', 'setting_name', '=', 'canPhone')
->paginate(10)
->toArray();
return $dashboardData;
}
Viel sauberer. Aber das Wichtigste ist: Wie sieht das in der Debug-Leiste aus?

Viel sauberer. Aber das Wichtigste ist: Wie sieht das in der Debug-Leiste aus?
Vier SQL-Anweisungen, 324ms zum Laden.
Die Lehre aus diesem Artikel ist nicht "hier ist offensichtlich schlechter Code, ich habe ihn korrigiert, und die Leistung ist besser". Sie könnten wahrscheinlich sehen, dass der Code schlecht war und dass er korrigiert werden musste, klar. Wenn Sie eine Führungsposition innehaben, sollten Sie die Debug-Balken von Symfony und Laravel aktivieren und als Teil Ihres Entwicklungsprozesses verwenden, damit Ihr gesamtes Team sie nutzt. Sie können einen groben Schwellenwert festlegen, einen Test einführen, der eine Methode dazu zwingt, viel härter zu arbeiten und die Ladezeit zu bewerten. Wir haben die Werkzeuge dafür, warum sollte dies also eine zweitrangige Überlegung sein?
Was ist leistungsabhängige Entwicklung? Es ist der Prozess.
Es geht darum, Ihren Entwicklern die Werkzeuge an die Hand zu geben, die sie benötigen, während sie an einer Funktion arbeiten. Genauso wie die Sicherheit sollte sie ein erstklassiger Bestandteil des Entwicklungszyklus sein, und in der PHP-Welt haben wir absolut erstklassige Open-Source-Tools, um hochwertige Software zu liefern.
Teilen Sie:
Als ausgebildeter Schauspieler mit einer Dissertation in Standup-Comedy bin ich über die Meetup-Szene zur PHP-Entwicklung gekommen. Man findet mich, wenn ich über Technik spreche oder schreibe, oder wenn ich seltsame Platten aus meiner Vinylsammlung spiele oder kaufe.