Skip to content
⚠️ This article was written in 2018. Some content may be outdated.

H1 2018: Building the Engineering System and Deep-Diving into Vue

The core work in the first half of 2018 revolved around three things: upgrading the architecture of back-office systems, building an internal component library from scratch, and improving the engineering pipeline. This is a systematic retrospective on the reasoning behind our technology choices and the real pitfalls we ran into.

H1 Key Project Review ​

Back-office System: Webpack 3 → 4 Migration + Incremental TypeScript Adoption ​

At the start of the year I took over a two-year-old back-office system running Webpack 3 + Vue 2.3, with a stable build time of around 45 seconds. I faced two choices: patch it and keep going, or do a thorough engineering upgrade. Considering the team would need to maintain multiple back-office projects in parallel, I chose the latter.

The core win from upgrading to Webpack 4 wasn't the version number itself — it was the mode mechanism and improved Tree Shaking. The biggest hurdle during the migration was plugin-ecosystem compatibility: several Webpack 3 plugins hadn't caught up to 4.x yet, requiring replacements or custom adapters. Build time ultimately dropped from 45s to 12s, driven mainly by two changes:

js
// webpack.config.js key config
module.exports = {
  mode: "production",
  optimization: {
    splitChunks: {
      chunks: "all",
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: "vendors",
          chunks: "all",
        },
      },
    },
  },
};

Adding HardSourceWebpackPlugin for persistent caching and route lazy-loading made the hot-reload experience in development much better.

Incremental TypeScript migration was a longer-term investment. The legacy code was full of any types — there was no way to fix everything at once. The strategy: enforce TS on new modules, gradually cover old modules via allowJs. By end of June, core module coverage was around 60%. The biggest takeaway from this process was realizing the type system isn't a "constraint" — it's a compile-time contract and form of documentation, especially valuable when several people collaborate, since type definitions serve as the best interface spec.

Component Library Construction ​

The first half of the year saw us kick off an internal shared component library. The goal wasn't to build a new UI framework, but to wrap common business-layer components on top of Element UI: permission buttons, data dictionary selectors, complex search forms, general CRUD tables, etc.

Several key design decisions were made:

  • Props-driven, non-invasive to business logic: components only care about UI and data flow; business logic is delegated out via slots and events
  • TypeScript type exports: each component also exports type definitions so consumers get full type hints
  • Tree-shakeable imports: paired with babel-plugin-component to avoid full bundle imports
ts
// Component type export example
export interface CrudTableProps {
  columns: ColumnConfig[];
  fetchApi: (params: QueryParams) => Promise<PaginatedResult<any>>;
  rowKey?: string;
  toolbar?: ToolbarConfig[];
}

By end of June, the component library covered 70% of common use cases across 3 back-office projects, cutting new-project setup time by about 40%.

Engineering Improvements ​

CI Integration ​

Previously the team deployed by manually running npm run build and then scp-ing to the server — very risky. In the first half of the year we integrated GitLab CI, with a pipeline covering lint → test → build → deploy four stages. Key config:

yaml
# .gitlab-ci.yml core pipeline
stages:
  - lint
  - test
  - build
  - deploy

lint:
  stage: lint
  script:
    - npm run lint
    - npm run type-check

build:
  stage: build
  script:
    - npm run build
  artifacts:
    paths:
      - dist/

The deploy step sends DingTalk notifications — failed builds automatically @-mention the relevant developers. Tracing production incidents shifted from "who deployed it?" to "which MR introduced it?".

Code Quality Toolchain ​

Rolling out ESLint + Prettier took longer than expected — the real friction wasn't the rules themselves, but the cost of reformatting the existing code. The final strategy: enable lint-staged to only check changed files; handle legacy code with a one-off bulk-format PR. Pairing with commitlint to standardize commit messages noticeably improved code review efficiency.

Pitfalls We Hit ​

1. TypeScript and Vue Compatibility Issues

Vue 2.x's TypeScript support isn't perfect. vue-property-decorator's decorator syntax loses type inference in some scenarios, especially with mixins and provide/inject. The solution: replace mixins with composition-style patterns where possible, and explicitly declare types for provide/inject.

2. Webpack 4 sideEffects Configuration

Debugging why Tree Shaking wasn't working took half a day. The root cause: sideEffects: false wasn't declared in package.json. Adding it reduced bundle size by another 15%. The lesson: upgrading toolchain means you can't just change config files — you also need to check the package.json of your dependencies.

3. Nuxt.js SSR Pitfalls

When we used Nuxt to handle SEO for a content site, window is not defined kept appearing. The root cause was third-party libraries accessing browser APIs at the module's top level. The solution: use process.client guards, or dynamically require them in the mounted hook. This taught me: SSR isn't just swapping frameworks — your entire dependency chain needs to be adapted.

H2 Plans ​

Technical Depth:

  • TypeScript advanced type system: generic constraints, conditional types, template literal types — put them into practice in the component library
  • Track React ecosystem: React 16.7 Hooks are worth diving into; planning to validate with an internal project
  • Frontend testing: currently only scattered unit tests; goal is to build automated testing for the component library + CI integration

Engineering System:

  • Integrate frontend monitoring with Sentry, covering error capture and performance metric reporting
  • Micro-frontend research: as back-office systems multiply, evaluate qiankun / single-spa feasibility
  • Dockerize build environment to unify the team's Node version and build dependencies

Technical Influence:

  • Give 2–3 internal tech talks (Webpack optimization, TypeScript practices)
  • Build component library documentation site to lower the onboarding cost for new members

Summary ​

  • Completed Webpack 4 upgrade and incremental TypeScript migration for the back-office system; build efficiency improved 73%
  • Launched the business component library, covering common use cases across 3 projects
  • Rolled out GitLab CI pipeline and code quality toolchain; said goodbye to manual deployments
  • Solved real problems: Vue + TypeScript compatibility, Webpack Tree Shaking, Nuxt SSR, and more
  • H2 focus: React validation, testing system, monitoring system, micro-frontend research

MIT Licensed