Wie das Sprichwort sagt, wenn Sie sich für Ihren alten Code nicht schämen, dann wachsen Sie nicht als Programmierer auf - und ich stimme dieser Meinung zu. Ich habe vor über 40 Jahren und vor 30 Jahren beruflich angefangen, für Unterhaltung zu programmieren, also habe ich
viele Fehler gemacht. Als Professor für Informatik bringe ich meinen Schülern bei, wie man aus Fehlern lernt - ihren, meinen, Fremden. Ich denke, es ist Zeit, über meine Fehler zu sprechen, um die Bescheidenheit nicht zu verlieren. Ich hoffe, jemand wird es nützlich finden.
Dritter Platz - Microsoft C-Compiler
Mein Schullehrer glaubte, dass „Romeo und Julia“ nicht als Tragödie angesehen werden können, da die Helden keine tragische Schuld hatten - sie verhielten sich einfach dumm, wie Teenager es sollten. Damals war ich mit ihm nicht einverstanden, aber jetzt sehe ich seiner Meinung nach einen rationalen Kernel - insbesondere im Zusammenhang mit der Programmierung.
Als ich mein zweites Jahr am MIT beendete, war ich jung und unerfahren, sowohl im Leben als auch in der Programmierung. Im Sommer habe ich eine Übung bei Microsoft im Team des C-Compilers durchgeführt. Zuerst war ich mit einer Routine wie der Profilerstellung befasst, und dann wurde mir die Arbeit an dem (wie ich dachte) spaßigsten Teil der Compiler-Backend-Optimierung anvertraut. Insbesondere musste ich den x86-Code für Verzweigungsanweisungen verbessern.
Entschlossen, den optimalen Maschinencode für jeden möglichen Fall zu schreiben, eilte ich mit meinem Kopf in den Pool. Wenn die Verteilungsdichte der Werte hoch war, habe ich sie in die
Übergangstabelle eingetragen . Wenn sie einen gemeinsamen Divisor hatten, habe ich ihn verwendet, um die Tabellendichte zu erhöhen (aber nur, wenn die Division mit einer
Bitverschiebung durchgeführt werden konnte ). Wenn alle Werte Zweierpotenzen waren, führte ich eine weitere Optimierung durch. Wenn der Wertesatz meine Bedingungen nicht erfüllt, habe ich ihn in mehrere optimierbare Fälle aufgeteilt und den bereits optimierten Code verwendet.
Es war ein Albtraum. Nach vielen Jahren erzählten sie mir, dass der Programmierer, der meinen Code geerbt hatte, mich hasste.
Lektion gelernt
Wie David Patterson und John Hennessy in dem Buch Computer Architecture and Computer Systems Design schreiben, ist eines der Hauptprinzipien der Architektur und Entwicklung, dass im Allgemeinen alles so schnell wie möglich funktioniert.
Die Beschleunigung häufiger Fälle erhöht die Produktivität effizienter als die Optimierung seltener Fälle. Ironischerweise sind häufige Fälle oft einfacher als selten. Dieser logische Ratschlag impliziert, dass Sie wissen, welcher Fall als häufig anzusehen ist - und dies ist nur durch sorgfältiges Testen und Messen möglich.
Zu meiner Verteidigung kann ich sagen, dass ich versucht habe herauszufinden, wie Verzweigungsoperatoren in der Praxis aussahen (zum Beispiel wie viele Verzweigungen existierten und wie Konstanten verteilt wurden), aber 1988 waren diese Informationen nicht verfügbar. Ich hätte jedoch keine Sonderfälle hinzufügen sollen, wenn der aktuelle Compiler nicht den optimalen Code für das künstliche Beispiel generieren konnte, das mir einfiel.
Ich musste einen erfahrenen Entwickler anrufen und mit ihm über die häufigsten Fälle nachdenken und mich speziell mit ihnen befassen. Ich würde weniger Code schreiben, aber das ist sogar gut. Jeff Atwood, Gründer von Stack Overflow, schrieb, der schlimmste Feind des Programmierers sei der Programmierer:
Ich weiß, dass Sie, wie wir alle, die besten Absichten haben. Wir erstellen Programme und lieben es, Code zu schreiben. Also sind wir arrangiert. Wir glauben, dass jedes Problem mit Klebeband, einer selbstgemachten Krücke und einer Prise Code gelöst werden kann. Egal wie schmerzhaft es für Codierer ist, dies zuzugeben, der beste Code ist der Code, der nicht existiert. Jede neue Zeile benötigt Debugging und Support, es muss verstanden werden. Wenn Sie neuen Code hinzufügen, sollten Sie dies mit Zurückhaltung und Ekel tun, da alle anderen Optionen ausgeschöpft sind. Viele Programmierer schreiben zu viel Code und machen ihn zu unserem Feind.
Wenn ich einfacheren Code schreiben würde, der häufig vorkommende Fälle abdeckt, wäre es viel einfacher, ihn bei Bedarf zu aktualisieren. Ich habe ein Chaos hinterlassen, mit dem sich niemand anlegen wollte.
Zweiter Platz: Social Media Advertising
Als ich bei Google an Social Media-Werbung arbeitete (erinnerst du dich an Myspace?), Schrieb ich in C ++ etwa Folgendes:
for (int i = 0; i < user->interests->length(); i++) { for (int j = 0; j < user->interests(i)->keywords.length(); j++) { keywords->add(user->interests(i)->keywords(i)) { } }
Programmierer können den Fehler sofort sehen: Das letzte Argument sollte j sein, nicht i. Unit-Tests haben weder einen Fehler ergeben, noch hat mein Prüfer ihn bemerkt. Es wurde ein Start durchgeführt, und eines Nachts ging mein Code auf den Server und stürzte alle Computer im Rechenzentrum ab.
Es ist nichts Schlimmes passiert. Keiner von ihnen ist kaputt gegangen, da der Code vor dem globalen Start im selben Rechenzentrum getestet wurde. Es sei denn, die SRE-Ingenieure hörten für kurze Zeit auf, Billard zu spielen, und machten einen kleinen Rollback. Am nächsten Morgen erhielt ich eine E-Mail mit einem Crash-Dump, korrigierte den Code und fügte Unit-Tests hinzu, die einen Fehler aufdeckten. Da ich das Protokoll befolgt habe - sonst wäre mein Code einfach nicht gelaufen - gab es keine weiteren Probleme.
Lektion gelernt
Viele sind überzeugt, dass ein so schwerwiegender Fehler notwendigerweise der Schuldige der Entlassung ist, aber das ist nicht so: Erstens sind alle Programmierer falsch, und zweitens machen sie selten zweimal einen Fehler.
Tatsächlich habe ich einen vertrauten Programmierer - einen brillanten Ingenieur, der wegen eines einzigen Fehlers gefeuert wurde. Danach wurde er von Google eingestellt (und bald befördert) - er sprach ehrlich über den Fehler im Interview, und sie galt nicht als tödlich.
Das sagt Thomas Watson, der legendäre Leiter von IBM:
Eine Regierungsverordnung im Wert von rund einer Million Dollar wurde angekündigt. Die IBM Corporation - oder besser gesagt Thomas Watson Sr. - wollte es unbedingt haben. Leider konnte der Vertriebsmitarbeiter dies nicht tun, und IBM verlor die Ausschreibung. Am nächsten Tag kam dieser Beamte in Mr. Watsons Büro und legte einen Umschlag auf seinen Schreibtisch. Mr. Watson sah ihn nicht einmal an - er wartete auf einen Angestellten und wusste, dass dies ein Kündigungsschreiben war.
Watson fragte, was schief gelaufen sei.
Der Handelsvertreter beschrieb den Fortschritt der Ausschreibung im Detail. Er nannte die Fehler, die vermieden werden konnten. Schließlich sagte er: „Mr. Watson, danke, dass Sie mich erklären ließen. Ich weiß, wie sehr wir diese Bestellung brauchten. Ich weiß, wie wichtig er war “und wollte gehen.
Watson ging an der Tür auf ihn zu, sah ihm in die Augen und gab den Umschlag mit den Worten zurück: „Wie kann ich dich gehen lassen? Ich habe gerade eine Million Dollar in deine Ausbildung investiert.
Ich habe ein T-Shirt mit der Aufschrift: "Wenn du wirklich aus Fehlern lernst, bin ich bereits ein Meister." In der Tat bin ich ein Doktor der Wissenschaften, was Fehler betrifft.
Erster Platz: App Inventor API
Wirklich beängstigende Fehler betreffen eine große Anzahl von Benutzern, werden öffentlich, werden lange behoben und von denen begangen, die sie nicht zulassen konnten. Mein größter Fehler erfüllt alle diese Kriterien.
Je schlimmer desto besser
Ich habe
Richard Gabriels Aufsatz über diesen Ansatz in den Neunzigern als Doktorand gelesen und es gefällt mir so gut, dass ich es meinen Studenten vorstelle. Wenn Sie sich nicht gut daran erinnern, aktualisieren Sie Ihr Gedächtnis, es ist klein. In diesem Aufsatz werden der Wunsch, „es richtig zu machen“ und der Ansatz „je schlechter, desto besser“ in vielerlei Hinsicht kontrastiert, einschließlich der Einfachheit.
Wie es sein sollte: Das Design sollte einfach zu implementieren und zu bedienen sein. Die Einfachheit der Schnittstelle ist wichtiger als die Einfachheit der Implementierung.
Je schlechter, desto besser: Das Design sollte einfach in Implementierung und Oberfläche sein. Die Einfachheit der Implementierung ist wichtiger als die Einfachheit der Schnittstelle.
Vergiss es für einen Moment. Leider habe ich es viele Jahre vergessen.
App Erfinder
Während meiner Zeit bei Google war ich Teil des
App Inventor- Teams, einer Online-Entwicklungsumgebung mit Drag & Drop-Unterstützung für Android-Einsteiger. Es war 2009 und wir hatten es eilig, die Alpha-Version rechtzeitig zu veröffentlichen, sodass wir im Sommer Meisterkurse für Lehrer abhalten konnten, die die Lernumgebung im Herbst nutzen konnten. Ich meldete mich freiwillig, um Sprites zu implementieren, die nostalgisch dafür waren, wie ich Spiele auf TI-99/4 geschrieben habe. Für diejenigen, die sich nicht auskennen: Ein Sprite ist ein zweidimensionales Grafikobjekt, das sich bewegen und mit anderen Programmelementen interagieren kann. Beispiele für Sprites sind Raumschiffe, Asteroiden, Bälle und Schläger.
Wir haben den objektorientierten App Inventor in Java implementiert, sodass es nur eine Reihe von Objekten gibt. Da sich Bälle und Sprites sehr ähnlich verhalten, habe ich eine abstrakte Sprite-Klasse mit Eigenschaften (Feldern) X, Y, Geschwindigkeit (Geschwindigkeit) und Richtung (Richtung) erstellt. Sie hatten die gleichen Methoden, um Kollisionen zu erkennen, vom Bildschirmrand abzuprallen usw.
Der Hauptunterschied zwischen der Kugel und dem Sprite ist genau das, was gezeichnet wird - ein gefüllter Kreis oder ein gefülltes Raster. Seit ich Sprites zum ersten Mal implementiert habe, war es logisch, die x- und y-Koordinaten der oberen linken Ecke des Ortes anzugeben, an dem sich das Bild befand.
Als die Sprites zu funktionieren begannen, entschied ich mich, dass Sie Ball-Objekte mit sehr wenig Code implementieren können. Das einzige Problem war, dass ich den einfachsten Weg gegangen bin (aus Sicht des Implementierers), indem ich die x- und y-Koordinaten der oberen linken Ecke der den Ball umgebenden Kontur angegeben habe.
In der Tat war es notwendig, die x- und y-Koordinaten des Mittelpunkts des Kreises anzugeben, wie es jedes Mathematiklehrbuch und jede andere Quelle, die Kreise erwähnte, lehrt.
Im Gegensatz zu meinen früheren Fehlern litten nicht nur meine Kollegen, sondern auch Millionen von App Inventor-Benutzern darunter. Viele von ihnen waren Kinder oder völlig neu in der Programmierung. Sie mussten viele unnötige Aktionen ausführen, wenn sie an jeder Anwendung arbeiteten, in der der Ball vorhanden war. Wenn ich mich an den Rest meiner Fehler mit einem Lachen erinnere, dann bringt mich das heute ins Schwitzen.
Ich habe diesen Fehler erst vor kurzem, zehn Jahre später, behoben. "Gepatcht", aber nicht "gefixt", denn wie Joshua Bloch sagt, sind die APIs ewig. Wir konnten keine Änderungen vornehmen, die sich auf vorhandene Programme auswirken würden, und haben die OriginAtCenter-Eigenschaft mit false in alten Programmen und true in allen zukünftigen Programmen hinzugefügt. Benutzer können eine berechtigte Frage stellen, an wen sich jemand gewandt hat, um einen Referenzpunkt an einem anderen Ort als der Mitte zu lokalisieren. An wen? Ein Programmierer, der vor zehn Jahren zu faul war, um eine normale API zu erstellen.
Lektionen gelernt
Wenn Sie an einer API arbeiten (was fast jeder Programmierer manchmal tun muss), sollten Sie die besten Tipps befolgen, die in Joshua Blochs Video "
Wie man eine gute API erstellt und warum sie so wichtig ist " oder
in dieser kurzen Liste aufgeführt sind :
- Die API kann für Sie sowohl von großem Nutzen als auch von großem Schaden sein . Eine gute API schafft treue Kunden. Schlecht wird dein ewiger Albtraum.
- Öffentliche APIs sind wie Diamanten ewig . Versuchen Sie Ihr Bestes: Es gibt keine andere Chance, alles so zu machen, wie es sollte.
- Zeitpläne für die API sollten kurz sein - eine Seite mit Klassen- und Methodensignaturen und Beschreibungen, die nicht mehr als eine Zeile umfassen. Auf diese Weise können Sie die API leicht umstrukturieren, wenn sie beim ersten Mal nicht perfekt ist.
- Beschreiben Sie Verwendungsszenarien, bevor Sie die API implementieren und sogar an ihrer Spezifikation arbeiten. Auf diese Weise vermeiden Sie die Implementierung und Spezifikation einer nicht funktionsfähigen API.
Wenn ich zumindest eine kleine Zusammenfassung mit einer künstlichen Schrift geschrieben hätte, hätte ich höchstwahrscheinlich einen Fehler identifiziert und korrigiert. Wenn nicht, würde es definitiv einer meiner Kollegen tun. Jede Entscheidung, die weitreichende Konsequenzen hat, muss mindestens einen Tag in Betracht gezogen werden (dies gilt nicht nur für die Programmierung).
Der Titel von Richard Gabriels Aufsatz "The Worse, the Better" zeigt den Vorteil, der - selbst bei einem unvollkommenen Produkt - zuerst auf den Markt kommt, während jemand anderes seit Ewigkeiten das Ideal verfolgt. Wenn ich über den Sprite-Code nachdenke, habe ich verstanden, dass ich nicht einmal mehr Code schreiben musste, um alles so zu machen, wie es sollte. Ob es mir gefällt oder nicht, ich habe mich grob geirrt.
Fazit
Programmierer machen jeden Tag Fehler - ob sie nun Code mit Fehlern schreiben oder nicht, um etwas auszuprobieren, das ihre Fähigkeiten und Produktivität steigert. Natürlich können Sie ein Programmierer sein und nicht so schwerwiegende Fehler zulassen wie ich. Es ist jedoch unmöglich, ein guter Programmierer zu werden, ohne seine Fehler zu erkennen und daraus nicht zu lernen.
Ich treffe ständig Studenten, die denken, dass sie zu viele Fehler machen und deshalb nicht für die Programmierung gedacht sind. Ich weiß, wie häufig das Imposter-Syndrom in der IT ist. Ich hoffe, Sie lernen die Lektionen, die ich aufgelistet habe - aber erinnern Sie sich an die wichtigste: Jeder von uns macht Fehler - beschämend, lustig, beängstigend. Ich werde überrascht und verärgert sein, wenn ich in Zukunft nicht genug Material habe, um den Artikel fortzusetzen.