
Jedna kódová základňa, dve aplikácie: ako spraviť white-label bez forku
GeekBye a Pavleur sú dve odlišne brandované desktopové aplikácie zostavené z jedného repozitára — bez forku, bez zdvojenej kódovej základne. Toto je build-time mašinéria, vďaka ktorej sa jedna kódová základňa skompiluje do dvoch produktov, a jednoriadková chyba, kvôli ktorej sa naša druhá aplikácia predstavovala pod nesprávnym menom.
GeekBye je zapisovač poznámok z porád. Pavleur je AI aplikácia na precvičovanie pracovných pohovorov. Vyzerajú ako dva rozdielne produkty, pretože nimi sú — ale sú postavené z jednej kódovej základne, bez forku a bez vetvy, ktorá by sa rozchádzala. Toto je inžinierska stránka white-labelingu: nie „prečo predávať brandovanú aplikáciu" (to je iný článok), ale ako vlastne skompilovať jeden repozitár do dvoch aplikácií — a tá neefektná chyba, ktorá sa skrýva v každom white-label systéme, kým ju nezačneš hľadať.
Generuj, neforkuj
Nesprávny spôsob, ako z jednej kódovej základne prevádzkovať dva produkty, je rozsypať if (product === 'pavleur') po celej aplikácii alebo forknúť a dúfať, že sa dve kópie nikdy nerozídu. Oboje rýchlo hnije.
Prístup GeekBye je build-time generovanie kódu. Každý produkt má konfiguračný súbor — configs/geekbye/config.ts, configs/pavleur/config.ts — ktorý drží jeho identitu: názov, bundle id, URL protokol, repozitár vydaní pre automatické aktualizácie a tak ďalej. Pred buildom sa spustí jeden skript:
node scripts/build-product.js pavleur
Ten skript prečíta config zvoleného produktu a spraví tri konkrétne veci:
- Vygeneruje dva konfiguračné moduly — jeden pre renderer (
src/config/product.ts), jeden pre hlavný proces (electron/config/product.ts) — každý opečiatkovaný hlavičkou „DO NOT EDIT MANUALLY — changes will be overwritten". Zvyšok aplikácie len importujeproductName,applicationIdaprotocolz configu a nikdy nerieši, ktorá značka to je. - Skopíruje iba assety danej značky — ikony, malé logo, ikonu toast notifikácie. Build GeekBye fyzicky obsahuje nula grafiky Pavleuru a naopak. Niet čo vyzradiť.
- Prepíše
package.json(pri release buildoch) — app id, názov produktu, URL protokol, GitHub repozitár vydaní, kam sa publikuje, a — pekne — brandovaný text povolenia mikrofónu, takže sa macOS pýta „Pavleur by chcel pristupovať k mikrofónu," a nie pod nesprávnou značkou.
Celá aplikácia je značkovo agnostická; značka sa vstrekuje počas buildu. Neexistuje žiadny runtime prepínač produktu, ktorý by sa dal pokaziť, pretože počas behu je vždy zapečený len jeden produkt.
Jeden backend, dve značky
Obe aplikácie komunikujú s rovnakým backendom. Ako teda server vie, ktorému produktu — a ktorému cenníku, promptom a limitom — požiadavka patrí?
Dva signály, oba nastavené počas buildu. Prvý, aplikačné id (geekbye alebo pavleur) zapečené v configu a posielané s požiadavkami, aby backend vedel smerovať na správne správanie daného produktu. Druhý, User-Agent, ktorý klient pripája ku každému volaniu backendu, v tvare Product/version (platform) — napr. Pavleur/1.2.5 (win32) alebo GeekBye/1.7.3 (darwin). Táto jedna hlavička dáva backendu atribúciu podľa produktu aj podľa OS zadarmo, bez samostatného analytického poľa. Automatické aktualizácie zostávajú medzitým izolované: každý produkt publikuje do vlastného repozitára vydaní, takže aktualizácia GeekBye nemôže byť nikdy doručená na inštaláciu Pavleuru.
Onboarding sa tiež líši podľa produktu — sprievodca GeekBye je o prepise porád, sprievodca Pavleuru o precvičovaní pohovorov — a ten správny sa vyberá počas behu z jediného zapečeného aplikačného id. Rovnaká mašinéria, dvoje vchodové dvere.
Chyba: keď sa Pavleur nazýval GeekBye
Tu je vojnová historka, a je to tá, ktorej verziu má každý white-label systém.
Generovanie configu fungovalo. Ikony sa vymenili. Výzva na mikrofón bola brandovaná. Protokoly a bundle id boli správne. Podľa každého viditeľného meradla build Pavleuru bol Pavleur. A predsa sa istý čas každý build Pavleuru hlásil backendu ako GeekBye.
Vinníkom bol jediný natvrdo zakódovaný reťazec a skrýval sa na najmenej efektnom mieste, aké si viete predstaviť: v builderi User-Agentu, zastrčenom vnútri sieťového retry pomocníka. Všetko, čo bolo obrátené k používateľovi, prechádzalo cez config, ale táto jedna hlbinná inštalatérska utilita stále obsahovala literál `GeekBye/${version}`. Nikto nepovažuje retry utilitu za „branding," takže ju nikto neskontroloval. Oprava mala štyri riadky — naimportovať dynamické productName z configu a interpolovať ho namiesto natvrdo zakódovanej značky — no ponaučenie je práve celou pointou:
White-labeling nie je „zmenilo sa logo." Je to „prechádza každý identifikačný reťazec cez config." A posledné zvyšky nikdy nežijú v UI, kde by si pozeral. Žijú v User-Agente, v handleri deep-link protokolu, v texte výzvy na povolenie, v chybových hláškach — v inštalatérstve, ktoré si nikto neuloží pod „značka." Jedinou spoľahlivou obranou je brať literál značky ako niečo, čo aktívne loví: grepni kódovú základňu na natvrdo zakódovaný názov a zhoď build, ak sa objaví kdekoľvek mimo configu, ktorý ho má definovať.
Tri veci, ktoré nás white-labeling naučil
- Generuj, neforkuj. Build-time codegen dvoch drobných konfiguračných modulov plus selektívne kopírovanie assetov poráža aj forkovanie, aj runtime húštinu produktových kontrol. Aplikácia zostáva značkovo agnostická; značka je vstupom buildu.
- Identifikačné reťazce presakujú do inštalatérstva. UI je ľahkých 90 %. Ťažkých 10 % je User-Agent, URL protokol, reťazec povolenia mikrofónu, chybové texty. Prežeň ich všetky cez jeden export
productName/applicationId— a grepni surový názov značky ako CI bránu, aby zabudnutý literál nikdy nemohol vyjsť. - Jeden backend, veľa značiek, rozlíš podľa id a hlavičky. Build-time aplikačné id plus User-Agent
Product/version (platform)dáva atribúciu podľa produktu aj podľa platformy s nulovou zdvojenou infraštruktúrou — čisto oddeľuje nemennú build-time identitu od runtime správania.
Pre produktovú a súkromnostnú stránku prevádzkovania white-label zapisovača pod vlastnou značkou pozri white-label AI zapisovač. Toto je šiesta kapitola príbehu v1, ktorý sa stáva GeekBye v2 — pre predchádzajúcu kapitolu, čím naozaj je vydanie zo 127 commitov (v1.7.0); a pre celý oblúk, anatómiu dodávania softvéru do dokonalosti.