Two sites under one company — who publishes what? Publishing the same article on both, building the docs on both, looks like "fuller coverage" but is really self-competition and double maintenance. Rebuilding our website, we treated this as an architecture problem and settled on four rule groups.
Rule one: one home per kind of content
Every content type has exactly one home:
| Content type | Sole home | What the other site does |
|---|---|---|
| Product documentation (manual) | Product site | Parent site has no entry page; one click leads there |
| Pricing, plans, subscription config | Product site | Parent site keeps a "see plans and pricing" button |
| Feature and module explanations | Product site (app pages) | Parent site summarises on a "product family" page |
| Company news, engineering practice | Parent site | Product site's blog covers product and industry topics |
| Legal (privacy, terms) | Both, separately | Each site serves its own forms and accounts; the terms differ |
The cost of duplication is not just maintaining everything twice: when two domains compete for the same pages, search engines will "pick one" for you — often not the one you wanted.
Rule two: if it does not exist, do not fake it
When the English version is not written, we do not publish a machine translation or an empty shell. The rule must hold at three consistent layers — missing any one leaks:
- List layer: Chinese-only articles do not appear in the English list;
- Detail layer: visiting that article in English returns a 404 — not a Chinese article wrapped in an English shell;
- Index layer:
hreflangdeclares only languages that actually exist; Chinese-only articles declarezh-CN + x-defaultonly — never pointing search engines at a 404.
There is a useful side effect: when the English version arrives later, nothing needs flipping — the body's existence is the switch, and hreflang, listings and the sitemap all derive from that single fact.
Rule three: every cross-site link carries attribution
All parent-site-to-product-site links go through one funnel registry (funnel.ts in the code): destination, utm parameters and placement declared in one place, with the type system allowing only registered placements:
// Placements are an enum; URLs are assembled in exactly one function
export const HZ_PLACEMENTS = ['nav-hongzhai', 'home-hero', 'footer-product', ...] as const
export function hongzhaiUrl(placement: HzPlacement, path = '/', locale: AppLocale = 'zh-CN') {
// utm_source / utm_medium / utm_campaign / utm_content assembled here
}
Two tests back it up: every placement's utm_content must be unique, and pages may not contain hard-coded counterpart domains. The result: one edit applies everywhere, and every entry point's traffic is attributable — after launch, utm_content in analytics answers exactly which button converted.
Rule four: every hop must land
A funnel button that 404s is the most damaging experience of all. We verified cross-site links in both directions: targets actually exist (sampled HTTP checks), paths carry the right locale prefix and any required query strings. Legacy handling is the same discipline: all 64 old URLs got an itemised disposition — renames as 301s, product terms across to the product site, template leftovers as 410s — in one edge-layer map file, versioned with the code, so rule changes are not releases.
Takeaways
The keyword for a two-site architecture is boundaries: one home per content type, honest language declarations, attributable funnels, and a definite landing for every hop. With clear boundaries, the two sites do not "publish twice" — they vouch for each other: the parent site evidences the engineering behind the products; the product site evidences the delivery behind the services.
