Von RFC zu Production: Was dir niemand über die Implementation erzählt

Die ehrliche Einschätzung eines Principal Engineers über die Kluft zwischen schönen RFC-Designs und chaotischer Production-Realität, mit echten Lektionen aus Notification-System-Implementierungen

Kennst du das Gefühl, wenn du ein wunderschön ausgearbeitetes RFC liest, bei den eleganten Architektur-Diagrammen nickst und denkst "Das ist es, das ist das Design, das endlich perfekt funktionieren wird"? Dann findest du dich sechs Monate später knietief in Production-Problemen wieder, die Timeline hat sich verdoppelt, und das makellose Database-Schema sieht aus, als wäre es durch einen Mixer gelaufen?

Willkommen in der Realität, RFCs in Production-Systeme zu verwandeln. Nach 20 Jahren, in denen ich brillante Designs mit organisatorischer Realität kollidieren sah, habe ich gelernt: Die Lücke zwischen RFC und Production ist kein Bug – es ist ein Feature beim Bau komplexer Systeme mit echten Teams unter tatsächlichem Business-Druck.

Lass mich dir zeigen, was wirklich passiert, wenn das 12-Wochen-Notification-System-RFC auf das Chaos der Production trifft, mit echten Erfahrungen aus mehreren Implementierungen. Spoiler: Es dauerte 8 Monate, nicht 12 Wochen, und das finale System sah nichts wie das ursprüngliche Design aus.

Die RFC-Honeymoon-Phase#

Jedes RFC beginnt mit Optimismus. Das Notification-System-RFC, an das ich denke, war ein Meisterwerk: saubere Architektur-Diagramme, umfassende Database-Schemas, phasenweise Rollout-Pläne. Es versprach, jedes Notification-Problem zu lösen, das wir je hatten:

TypeScript
// Das schöne Versprechen des RFC
interface NotificationSystemGoals {
  deliveryTime: '<100ms für In-App, <5s für Email',
  throughput: '10.000+ Notifications pro Sekunde',
  uptime: '99,9% Verfügbarkeit',
  timeline: '12 Wochen mit 2 Developern',
  budget: '$120.000-180.000'
}

// Was tatsächlich passierte
interface ProductionReality {
  deliveryTime: '2-3s für In-App an guten Tagen, 30s+ während Peaks',
  throughput: 'Startete bei 500/Sek, brauchte 6 Monate für 5.000/Sek',
  uptime: '97% erstes Quartal, 99% nach Jahr eins',
  timeline: '8 Monate mit 4 Developern plus 2 Contractors',
  budget: '$400.000+ und Maintenance-Kosten laufen noch'
}

Das RFC sah kugelsicher aus. Wir hatten an alles gedacht: Rate Limiting, Deduplication, Preference Management, sogar Ruhezeiten. Der Phasen-Ansatz schien konservativ – sicher waren 4 Wochen für Core Infrastructure genug?

Wenn Database-Schemas auf die Realität treffen#

Das Database-Schema des RFC war ein Kunstwerk. Sauber, normalisiert, mit ordentlichen Foreign Keys und Constraints. Das schlug das RFC vor:

SQL
-- Das makellose Schema des RFC
CREATE TABLE notification_events (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id UUID REFERENCES users(id) ON DELETE CASCADE,
    notification_type VARCHAR(100) NOT NULL,
    template_id UUID REFERENCES notification_templates(id),
    data JSONB DEFAULT '{}',
    status VARCHAR(20) DEFAULT 'pending',
    sent_at TIMESTAMP,
    delivered_at TIMESTAMP,
    read_at TIMESTAMP,
    created_at TIMESTAMP DEFAULT NOW()
);

Drei Monate in Production sah diese Tabelle tatsächlich so aus:

SQL
-- Production-Realität nach 20+ Migrations
CREATE TABLE notification_events (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id UUID, -- Foreign Key entfernt wegen Performance-Problemen
    notification_type VARCHAR(100),
    notification_type_v2 VARCHAR(255), -- Migration läuft
    template_id UUID,
    template_id_v2 BIGINT, -- Anderes Team nutzte anderen ID-Typ
    data JSONB DEFAULT '{}',
    data_compressed BYTEA, -- Hinzugefügt als JSONB zu groß wurde
    status VARCHAR(20) DEFAULT 'pending',
    status_v2 VARCHAR(50), -- Mehr Status als erwartet
    priority INTEGER DEFAULT 0, -- Nicht im RFC, kritisch für Production
    retry_count INTEGER DEFAULT 0, -- Nicht im RFC, essentiell fürs Debugging
    channel VARCHAR(50), -- Denormalisiert für Query-Performance
    correlation_id UUID, -- Hinzugefügt für Distributed Tracing
    partition_key INTEGER, -- Hinzugefügt für Sharding
    sent_at TIMESTAMP,
    delivered_at TIMESTAMP,
    read_at TIMESTAMP,
    failed_at TIMESTAMP, -- Nicht im RFC, sehr nötig
    expires_at TIMESTAMP, -- Nicht im RFC, verhinderte unendliches Wachstum
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW() -- Nach Debugging-Alpträumen hinzugefügt
);

-- Plus 15 Indexes, die wir nicht erwartet hatten
CREATE INDEX CONCURRENTLY idx_notification_events_user_created ON notification_events(user_id, created_at DESC) WHERE status != 'deleted';
CREATE INDEX CONCURRENTLY idx_notification_events_correlation ON notification_events(correlation_id) WHERE correlation_id IS NOT NULL;
-- ... und 13 weitere

Jede dieser Schema-Änderungen repräsentiert einen Production-Incident, eine Performance-Krise oder ein Feature-Request, das wir nicht vorhersehen konnten. Das makellose RFC-Design traf auf die Realität, und die Realität gewann.

Das WebSocket Connection Management Desaster#

Das RFC behauptete selbstbewusst, wir würden Real-Time-Notifications mit WebSockets handhaben. Der Sample-Code sah so sauber aus:

TypeScript
// RFC's WebSocket-Implementation
class NotificationWebSocketManager {
  private connections: Map<string, WebSocket> = new Map();
  
  async sendNotification(userId: string, notification: NotificationEvent) {
    const connection = this.connections.get(userId);
    if (connection && connection.readyState === WebSocket.OPEN) {
      connection.send(JSON.stringify({
        type: 'notification',
        data: notification
      }));
    }
  }
}

Sechs Monate später, nach mehreren Production-Incidents einschließlich des berüchtigten "50.000 Zombie-Connections"-Desasters während eines fehlgeschlagenen Mobile-App-Deployments, hatten wir tatsächlich das:

TypeScript
// Production-Realität mit allen Edge Cases
class NotificationWebSocketManager {
  private connections: Map<string, Set<WebSocketConnection>> = new Map();
  private connectionMetadata: Map<string, ConnectionMetadata> = new Map();
  private healthChecks: Map<string, NodeJS.Timeout> = new Map();
  private rateLimiters: Map<string, RateLimiter> = new Map();
  private deadLetterQueue: Queue<FailedNotification>;
  private circuit: CircuitBreaker;
  
  async sendNotification(userId: string, notification: NotificationEvent) {
    // 200+ Zeilen Defensive Programming
    const connections = this.connections.get(userId);
    if (!connections || connections.size === 0) {
      await this.queueForLaterDelivery(userId, notification);
      return;
    }
    
    // Handle mehrere Connections pro User (Mobile + Web + Tablet)
    const results = await Promise.allSettled(
      Array.from(connections).map(async (conn) => {
        try {
          // Check Connection-Health
          if (!this.isConnectionHealthy(conn)) {
            await this.reconnectOrEvict(conn);
            throw new Error('Unhealthy connection');
          }
          
          // Rate Limiting pro Connection
          const limiter = this.getRateLimiter(conn.id);
          if (!await limiter.tryAcquire()) {
            await this.backpressure(conn, notification);
            return;
          }
          
          // Circuit Breaker für kaskadierte Failures
          return await this.circuit.fire(async () => {
            // Message-Size-Validation (das haben wir auf die harte Tour gelernt)
            const message = this.serializeNotification(notification);
            if (message.length > MAX_MESSAGE_SIZE) {
              const chunks = this.chunkMessage(message);
              for (const chunk of chunks) {
                await this.sendChunk(conn, chunk);
              }
            } else {
              await this.sendMessage(conn, message);
            }
          });
        } catch (error) {
          await this.handleDeliveryFailure(conn, notification, error);
        }
      })
    );
    
    // Track Delivery-Metriken
    await this.recordDeliveryMetrics(userId, notification, results);
  }
  
  // Plus 50+ andere Methods für Edge Cases
}

Jede einzelne dieser Ergänzungen kam von einem Production-Incident. Der Circuit Breaker? Hinzugefügt, nachdem wir unseren Redis-Cluster lahmgelegt hatten. Die Chunking-Logic? Entdeckt, als eine Marketing-Notification mit eingebetteten Bildern Mobile Clients zum Absturz brachte. Das Rate Limiting? Das haben wir während eines Notification-Storms gelernt, der 100.000 Support-Tickets generierte.

Timeline vs. Realität: Die 12-Wochen-Täuschung#

Der Phasen-Ansatz des RFC schien so vernünftig:

  • Phase 1 (Wochen 1-4): Core Infrastructure
  • Phase 2 (Wochen 5-8): Advanced Features
  • Phase 3 (Wochen 9-12): Integration & Optimization

Was tatsächlich passierte:

Wochen 1-4: Die Infrastructure-Überraschung#

Statt Core Infrastructure zu bauen, verbrachten wir drei Wochen nur damit, Environments aufzusetzen und zu entdecken, dass unser "Standard"-Database-Setup den Write-Throughput nicht bewältigen konnte. Woche 4 wurde komplett von einem Production-Incident in einem anderen System verschlungen, der unser Team abzog.

Wochen 5-12: Die Scope-Creep-Symphonie#

Product Management wurde aufgeregt, als sie die frühen Demos sahen. "Können wir Slack-Notifications hinzufügen?" Klar, nicht zu schwer. "Was ist mit SMS für kritische Alerts?" Macht Sinn. "Oh, und wir müssen Notifications bis zu 90 Tage im Voraus für Marketing-Kampagnen schedulen können." Moment, was?

TypeScript
// Original Scope
const originalChannels = ['in_app', 'email', 'push'];

// Monat 3 Scope
const actualChannels = [
  'in_app', 
  'email', 
  'push', 
  'sms',          // Woche 6 hinzugefügt
  'slack',        // Woche 8 hinzugefügt
  'teams',        // Woche 10 hinzugefügt
  'webhook',      // Woche 11 hinzugefügt
  'discord',      // Woche 14 hinzugefügt (ja, wir waren schon verspätet)
  'voice_call'    // Woche 20 hinzugefügt (für kritische Security-Alerts)
];

Monate 4-6: Die Integration-Hölle#

Erinnerst du dich an das saubere API-Design im RFC? Es nahm an, dass alle unsere Services dasselbe Authentication-System nutzen. Plot Twist: Taten sie nicht. Wir hatten drei verschiedene Auth-Systeme (JWT, OAuth2 und ein Legacy-Session-basiertes System), und Notifications mussten mit allen funktionieren.

TypeScript
// RFC-Annahme
interface AuthContext {
  userId: string;
  token: string;
}

// Production-Realität
type AuthContext = 
  | { type: 'jwt'; userId: string; token: string; claims: JWTClaims }
  | { type: 'oauth2'; userId: string; accessToken: string; refreshToken: string; expiresAt: Date }
  | { type: 'legacy'; sessionId: string; userId?: string; cookieData: LegacyCookie }
  | { type: 'service_account'; serviceId: string; apiKey: string }
  | { type: 'anonymous'; temporaryId: string; ipAddress: string };

// Jeder Auth-Typ brauchte unterschiedliches Handling, unterschiedliche Rate Limits, 
// unterschiedliche Security-Checks und unterschiedliches Audit-Logging

Monate 7-8: Die Performance-Abrechnung#

Das System "funktionierte", konnte aber die Production-Last nicht bewältigen. Das RFC versprach 10.000 Notifications pro Sekunde. Wir kämpften mit 500. Die nächsten zwei Monate waren ein Verschwimmen von Profiling, Caching, Query-Optimization und architektonischen Änderungen.

Die größte Überraschung? Der Bottleneck war nicht dort, wo wir ihn erwartet hatten (Database-Writes). Es war Template-Rendering. Unser ausgefallenes Personalisierungs-System machte 20+ API-Calls pro Notification, um User-Context zu sammeln.

Team-Dynamik: Der menschliche Faktor#

Das RFC erwähnte "2 Developer für 12 Wochen." Wer tatsächlich daran arbeitete:

  • 2 Senior Engineers (sollten Vollzeit sein, tatsächlich 60% wegen Production-Support)
  • 1 Junior Engineer (Monat 2 hinzugefügt, verbrachte Monat 3 damit, die Codebase zu lernen)
  • 2 Contractors (Monat 4 für "schnelle Erfolge" hinzugefügt, verbrachten Monat 5 damit, ihren Code zu fixen)
  • 1 DevOps Engineer (angeblich "beratend", tatsächlich Vollzeit ab Monat 3)
  • 1 Database-Experte (Monat 5 für Performance-Krise geholt)
  • Product Manager (wechselte zweimal während des Projekts)
  • 3 verschiedene Engineering Manager (Reorg passierte in Monat 6)

Jeder Team-Wechsel bedeutete Context-Verlust, Architektur-Debatten und Nacharbeit. Der Contractor-Code sah im Code Review gut aus, schuf aber Technical Debt, die wir immer noch abbezahlen. Die Reorg in Monat 6 hätte das Projekt fast getötet, als der neue Engineering Manager die "Architektur überdenken" wollte.

Die Monitoring-Lücke, über die niemand spricht#

Das RFC hatte einen Abschnitt über Monitoring. Es listete Metriken wie Delivery Rate, Response Time und Error Rate auf. Vernünftig, oder? Was wir tatsächlich in Production monitoren mussten:

TypeScript
// RFC Monitoring-Plan
const plannedMetrics = [
  'delivery_rate',
  'response_time', 
  'error_rate',
  'throughput'
];

// Was wir tatsächlich monitoren
const productionMetrics = [
  // Basis-Metriken (vom RFC)
  'delivery_rate_by_channel_by_priority_by_user_segment',
  'response_time_p50_p95_p99_p999',
  'error_rate_by_type_by_service_by_retry_count',
  
  // Die Metriken, die wirklich wichtig sind
  'template_render_time_by_template_by_variables_count',
  'database_connection_pool_wait_time',
  'redis_operation_time_by_operation_type',
  'webhook_retry_backoff_effectiveness',
  'notification_staleness_at_delivery',
  'user_preference_cache_hit_rate',
  'deduplication_effectiveness_by_time_window',
  'rate_limit_rejection_by_reason',
  'circuit_breaker_state_transitions',
  'message_size_distribution_by_channel',
  'websocket_reconnection_storms',
  'push_token_invalidation_rate',
  'email_bounce_classification',
  'notification_feedback_loop_latency',
  'cost_per_notification_by_channel',
  'regulatory_compliance_audit_completeness',
  
  // Die seltsamen, die wir nach spezifischen Incidents brauchten
  'mobile_app_version_vs_notification_compatibility',
  'timezone_calculation_accuracy',
  'emoji_rendering_failures_by_client',
  'notification_delivery_during_database_failover',
  'memory_leak_in_template_cache',
  'thundering_herd_detection'
];

Jede dieser Metriken existiert, weil etwas schief ging, und wir es nicht kommen sahen, bis es zu spät war.

Technical Debt: Der Zinseszins der Abkürzungen#

Das RFC erwähnte keine Technical Debt. Bis Monat 8 hatten wir damit zu kämpfen:

Das Template-System-Frankenstein#

Wir starteten mit einem einfachen Template-System. In Production hatten wir drei verschiedene Template-Engines, die gleichzeitig liefen, weil verschiedene Teams unterschiedliche Anforderungen hatten, und wir nie Zeit hatten, sie zu vereinheitlichen.

TypeScript
// Die Technical Debt, die wir noch bezahlen
class NotificationTemplateManager {
  private mustacheTemplates: Map<string, MustacheTemplate>;    // Original-System
  private handlebarsTemplates: Map<string, HandlebarsTemplate>; // Für Marketing hinzugefügt
  private reactEmailTemplates: Map<string, ReactEmailTemplate>; // Für hübsche Emails hinzugefügt
  
  async render(templateId: string, data: any): Promise<string> {
    // 150 Zeilen Logic, um herauszufinden, welche Template-Engine zu nutzen ist,
    // Edge Cases zu handhaben, Backwards-Compatibility zu erhalten,
    // und Bugs zu umgehen, die wir nicht ohne Production-Bruch fixen können
    
    // Dieser Kommentar ist seit Monat 4 hier:
    // TODO: Template-Systeme vereinheitlichen (geschätzt: 2 Wochen)
    // Tatsächliche Schätzung nach Untersuchung: 3 Monate + Migrations-Plan
  }
}

Die nie endende Migration#

Erinnerst du dich an das schöne Database-Schema? Wir migrieren seit sechs Monaten zu "v2". Wir lassen beide Schemas parallel laufen, mit einem komplexen Sync-System, das gelegentlich Notifications verliert.

SQL
-- Der Migrations-Alptraum
BEGIN;
  -- Schritt 1 von 47 im Migrations-Plan
  INSERT INTO notification_events_v2 
  SELECT 
    id,
    user_id,
    -- 50 Zeilen komplexe Transformations-Logic
    CASE 
      WHEN notification_type IN ('old_type_1', 'old_type_2') THEN 'new_type_1'
      WHEN notification_type LIKE 'legacy_%' THEN REPLACE(notification_type, 'legacy_', 'classic_')
      -- 20 weitere WHEN-Klauseln
    END as notification_type_v2,
    -- Mehr Transformationen...
  FROM notification_events 
  WHERE created_at > NOW() - INTERVAL '1 hour'
    AND status != 'migrated'
    AND NOT EXISTS (
      SELECT 1 FROM notification_events_v2 
      WHERE notification_events_v2.id = notification_events.id
    );
  
  -- Update Migration-Status
  UPDATE migration_status 
  SET last_run = NOW(), 
      records_migrated = records_migrated + row_count,
      estimated_completion = NOW() + (remaining_records / current_rate * INTERVAL '1 second')
  WHERE migration_name = 'notification_schema_v2';
  
  -- Check für Konflikte
  -- Handle Rollback-Szenarien
  -- Update Monitoring-Metriken
  -- 100 weitere Zeilen...
COMMIT;

Die Success-Metriken, die nicht wichtig waren#

Das RFC definierte klare Erfolgskriterien: 99,9% Uptime, <100ms Delivery, 10.000 Notifications pro Sekunde. Wir erreichten schließlich einige dieser Zahlen, aber sie stellten sich als die falschen Metriken heraus.

Was tatsächlich wichtig war:

  • User-Zufriedenheit: Wir hatten 99% Delivery Rate, aber User hassten die Notifications, weil sie schlecht getimed waren
  • Developer-Produktivität: Andere Teams konnten nicht ohne umfangreiche Hilfe mit unserer "sauberen" API integrieren
  • Operationale Last: Das System brauchte trotz all unserer Automatisierung ständige Pflege
  • Business-Value: Marketing konnte die Hälfte der Features nicht nutzen, weil sie zu komplex waren
TypeScript
// Wofür wir optimiert haben (vom RFC)
const technicalMetrics = {
  uptime: 99.9,
  deliveryTime: 95, // ms
  throughput: 10000, // pro Sekunde
  errorRate: 0.1 // Prozent
};

// Was tatsächlich wichtig war
const businessMetrics = {
  userNotificationDisableRate: 45, // Prozent - viel zu hoch
  developerIntegrationTime: 3, // Wochen - sollten Stunden sein
  supportTicketsPerWeek: 150, // bezogen auf Notifications
  marketingCampaignSetupTime: 2, // Tage - sollten Minuten sein
  monthlyOperationalCost: 25000, // Dollar - 5x die Schätzung
  engineersPagedPerWeek: 12 // Mal - nicht nachhaltig
};

Auf die harte Tour gelernte Lektionen#

Nach der Implementierung von Notification-Systemen bei mehreren Unternehmen und dem Beobachten, wie RFCs mit der Realität kollidieren, habe ich gelernt:

1. RFCs sind Hypothesen, keine Spezifikationen#

Behandle dein RFC als Start-Hypothese. In dem Moment, wo es auf Production trifft, wird es ein lebendes Dokument, das ständige Überarbeitung braucht. Wir hielten unser RFC zu lange als "die Spec" eingefroren, was endlose Verwirrung verursachte, als die Realität abwich.

2. Budget für die unbekannten Unbekannten#

Welche Timeline und welches Budget du auch hast, verdopple es, dann füge 50% für die Dinge hinzu, von denen du nicht weißt, dass du sie nicht weißt. Das ist kein Pessimismus; es ist Mustererkennung aus Dutzenden von Projekten.

3. Design für Migration vom ersten Tag an#

Jedes schöne Schema wird Migration brauchen. Jede saubere API wird Versionierung brauchen. Jedes einfache System wird Backward-Compatibility brauchen. Baue diese Fähigkeiten von Anfang an ein, nicht als nachträgliche Gedanken.

4. Die Edge Cases sind die Norm#

Dieser Edge Case, den du im RFC-Review diskutierst? Der, bei dem alle sagen "wir kümmern uns darum, wenn er auftaucht"? Er wird auftauchen, wahrscheinlich in Production, wahrscheinlich zur schlimmstmöglichen Zeit. Wenn du darüber diskutierst, ist es kein Edge Case.

5. Organisatorische Dynamik schlägt technische Exzellenz#

Das beste technische Design wird scheitern, wenn es Team-Dynamik, politische Realitäten und organisatorische Einschränkungen nicht berücksichtigt. Der Contractor, der in Monat 3 dazukommt, kümmert sich nicht um deine schöne Architektur. Die Reorg in Monat 6 wird jede Entscheidung herausfordern.

6. Monitore, was du tatsächlich debuggen wirst#

Monitore nicht, was das RFC sagt. Monitore, was du während eines Incidents brauchst, wenn alles brennt. Das bedeutet Business-Metriken, User-Experience-Metriken und detaillierte operative Metriken, nicht nur technische Stats.

Der Weg nach vorne: Design und Realität überbrücken#

Wie überbrücken wir also die Lücke zwischen RFC und Production? Nach Jahren des Ausprobierens verschiedener Ansätze funktioniert das tatsächlich:

Starte mit einem Minimum Lovable Product#

Nicht Minimum Viable – Minimum Lovable. Baue etwas Kleines, das User tatsächlich nutzen wollen, dann iteriere. Unser Notification-System wäre besser gewesen, wenn wir nur mit perfekt funktionierenden Email-Notifications gestartet hätten, statt zu versuchen, alles auf einmal zu bauen.

Design für Veränderung, nicht Perfektion#

Dein Database-Schema wird sich ändern. Deine API wird sich entwickeln. Deine Architektur wird sich verschieben. Designe Systeme, die sich elegant entwickeln können, statt zu versuchen, den perfekten Endzustand vorherzusagen.

Investiere früh in Developer Experience#

Je einfacher dein System zu integrieren und zu betreiben ist, desto erfolgreicher wird es sein. Wir verbrachten Monate damit, Performance zu optimieren, als wir die API einfacher zu nutzen machen sollten.

Erstelle lebende Dokumentation#

Dieses RFC sollte kein historisches Artefakt sein. Es sollte sich mit dem System entwickeln. Wir pflegen unsere RFCs jetzt als lebende Dokumente, mit Abschnitten für "Original Design", "Aktuelle Implementation" und "Gelernte Lektionen".

Baue Feedback-Loops auf jeder Ebene#

Von User-Feedback über operative Metriken bis zu Developer-Experience-Umfragen, baue Feedback-Loops in deinen Prozess ein. Je schneller du lernen kannst, was nicht funktioniert, desto schneller kannst du es fixen.

Fazit: Das Chaos umarmen#

Nach 20 Jahren des System-Bauens habe ich gelernt, das Chaos zu umarmen. Dieses makellose RFC wird chaotisch werden. Deine schöne Architektur wird Warzen bekommen. Deine saubere Codebase wird Technical Debt ansammeln. Das ist kein Versagen – es ist die natürliche Evolution von Systemen, die echte Probleme für echte User lösen.

Die Lücke zwischen RFC und Production ist nichts, was eliminiert werden muss; es ist etwas, das gemanagt werden muss. Die besten Engineers sind nicht die, die perfekte RFCs schreiben, die jedes Detail vorhersagen. Es sind die, die sich anpassen können, wenn die Realität unvermeidlich vom Plan abweicht.

Wenn ich auf unser Notification-System zurückblicke, ist es nicht das, was wir im RFC designed haben. Es ist chaotischer, komplexer und brauchte dreimal länger zum Bauen. Aber es ist auch fähiger, widerstandsfähiger und löst Probleme, von deren Existenz wir nicht einmal wussten, als wir das RFC schrieben.

Das nächste Mal, wenn du ein RFC schreibst, denk daran: Du schreibst keine Spezifikation, du startest eine Konversation mit der Realität. Und die Realität hat immer das letzte Wort.

Hast du die RFC-zu-Production-Lücke erlebt? Welche Lektionen hast du gelernt, als du beobachtet hast, wie schöne Designs auf chaotische Realität treffen? Ich würde gerne deine War Stories hören und was du beim nächsten Mal anders machen würdest.

Loading...

Kommentare (0)

An der Unterhaltung teilnehmen

Melde dich an, um deine Gedanken zu teilen und mit der Community zu interagieren

Noch keine Kommentare

Sei der erste, der deine Gedanken zu diesem Beitrag teilt!

Related Posts