
Jedna codebase, dvě aplikace: jak na white-label bez forku
GeekBye a Pavleur jsou dvě desktopové aplikace s odlišným brandem, postavené z jediného repozitáře — žádný fork, žádná duplicitní codebase. Tady je mašinerie fungující v čase buildu, díky které se jedna codebase zkompiluje do dvou produktů, a jednořádková chyba, kvůli které se naše druhá aplikace představovala špatným jménem.
GeekBye je zapisovatel schůzek. Pavleur je AI aplikace na trénink pohovorů. Vypadají jako dva různé produkty, protože to jsou dva různé produkty — ale staví se z jedné codebase, bez forku a bez větve, která se rozjíždí. Tohle je inženýrská strana white-labelingu: ne „proč prodávat brandovanou aplikaci" (to je jiný článek), ale jak vlastně zkompilujete jeden repozitář do dvou aplikací — a ta neatraktivní chyba, která se skrývá v každém white-label systému, dokud ji nezačnete aktivně hledat.
Generujte, nevětvěte
Špatný způsob, jak provozovat dva produkty z jedné codebase, je rozesít po aplikaci if (product === 'pavleur'), nebo udělat fork a modlit se, aby se ty dvě kopie nikdy nerozešly. Obojí rychle hnije.
Přístup GeekBye je generování kódu v čase buildu. Každý produkt má konfigurační soubor — configs/geekbye/config.ts, configs/pavleur/config.ts — s jeho identitou: název, bundle id, URL protokol, repozitář vydání pro automatické aktualizace a tak dále. Před buildem se spustí jeden script:
node scripts/build-product.js pavleur
Ten script přečte konfiguraci zvoleného produktu a udělá tři konkrétní věci:
- Vygeneruje dva konfigurační moduly — jeden pro renderer (
src/config/product.ts), jeden pro main proces (electron/config/product.ts) — každý orazítkovaný hlavičkou „NEUPRAVOVAT RUČNĚ — změny budou přepsány". Zbytek aplikace si prostě importujeproductName,applicationIdaprotocolz konfigurace a nikdy nepřemýšlí, kterým brandem je. - Zkopíruje jen assety daného brandu — ikony, malé logo, ikonu toast notifikací. Build GeekBye fyzicky neobsahuje ani pixel grafiky Pavleuru a naopak. Není co uniknout.
- Přepíše
package.json(u release buildů) — app id, název produktu, URL protokol, GitHub repozitář vydání, kam se publikuje, a — což potěší — brandovaný text žádosti o přístup k mikrofonu, takže se macOS zeptá „Pavleur žádá o přístup k mikrofonu", ne špatný brand.
Celá aplikace je brand-agnostická; brand se injektuje v čase buildu. Neexistuje žádný runtime přepínač produktů, který by šlo pokazit, protože za běhu je vždycky zapečený jen jeden produkt.
Jeden backend, dva brandy
Obě aplikace mluví se stejným backendem. Jak tedy server pozná, ke kterému produktu — a ke kterým cenám, promptům a limitům — požadavek patří?
Dva signály, oba nastavené v čase buildu. Zaprvé application id (geekbye nebo pavleur) zapečené v konfiguraci a posílané s požadavky, aby backend uměl směrovat na chování správného produktu. Zadruhé User-Agent, který klient připojuje ke každému volání backendu, ve tvaru Product/version (platform) — např. Pavleur/1.2.5 (win32) nebo GeekBye/1.7.3 (darwin). Tahle jedna hlavička dává backendu atribuci podle produktu i podle operačního systému zadarmo, bez samostatného analytického pole. Automatické aktualizace mezitím zůstávají izolované: každý produkt publikuje do vlastního repozitáře vydání, takže aktualizace GeekBye nikdy nemůže být naservírována instalaci Pavleuru.
Onboarding se také liší podle produktu — průvodce GeekBye je o přepisu schůzek, průvodce Pavleuru o tréninku pohovorů — a ten správný se vybírá za běhu podle jediného zapečeného application id. Stejná mašinerie, dvoje vstupní dveře.
Ta chyba: když si Pavleur říkal GeekBye
Tady je historka z fronty — a je to ta, jejíž verzi má každý white-label systém.
Generování konfigurace fungovalo. Ikony se přepínaly. Dotaz na mikrofon byl brandovaný. Protokoly i bundle id seděly. Podle všech viditelných měřítek build Pavleuru byl Pavleur. A přesto se po nějakou dobu každý build Pavleuru hlásil backendu pod jménem GeekBye.
Viníkem byl jediný natvrdo zapsaný string a schovával se na tom nejméně okouzlujícím místě, jaké si dovedete představit: v sestavovači User-Agentu, zastrčeném uvnitř síťového retry pomocníka. Všechno, co viděl uživatel, procházelo konfigurací, ale tahle jedna hluboko zahrabaná instalatérská utilita v sobě pořád měla literál `GeekBye/${version}`. Nikoho nenapadne přemýšlet o retry utilitě jako o „brandingu", takže ji nikdo nezkontroloval. Oprava měla čtyři řádky — naimportovat dynamický productName z konfigurace a interpolovat ho místo natvrdo zapsaného brandu — ale poučení je celá pointa:
White-labeling není „změnilo se logo". Je to „prochází každý identitní string konfigurací". A poslední opozdilci nikdy nežijí v UI, kde byste hledali. Žijí v User-Agentu, v obsluze deep-link protokolu, v textu žádosti o oprávnění, v chybových hláškách — v instalatérské vrstvě, kterou nikdo neřadí pod „brand". Jediná spolehlivá obrana je zacházet s brandovým literálem jako s něčím, co aktivně lovíte: grepněte codebase na natvrdo zapsané jméno a shoďte build, pokud se objeví kdekoli mimo konfiguraci, která ho má definovat.
Tři věci, které nás white-labeling naučil
- Generujte, nevětvěte. Codegen dvou drobných konfiguračních modulů v čase buildu plus selektivní kopírování assetů poráží jak fork, tak runtime houštinu produktových kontrol. Aplikace zůstává brand-agnostická; brand je vstup buildu.
- Identitní stringy prosakují do instalatérské vrstvy. UI je snadných 90 %. Těžkých 10 % je User-Agent, URL protokol, string žádosti o mikrofon, texty chyb. Protáhněte je všechny jedním exportem
productName/applicationId— a grepujte na syrové jméno brandu jako CI bránu, aby zbloudilý literál nikdy nemohl doputovat k uživatelům. - Jeden backend, mnoho brandů, rozlišujte pomocí id a hlavičky. Application id z času buildu plus User-Agent ve tvaru
Product/version (platform)dává atribuci podle produktu i podle platformy s nulovou duplicitní infrastrukturou — a čistě odděluje neměnnou identitu z času buildu od chování za běhu.
Produktovou a soukromí se týkající stránku provozování white-label zapisovatele pod vlastním brandem najdete v white-label AI zapisovateli. Tohle je šestá kapitola příběhu v1, který se stane GeekBye v2 — předchozí kapitolu najdete v co ve skutečnosti znamená vydání se 127 commity (v1.7.0); a celý oblouk v anatomii dodávání softwaru k dokonalosti.