본문으로 건너뛰기

React 배후의 도구화 체계

무료2018-01-28#Tool#React工具链#React engineering architecture#前端工程化#前端工具链#前端工作流最佳实践

React 팀의 프런트엔드 도구 체계에서의 탐구

一.개관

React 도구 체인 태그 클라우드:

Rollup    Prettier    Closure Compiler
Yarn workspace    [x]Haste    [x]Gulp/Grunt+Browserify
ES Module    [x]CommonJS Module
Flow    Jest    ES Lint    React DevTools
Error Code System    HUBOT(GitHub Bot)    npm

P.S. [x] 가 붙은 것은 이전에 사용했지만, 최근 (React 16) 사용하지 않게 된 것

간단한 분류는 다음과 같다:

개발: ES Module, Flow, ES Lint, Prettier, Yarn workspace, HUBOT
구축: Rollup, Closure Compiler, Error Code System, React DevTools
테스트: Jest, Prettier
릴리스: npm

ES 모듈 메커니즘으로 소스 코드를 조직하고, 타입 체크와 Lint/포맷 도구를 보조로 하며, Yarn 으로 모듈 의존을 처리하고, HUBOT 이 PR 을 체크; Rollup + Closure Compiler 로 구축하고, Error Code 메커니즘으로 프로덕션 환경 오류 추적을 실현하고, DevTools 가 번들 체크를 측면에서 보조; Jest 가 유닛 테스트를 구동하고, 포맷된 번들로 구축 결과가 충분히 클린한지 확인; 마지막으로 npm 으로 새로운 package 를 릴리스

프로세스 전체는 그다지 복잡하지 않지만, 몇 가지 세부 사항에서의 고려 는 상당히 깊다. 예를 들어 Error Code System, 이중 보험 envification(dev/prod 환경 구분), 릴리스 플로우의 도구화

二.개발 도구

CommonJS Module + Haste -> ES Module

React 15 이전 버전은 모두 CommonJS 모듈로 정의. 예를 들어:

var ReactChildren = require('ReactChildren');
module.exports = React;

현재는 ES Module 로 전환. 몇 가지 이유:

  • 모듈 도입/내보내기 문제를 조기에 발견하는 데 도움

    CommonJS Module 은 존재하지 않는 메서드를 쉽게 require 할 수 있으며, 호출하여 오류가 날 때까지 문제를 발견할 수 없다. ES Module 의 정적 모듈 메커니즘은 importexport 가 이름으로 일치해야 한다. 否则 컴파일 구축에서 오류

  • bundle size 상의 우위성

    ES Module 은 tree shaking 으로 bundle 을 더 클린하게 할 수 있다. 근본적인 이유는 module.exports 가 오브젝트 레벨 내보내기이고, export 는 더 세밀한 아토믹 레벨 내보내기를 지원하기 때문. 另一方面, 이름으로 도입함으로써 rollup 등의 도구가 모듈을 평평하게拼接할 수 있고, 압축 도구가 이를 기반으로 더 폭력적인 변수명 난독화를 수행하여 bundle size 를 더욱 감소

소스 코드만 ES Module 로 전환하고, 유닛 테스트用例는 전환하지 않았다. CommonJS Module 이 Jest 의 몇 가지 특성 (예를 들어 resetModules) 에 더 우호적이기 때문 (ES Module 로 전환해도, 모듈 상태 격리가 필요한 씬에서는, 여전히 require 를 사용해야 함. 따라서 전환의 의미가 크지 않음)

Haste 에 대해서는, React 팀이 커스터마이즈한 모듈 처리 도구로, 긴 상대 경로 문제를 해결하기 위해 사용. 예를 들어:

// ref: react-15.5.4
var ReactCurrentOwner = require('ReactCurrentOwner');
var warning = require('warning');
var canDefineProperty = require('canDefineProperty');
var hasOwnProperty = Object.prototype.hasOwnProperty;
var REACT_ELEMENT_TYPE = require('ReactElementSymbol');

Haste 모듈 메커니즘 하에서는 모듈 참조에 명확한 상대 경로를示す 필요가 없고, 프로젝트 레벨에서 유일한 모듈 이름으로 자동 검색. 예를 들어:

// 선언
/**
 * @providesModule ReactClass
 */

// 인용
var ReactClass = require('ReactClass');

표면적으로는 긴 경로 참조 문제를 해결 (프로젝트 구조의 깊은 네스트라는 근본 문제를 해결한 것은 아님). 그러나 비표준 모듈 메커니즘을 사용하는 데는 몇 가지 전형적인 단점이 있음:

  • 표준과 맞지 않아, 표준 생태계의 도구에接入할 때 적응 문제에 직면

  • 소스 코드가 읽기 어렵고, 모듈 의존 관계를 이해하기 어려움

React 16 에서는 대부분의 커스텀 모듈 메커니즘을 제거 (ReactNative 에는 아직一小部分 남아 있음). Node 표준의 상대 경로 참조를 채택. 긴 경로 문제는 프로젝트 구조를 리팩토링하여 철저히 해결. 플랫한 디렉토리 구조를 채택 (동일 package 하에서最深 2 레벨 참조, 跨 package 는 Yarn 처리를 거쳐顶层 절대 경로 참조)

Flow + ES Lint

Flow 는 타입 오류를 체크하고, 타입 불일치의 잠재적 문제를 조기에 발견. 예를 들어:

export type ReactElement = {
  $$typeof: any,
  type: any,
  key: any,
  ref: any,
  props: any,
  _owner: any, // ReactInstance or ReactFiber

  // __DEV__
  _store: {
    validated: boolean,
  },
  _self: React$Element<any>,
  _shadowChildren: any,
  _source: Source,
};

정적 타입 선언 및 체크 외에, Flow 의 최대 특징은 React 컴포넌트 및 JSX 에 대한深度서포트:

type Props = {
  foo: number,
};
type State = {
  bar: number,
};
class MyComponent extends React.Component<Props, State> {
  state = {
    bar: 42,
  };

  render() {
    return this.props.foo + this.state.bar;
  }
}

P.S. Flow 의 React 서포트에 대한 상세 정보는, Even Better Support for React in Flow 참조

또한 내보내기 타입 체크의 Flow "마법"이 있어, mock 모듈의 내보내기 타입이 소스 모듈과 일치하는지 검증:

type Check<_X, Y: _X, X: Y = _X> = null;
(null: Check<FeatureFlagsShimType, FeatureFlagsType>);

ES Lint 은 구문 오류 및 約定 코딩 스타일 오류를 체크. 예를 들어:

rules: {
  'no-unused-expressions': ERROR,
  'no-unused-vars': [ERROR, {args: 'none'}],
  // React & JSX
  // Our transforms set this automatically
  'react/jsx-boolean-value': [ERROR, 'always'],
  'react/jsx-no-undef': ERROR,
}

Prettier

Prettier 는 코드를 자동 포맷. 몇 가지 용도:

  • 구 코드를 통일 스타일로 포맷

  • 提交 전에 변경이 있는 부분을 포맷

  • 지속적 통합과 연계하여, PR 코드 스타일이 완전히 일치하는 것을 보증 (否则 build 실패하고, 스타일 차이가 있는 부분을 출력)

  • IDE 에 통합, 일상没事 포맷

  • 구축 결과를 포맷. 一方面 dev bundle 의 가독성을 향상. 另外有助於 prod bundle 중의 冗長 코드를 발견

통일 코드 스타일은 물론 협력에 유리. 另外, 오픈소스 프로젝트에서는, 스타일이 다양한 PR 에 빈번히 직면. 엄격한 포맷 체크를 지속적 통합의 강제環節으로 함으로써 코드 스타일 차이 문제를 철저히 해결할 수 있고, 오픈소스 작업의 간소화에 도움

P.S. 프로젝트 전체에 강제 통일 포맷은 다소 극단적으로 보이지만, 대담한 시도. 그러나 효과는 꽤 좋다고 함:

Our experience with Prettier has been fantastic, and we recommend it to any team that writes JavaScript.

Yarn workspace

Yarn 의 workspace 특성은 monorepo 의 package 의존을 해결 (lerna bootstrap 과 유사한 작용). node_modules 하에소프트 링크를建立하여 Node 모듈 메커니즘을 "속임"

Yarn Workspaces is a feature that allows users to install dependencies from multiple package.json files in subfolders of a single root package.json file, all in one go.

package.json/workspaces 로 Yarn workspaces 를 배치:

// ref: react-16.2.0/package.json
"workspaces": [
  "packages/*"
],

주의: Yarn 의 실제 처리는 Lerna 와 유사. 둘 다 소프트 링크로 실현. 그러나 패키지 매니저这一層에서 monorepo package 서포트를 제공하는 것이 더 합리적. 상세한 이유는 Workspaces in Yarn | Yarn Blog 참조

그리고 yarn install 후에 愉快地跨 package 인용 가능:

import {enableUserTimingAPI} from 'shared/ReactFeatureFlags';
import getComponentName from 'shared/getComponentName';
import invariant from 'fbjs/lib/invariant';
import warning from 'fbjs/lib/warning';

P.S. 另外, Yarn 과 Lerna 는 시ーム리스하게 결합 가능. useWorkspaces 옵션으로 의존 처리 부분을 Yarn 에게 맡김. 상세는 Integrating with Lerna 참조

HUBOT

HUBOT 은 GitHub 로봇. 일반적으로 다음에 사용:

  • 지속적 통합에接入. PR 이 구축/체크를 트리거

  • Issue 를 관리. 비활성 논의 포스트를 클로즈

주로 PR 과 Issue 를 중심으로 자동화를 수행. 예를 들어 React 팀은 계획 (현재 아직 실시하지 않음) 로봇이 PR 의 bundle size 에 대한 영향을 회신. 이로써 bundle size 의 지속 최적화를 독촉

현재 매번 구축 시 bundle size 변화를 파일에 출력하고, Git 에 변화를 추적시킴 (提交). 예를 들어:

// ref: react-16.2.0/scripts/rollup/results.json
{
  "bundleSizes": {
    "react.development.js (UMD_DEV)": {
      "size": 54742,
      "gzip": 14879
    },
    "react.production.min.js (UMD_PROD)": {
      "size": 6617,
      "gzip": 2819
    }
  }
}

단점은 상상한 대로. 이 json 파일은 빈번히 컨플릭트. 要么 머지 컨플릭트에精力을 낭비, 要么 이 자동 생성된 번거로운 파일을 提交 하는 것을 게을리하여, 버전이滞后. 따라서 GitHub Bot 으로 이 번거로움을 추출할 계획

三.구축 도구

bundle 형식

이전에는 2 종류의 bundle 형식을 제공:

  • UMD 단일 파일. 외부 의존으로 사용

  • CJS 散 파일. 自行 구축 bundle 을 서포트 (React 를 소스 코드 의존으로)

몇 가지 문제가 존재:

  • 自行 구축 버전이 불일치: 다른 build 환경/配置로 구축된 bundle 은 모두 다름

  • bundle 퍼포먼스에 최적화 공간: 打包 App 의 방식으로 類庫를 구축하는 것은 그다지 적절하지 않음. 퍼포먼스에 향상 여지

  • 실험적 최적화 시도에 불리: 散 파일 모듈에打包, 압축 등의 최적화 수단을 적용할 수 없음

React 16 은 bundle 형식을 조정:

  • CJS 散 파일을 제공하지 않음. npm 에서 취득하는 것은 구축済み로, 통일 최적화된 bundle

  • UMD 단일 파일과 CJS 단일 파일을 제공. 각각 Web 환경과 Node 환경 (SSR) 에 사용

再分割할 수 없는 類庫의 자세로, 최적화環節을 모두 거두어, bundle 형식에 의한 제한에서 탈피

Gulp/Grunt+Browserify -> Rollup

이전의 구축 시스템은 Gulp/Grunt+Browserify 로 손으로 만든 한 세트의 도구. 후에 확장 면에서 도구에 제한. 예를 들어:

  • Node 환경에서 퍼포먼스가 좋지 않음: 빈번한 process.env.NODE_ENV 액세스가 SSR 퍼포먼스를拖慢. 그러나 類庫 각도에서 해결할 방법이 없음. Uglify 가 이에 의존하여 无用 코드를去除하기 때문

따라서 React SSR 퍼포먼스最佳 실천에는 일반적으로 "React 를重新打包하고, 구축 시에 process.env.NODE_ENV 를去除"라는一条가 있음 (물론, React 16 은 더 이상 이렇게 할 필요 없음. 이유는 위에서 언급한 bundle 형식 변화)

과도하게 복잡한 (overly-complicated) 커스텀 구축 도구를丢弃하고, 더 적절한 Rollup 으로改用:

It solves one problem well: how to combine multiple modules into a flat file with minimal junk code in between.

P.S. Haste -> ES Module 이든 Gulp/Grunt+Browserify -> Rollup 의 전환이든, 비표준의 커스터마이즈方案에서 표준의 개방方案으로 전환. "손으로 만들기" 면에서 교훈을 흡수해야 함. 왜 업계 규범의 것이 우리 씬에서 적용하지 않고, 스스로 만들어야 하는가?

mock module

구축 시에동적 의존의 씬에 직면할 가능성: 다른 bundle 이 기능은 유사하지만 구현에 차이가 있는 module 에 의존. 예를 들어 ReactNative 의 오류 알림 메커니즘은 빨간 프레임을 표시. Web 환경은 Console 에 출력

일반 해법은 2 종류:

  • 런타임 동적 의존 (주입): 2 개를 bundle 에 넣고, 런타임에配置또는 환경에 따라 선택

  • 구축 시에 의존을 처리: 여러份을 다 구축. 다른 bundle 에 각각 필요한 의존 모듈을 포함

명백히 구축 시 처리가 더 클린함. 즉 mock module. 개발 중에는 이러한 차이를気に하지 않고, 구축 시에 환경에 따라 자동으로 구체적인 의존을 선택. 손으로 쓴 간단한 Rollup 플러그인으로 실현: 동적 의존配置 + 구축 시 의존 치환

Closure Compiler

google/closure-compiler 는 매우 강력한 minifier. 3 종류의 최적화 모드 (compilation_level) 가 있음:

  • WHITESPACE_ONLY: 주석, 여분의 구두점 기호와 공백 문자를去除. 논리 기능상으로는 소스 코드와 완전히 동치

  • SIMPLE_OPTIMIZATIONS: 디폴트 모드. WHITESPACE_ONLY 의基础上에서 더욱 변수명을 단축 (국소 변수와 함수 형参). 논리 기능은 기본적으로 동치. 특수情況 (예를 들어 eval('localVar') 로 이름으로 국소 변수�� 액세스하고 fn.toString() 를解析) 을 제외

  • ADVANCED_OPTIMIZATIONS: SIMPLE_OPTIMIZATIONS 의基础上에서 더 강력한 리네임 (全局 변수명, 함수명과 속성). 无用 코드를去除 (도달할 수 없는, 사용하지 않는). 메서드 호출과 상수를 인라인 (划算하면, 함수 호출을 함수 본체 내용으로, 상수를 그 값으로 치환)

P.S. compilation_level 의 상세 정보는 Closure Compiler Compilation Levels 참조

ADVANCED 모드는 과도하게 강력:

// 입력
function hello(name) {
  alert('Hello, ' + name);
}
hello('New user');

// 출력
alert("Hello, New user");

P.S. Closure Compiler Service 에서 온라인試玩 가능

이행 전환에는 일정의 리스크가 있음. 따라서 React 가 사용하고 있는 것은 아직 SIMPLE 모드. 그러나後続에 ADVANCED 모드를开启할 계획이 있을 가능성. Closure Compiler 로 bundle size 최적화를充分利用

Error Code System

In order to make debugging in production easier, we're introducing an Error Code System in 15.2.0. We developed a gulp script that collects all of our invariant error messages and folds them to a JSON file, and at build-time Babel uses the JSON to rewrite our invariant calls in production to reference the corresponding error IDs.

簡言之, prod bundle 중에서 상세한 오류 메시지를 대응하는 오류 코드로 치환. 프로덕션 환경에서 런타임 오류를 캡처하면 오류 코드와 컨텍스트 정보를 던져내고, 오류 코드 변환 서비스에 던져 완전한 오류 정보를 환원. 이로써 prod bundle 을 가능한 한 클린하게 보증함과 동시에, 개발 환경과 같은 상세 오류 보고 능력을 보유

예를 들어 프로덕션 환경 하의 非法 React Element 오류:

Minified React error #109; visit https://reactjs.org/docs/error-decoder.html?invariant=109&args[]=Foo for the full message or use the non-minified dev environment for full errors and additional helpful warnings.

매우 재미있는 테크닉. 확실히 개발 체험 향상에 많은心思을 쏟음

envification

소위envification이란 환경별로 build. 예를 들어:

// ref: react-16.2.0/build/packages/react/index.js
if (process.env.NODE_ENV === 'production') {
  module.exports = require('./cjs/react.production.min.js');
} else {
  module.exports = require('./cjs/react.development.js');
}

常用 수단. 구축 시에 process.env.NODE_ENV 를 목표 환경 대응의 문자열 상수로 치환. 後続 구축 프로세스 중 (打包 도구/압축 도구) 에서 여분의 코드를剔除

package 入口 파일 외에, 안에서도 같은 판단을이중 보험으로 실시:

// ref: react-16.2.0/build/packages/react/cjs/react.development.js
if (process.env.NODE_ENV !== "production") {
  (function() {
    module.exports = react;
  })();
}

此外, 개발자가 실수로 dev bundle 을上线하는 것을 걱정. 따라서 React DevTools 에도一点 알림을 추가:

This page is using the development build of React. 🚧

DCE check

DCE(dead code eliminated) check 란 无用 코드가 정상적으로去除되었는지를 확인

一種의 특수情況을 고려: process.env.NODE_ENV 가 런타임에 설정되는 것도 불합리 (다른 환경의 여분 코드가 존재할 가능성). 따라서 React DevTools 로 bundle 환경 체크도 실시:

// ref: react-16.2.0/packages/react-dom/npm/index.js
function checkDCE() {
  if (process.env.NODE_ENV !== 'production') {
    throw new Error('^_^');
  }
  try {
    __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(checkDCE);
  } catch (err) {
    console.error(err);
  }
}
if (process.env.NODE_ENV === 'production') {
  checkDCE();
}

// DevTools 即__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE 선언
checkDCE: function(fn) {
  try {
    var toString = Function.prototype.toString;
    var code = toString.call(fn);
    if (code.indexOf('^_^') > -1) {
      hasDetectedBadDCE = true;
      setTimeout(function() {
        throw new Error(
          'React is running in production mode, but dead code ' +
            'elimination has not been applied. Read how to correctly ' +
            'configure React for production: ' +
            'https://fb.me/react-perf-use-the-production-build'
        );
      });
    }
  } catch (err) { }
}

원리는 Redux 의 [minified 검출](/articles/redux 소스 코드解读/#articleHeader5) 과 유사. 먼저 dev 환경 판단을 포함한 메서드를 선언. 판단 중에 하나의標識 문자열 (위 예에서는 ^_^) 을 포함하고, 然后 런타임 (DevTools 를 통해) 에 fn.toString() 소스 코드를 체크. 該標識 문자열을 포함하고 있으면 DCE 실패 (无用 코드가 build 프로세스 중에去除되지 않음) 라고 판단. 비동기 throw

P.S. DCE check 의 상세 정보는, Detecting Misconfigured Dead Code Elimination 참조

四.테스트 도구

Jest

Jest 는 Facebook 이推出한 테스트 도구.亮点은 다음과 같다:

  • Snapshot Testing: DOM 트리 스냅샷으로 React/React Native 컴포넌트의 UI 테스트를 실시. 컴포넌트 렌더링 결과를 이전의 스냅샷과 비교. 차이가 없으면 통과

  • 제로配置: Mocha 와 같이強大하고 유연하지만配置가 번거롭지 않음. Jest 는开箱即用. 테스트 구동, 어설션 라이브러리, mock 메커니즘, 테스트 커버리지 등을自带

Snapshot Testing 은 UI 자동화 테스트의 일반做法와 유사. 올바른 결과를 스크린샷하여 기준으로 함 (이 기준은 지속적으로 업데이트할 필요가 있음. 따라서 스냅샷 파일은 일반적으로 소스 코드와 함께 提交). 後続 매번 변경 후에 이전의 스크린샷과 픽셀 레벨로 비교. 차이가 있으면 문제가 있다고 판단

另外, React App 테스트에言及하면, 또 하나더狠한것이 있음: Enzyme. Jest + Enzyme 으로 React 컴포넌트의深度 테스트를 실시 가능. 상세 정보는 Unit Testing React Components: Jest or Enzyme? 참조

P.S. 프런트엔드 UI 자동화 테스트의 일반 방법에 대해서는, 如何进行前端自动化测试? - 张云龙的回答 - 知乎 참조

P.S. repl.it - try-jest by @amasad 에서 온라인試玩 가능

preventing Infinite Loops

즉死循環 체크. 테스트 프로세스가死循環으로阻塞되는 것을 희망하지 않음 (React 16 에서 재귀를 루프로 변경한 후 많은 while (true) 가 있음. 그들은 너무放心하지 않음). 처리 방식은死再帰 체크와 유사: 최대深度 (TTL) 를 제한. Babel 플러그인으로 실시. 테스트 환경 구축 시에 체크를 주입:

// ref: https://github.com/facebook/react/blob/master/scripts/jest/preprocessor.js#L38
require.resolve('../babel/transform-prevent-infinite-loops'),

// ref: https://github.com/facebook/react/blob/master/scripts/babel/transform-prevent-infinite-loops.js#L37
'WhileStatement|ForStatement|DoWhileStatement': (path, file) => {
  const guard = buildGuard({
    ITERATOR: iterator,
    MAX_ITERATIONS: t.numericLiteral(MAX_ITERATIONS),
  });
  if (!path.get('body').isBlockStatement()) {
    const statement = path.get('body').node;
    path.get('body').replaceWith(t.blockStatement([guard, statement]));
  } else {
    path.get('body').unshiftContainer('body', guard);
  }
}

防護에 사용하는 buildGuard 는 다음과 같다:

const buildGuard = template(`
  if (ITERATOR++ > MAX_ITERATIONS) {
    global.infiniteLoopError = new RangeError(
      'Potential infinite loop: exceeded ' +
      MAX_ITERATIONS +
      ' iterations.'
    );
    throw global.infiniteLoopError;
  }
`);

여기서 하나의全局 오류 변수 global.infiniteLoopError 를 사용. 後続 테스트 프로세스를 중단:

// ref: https://github.com/facebook/react/blob/master/scripts/jest/setupTests.js#L56
 env.afterEach(() => {
  const error = global.infiniteLoopError;
  global.infiniteLoopError = null;
  if (error) {
    throw error;
  }
});

각 case 종료 시에死循環이 발생했는지를 확인. guard 중의 throw 오류가外層 catch 에포착된 후, 테스트 프로세스가 여전히 정상적으로 진행하는 것을 방지

manual test fixture

Node 환경 엔지니어링의 유닛 테스트 외에, 브라우저 환경人工 테스트의用例集도作成. 다음을 포함:

  • WebDriver 에 기반한 애플리케이션 테스트 (Facebook 에서는, 이 애플리케이션은主站을 가리킴)

  • 人工 테스트用例. 필요한 때에人工으로 DOM 관련 변경을 검증

브라우저 환경의 자동화 테스트를 실시하지 않는 주요 이유는 3 개:

  • 브라우저 환경의 테스트 도구는 그다지 신뢰할 수 없음 (flaky). 以往의 경험에서 보면, 많은 문제를 발견할 수 없음

  • 지속적 통합을拖慢하고, 개발 워크플로우 효율에 영향. 또한 지속적 통합도 상대적으로 취약해짐

  • 자동화 테스트는 항상 DOM 문제를 발견할 수 있는 것은 아님. 예를 들어 브라우저 표시의 입력 값은 DOM 속성으로 취득한 것과 일치하지 않을 가능성

브라우저 환경의 자동화 테스트를 실시하고 싶지 않지만, 유지 중에添加한一些경계 case 처리가 업데이트 변경으로 파괴되지 않는 것을確保하고 싶음. 따라서 가장 유효한 방식을 채택: 경계 case 에 대해 테스트用例를 쓰고, 人工 테스트 검증

구체적인做法는 Demo App 에 대해 수동으로 React 버전을 전환하고, 다른 버전/다른 브라우저 하에서 표현이 일치하는지를 확인:

The fixture app lets you choose a version of React (local or one of the published versions) which is handy for comparing the behavior before and after the changes.

매우 어리석게 보이지만, DOM 관련 문제를 발견하는 데는 확실히 가장 직접 유효한 방식. 또한 이러한用例가 일정의 정도에蓄積되었을 때, 품질 보증에 상당 큰 작용을 함 (자신 있게 DOM 관련 변경을 실시. 나중에 아무도 움직이지 않는境地를 피함). 예를 들어:

the DOM attribute handling in React 16 was very hard to pull off with confidence at first. We kept discovering different edge cases, and almost gave up on doing it in time for the React 16 release.

가치 있는人工 테스트用例를蓄積하려면 많은精力을投入해야 함. 엔지니어링 수단으로 가능한 한 자동화하는 외에, GitHub Bot 으로 커뮤니티 파트너도 쉽게 참여할 수 있도록 할 계획. 따라서 이러한 "어리석은 일"도為すべからず가 아니라, 為すべし. 예측 가능한 이점은: 大改不虚

五.릴리스 도구

npm publish

릴리스 플로우를規範/간소화하기 위해, 몇 가지 일을 실시:

  • master + feature flag 의 브랜치 전략을 채택

  • 릴리스 플로우를 도구화

이전에는 stable 브랜치 전략을 채택. 버전 릴리스 시에 수동으로 cherry-pick. 버전을 릴리스하면 하루를 소비. 후에 master 에서 직접 릴리스하도록 조정. 원하지 않는 breaking change 에 대해, feature flag 로 구축 시에去除. 수동 cherry-pick 의 번거로움을 면제

릴리스 플로우에全套 도구를 실시. 자동화할 수 있는 것은 자동으로 순서 실행.人工操作에 의존하는 것은提示하여 저장退出. 人工 처리完毕后進度를回復하여先に나아감. 예를 들어:

자동
  test
  build
人工
  changelog
  smoke test
자동
  commit changelog
  publish npm package
人工
  GitHub release
  update site version
  test new release
  notify involved team

이로써 도구화 checklist 로 인위적 실수를 감소. 規範 일관된 릴리스 플로우를 보증

P.S. 릴리스 도구 자신을 체크하기 쉽게 하기 위해, 모의 릴리스 옵션도 제공. 버전 릴리스의 실제 조작을 스킵하고, 플로우를空走 가능

참고 자료

댓글

아직 댓글이 없습니다

댓글 작성