
Yksi koodipohja, kaksi sovellusta: white-label ilman forkkausta
GeekBye ja Pavleur ovat kaksi eri tavalla brändättyä työpöytäsovellusta, jotka rakennetaan yhdestä repositoriosta — ei forkkia, ei duplikoitua koodipohjaa. Tässä on käännösaikainen koneisto, jolla yksi koodipohja kääntyy kahdeksi tuotteeksi, ja yhden rivin bugi, jonka takia toinen sovelluksemme esittäytyi väärällä nimellä.
GeekBye on kokousmuistiinpanosovellus. Pavleur on tekoälypohjainen haastatteluharjoittelusovellus. Ne näyttävät kahdelta eri tuotteelta, koska ne ovat kaksi eri tuotetta — mutta ne rakennetaan yhdestä koodipohjasta, ilman forkkia ja ilman haaraa, joka ajautuu erilleen. Tämä on white-labelin insinööripuoli: ei "miksi myydä brändättyä sovellusta" (se on eri kirjoitus), vaan miten yksi repositorio oikeasti käännetään kahdeksi sovellukseksi — ja se epäglamouri bugi, joka piileskelee jokaisessa white-label-järjestelmässä, kunnes sitä lähtee etsimään.
Generoi, älä haaraudu
Väärä tapa pyörittää kahta tuotetta yhdestä koodipohjasta on ripotella if (product === 'pavleur') ympäri sovellusta, tai forkata ja rukoilla, etteivät kopiot koskaan ajaudu erilleen. Molemmat mätänevät nopeasti.
GeekByen lähestymistapa on käännösaikainen koodigenerointi. Jokaisella tuotteella on konfiguraatiotiedosto — configs/geekbye/config.ts, configs/pavleur/config.ts — joka sisältää sen identiteetin: nimen, bundle id:n, URL-protokollan, automaattipäivitysten release-repositorion ja niin edelleen. Ennen buildia ajetaan yksi skripti:
node scripts/build-product.js pavleur
Skripti lukee valitun tuotteen konfiguraation ja tekee kolme konkreettista asiaa:
- Generoi kaksi konfiguraatiomoduulia — yhden renderöijälle (
src/config/product.ts), yhden pääprosessille (electron/config/product.ts) — kumpikin leimattuna "DO NOT EDIT MANUALLY — changes will be overwritten" -otsakkeella. Muu sovellus vain importtaa konfistaproductName:n,applicationId:n japrotocol:n eikä koskaan mieti, mikä brändi se on. - Kopioi vain sen brändin assetit — ikonit, pienen logon, ilmoitusikonin. GeekBye-build ei fyysisesti sisällä yhtäkään Pavleur-grafiikkaa ja päinvastoin. Ei mitään vuodettavaa.
- Uudelleenkirjoittaa
package.json:in (release-buildeissa) — sovelluksen id:n, tuotenimen, URL-protokollan, GitHub-release-repon johon julkaistaan, ja kivana yksityiskohtana brändätyn mikrofonilupatekstin, jotta macOS kysyy "Pavleur haluaa käyttää mikrofonia" eikä väärällä brändillä.
Koko sovellus on brändiagnostinen; brändi injektoidaan käännösaikana. Ei ole ajonaikaista tuotekytkintä, jonka voisi mokata, koska ajon aikana sisään on leivottu aina vain yksi tuote.
Yksi backend, kaksi brändiä
Molemmat sovellukset puhuvat samalle backendille. Mistä palvelin siis tietää, mille tuotteelle — ja mille hinnoittelulle, prompteille ja rajoille — pyyntö kuuluu?
Kaksi signaalia, molemmat asetettu käännösaikana. Ensin application id (geekbye tai pavleur), leivottu konfiin ja lähetetty pyyntöjen mukana, jotta backend osaa reitittää oikean tuotteen käyttäytymiseen. Toiseksi User-Agent, jonka client liittää jokaiseen backend-kutsuun, muotoa Product/version (platform) — esim. Pavleur/1.2.5 (win32) tai GeekBye/1.7.3 (darwin). Tuo yksi otsake antaa backendille tuote- ja käyttöjärjestelmäkohtaisen attribuution ilmaiseksi, ilman erillistä analytiikkakenttää. Automaattipäivitykset pysyvät sillä välin eristettyinä: kumpikin tuote julkaisee omaan release-repositorioonsa, joten GeekByen päivitystä ei voida koskaan tarjoilla Pavleur-asennukselle.
Myös onboarding eroaa tuotteittain — GeekByen ohjattu aloitus käsittelee kokousten transkriptiota, Pavleurin haastatteluharjoittelua — ja oikea valitaan ajon aikana siitä yhdestä sisäänleivotusta application id:stä. Sama koneisto, kaksi etuovea.
Bugi: kun Pavleur kutsui itseään GeekByeksi
Tässä on sotatarina, ja se on juuri se, josta jokaisella white-label-järjestelmällä on oma versionsa.
Konfigenerointi toimi. Ikonit vaihtuivat. Mikrofonikysely oli brändätty. Protokollat ja bundle id:t olivat oikein. Kaikilla näkyvillä mittareilla Pavleur-build oli Pavleur. Ja silti jonkin aikaa jokainen Pavleur-build soitti kotiin backendille kutsuen itseään GeekByeksi.
Syyllinen oli yksi ainoa kovakoodattu merkkijono, ja se piileskeli kaikkein epäglamourimmassa mahdollisessa paikassa: User-Agent-rakentajassa, tungettuna verkon retry-apurin sisään. Kaikki käyttäjälle näkyvä oli reititetty konfin kautta, mutta tässä yhdessä syvän putkiston apuvälineessä oli yhä literaali `GeekBye/${version}`. Kukaan ei ajattele retry-utiliteettia "brändäyksenä", joten kukaan ei ollut tarkistanut sitä. Korjaus oli neljä riviä — importtaa dynaaminen productName konfista ja interpoloida se kovakoodatun brändin tilalle — mutta opetus on koko pointti:
White-label ei ole "vaihtuiko logo". Se on "kulkeeko jokainen identiteettimerkkijono konfin kautta." Ja viimeiset sinnittelijät eivät koskaan asu UI:ssa, josta katsoisit. Ne asuvat User-Agentissa, deep-link-protokollan käsittelijässä, lupakyselyjen teksteissä, virheilmoituksissa — putkistossa, jota kukaan ei arkistoi "brändin" alle. Ainoa luotettava puolustus on kohdella brändiliteraalia jonain, jota aktiivisesti jahtaa: greppaa koodipohjasta kovakoodattu nimi ja kaada build, jos se ilmestyy mihin tahansa sen konfin ulkopuolelle, jonka on tarkoituskin määritellä se.
Kolme asiaa, jotka white-label opetti meille
- Generoi, älä haaraudu. Kahden pikkuruisen konfimoduulin käännösaikainen generointi plus valikoiva assettien kopiointi voittaa sekä forkkauksen että ajonaikaisen tuotetarkistusten ryteikön. Sovellus pysyy brändiagnostisena; brändi on buildin syöte.
- Identiteettimerkkijonot vuotavat putkistoon. UI on helppo 90 %. Vaikea 10 % on User-Agent, URL-protokolla, mikrofonilupateksti, virhetekstit. Reititä ne kaikki yhden
productName- /applicationId-exportin kautta — ja greppaa raakaa brändinimeä CI-porttina, jottei eksynyt literaali voi koskaan päätyä julkaisuun. - Yksi backend, monta brändiä, erottele id:llä ja otsakkeella. Käännösaikainen application id plus
Product/version (platform)-muotoinen User-Agent antaa tuote- ja alustakohtaisen attribuution ilman riviäkään duplikoitua infrastruktuuria — erottaen siististi muuttumattoman käännösaikaisen identiteetin ajonaikaisesta käyttäytymisestä.
Tuote- ja yksityisyyspuolesta — white-label-muistiinpanosovelluksen pyörittämisestä oman brändisi alla — katso white-label-tekoälymuistiinpanija. Tämä on kuudes luku v1-tarinassa, josta tulee GeekBye v2 — edelliselle luvulle, mitä 127 commitin julkaisu oikeasti on (v1.7.0); ja koko kaarelle, ohjelmiston toimittamisen anatomia täydellisyyteen asti.