2016年1月8日星期五

阮一峰的网络日志

阮一峰的网络日志


世界第一个机器人

Posted: 08 Jan 2016 04:33 AM PST

上个月,"机器人之父"恩格尔伯格去世。

《财新周刊》的编辑让我写他的介绍。我查了网上资料,发现那些早期机器人的照片非常有意思。

下面就是我写的人物生平,配上这些有趣的照片。

1.

12月1日,被誉为"机器人之父"的约瑟夫·恩格尔伯格(Joseph Engelberger),在美国因病去世,享年90岁。他发明制造了人类第一台机器人。

恩格尔伯格1925年生于纽约,先后获得哥伦比亚大学物理学士和电子工程硕士学位。

2.

1950年,恩格尔伯格读到了阿西莫夫的小说集《我,机器人》(I, Robot),爱不释手,产生了制造机器人的念头。

3.

1956年的一场酒会,他偶遇发明家德沃尔(George C. Devol)。后者提到,他刚刚申请了一个专利,叫做"可编程的用于移动物体的设备"(Programmed Article Transfer)。恩格尔伯格脱口而出,"这不是阿西莫夫笔下的机器人吗!"两人一拍即合,决定合作创立一家生产机器人的公司。

4.

新公司取名Unimation,意为"自动化单位",1958年正式运作。当年就拿出了第一个产品:一个可以自动完成搬运的机械手臂。虽然,这个产品庞大而笨重,只能完成很简单的任务,但它是人类历史上第一个机器人。

5.

恩格尔伯格和德沃尔从一开始就很清楚,机器人的研发成本很高,只有大公司才买得起。他们把研发方向定在为重型制造业服务,让机器人承担对人类有危险的工作。美国最大的通用汽车公司,是他们首先想到的目标客户。

6.

恩格尔伯格游说通用汽车,希望他们买一台机器人。由于以前从来没有这种东西,很多人对这个产品将信将疑。1961年,通用汽车好不容易答应,在离纽约最近的新泽西工厂,装一台试试看。第一台Unimation机器人的成本是6万美元,售价却只有2万5千美元,因为不这样就卖不出去。

7.

工业机器人在生产线上的作用非常明显,动作精准、永不疲倦、不怕高温和污染。比如,焊接工作处于高温环境,会产生有害气体,工人一不小心就会中毒,机器人就没有这个问题。

通用汽车开始订购更多的机器人,安装在全美各地的工厂,承担的工作扩展到焊接、油漆、粘合和装配。这帮助它取得了自动化生产的革命性突破,巩固和扩大了行业领先地位。其他汽车公司纷纷跟进,将机器人用于自家的流水线。美国机器人协会后来评价,恩格尔伯格的这个发明"彻底改变了现代工业和汽车制造的流程"。

8.

上个世纪60年代,现代工业革命达到了顶峰,阿波罗计划把人类送上了月球,整个美国都对新技术、新设备充满了兴趣。1966年,最热门的晚间谈话节目把Unimation机器人请上了电视,让它对着300万全国观众,发高尔夫球、倒啤酒、挥舞指挥棒,甚至拉手风琴。从此,恩格尔伯格一举成名。

9.

1969年,日本人将恩格尔伯格请到东京演讲,指导日本汽车厂商研发机器人。

10.

川崎重工引进了Unimation机器手臂。这件事对日本汽车工业有深远影响,日本后来超过美国成了"机器人王国"。恩格尔伯格在这个过程中扮演了重要角色,他在日本的知名度甚至比在美国还要大。

11.

随着年龄和荣誉的增长,恩格尔伯格在技术上变得保守。他不赞成在机器人内部,使用电机替代液压,也不认为制造有腿的机器人是必要的,使用轮子更符合实际。这导致他在1983年将Unimation公司以1.07亿美元的价格,卖给了西屋公司,退出了工业机器人行业。

那时,恩格尔伯格可谓功成名就。第一个Unimation机器人,在运作了10万个小时之后,已经光荣退役,送进了博物馆。全世界制作业使用的机器人,超过了300万个。他本人也当选了美国工程院院士。

12.

他并没有就此止步,1984年又创建了TRC公司,研发服务机器人。1988年,他又推出了世界第一个服务业机器人HelpMate。

13.

这个机器人用来在医院走廊穿行,为病人送饭、送药、送邮件,并记录病人的情况。

14.

恩格尔伯格的目标是为机器人装上各种传感器,使其能够直接为人类服务,首先是帮助老年人和残疾人。

15.

他觉得,很多老人进入养老院时,心智还十分健康,只是行动不便,不能很好地照顾自己。

16.

机器人能够承担重复性家务,比如取物、清洁、做饭等等,用来护理老年人,最合适不过了。

17.

接受彭博商业周刊采访时,恩格尔伯格说道,"家用机器人比工业机器人有更广阔的市场前景"。

18.

他认为,机器人未来一定无处不在。他更想作为"家用机器人之父"被人们铭记。

(完)

文档信息

2016年1月7日星期四

阮一峰的网络日志

阮一峰的网络日志


Commit message 和 Change log 编写指南

Posted: 06 Jan 2016 05:23 AM PST

Git 每次提交代码,都要写 Commit message(提交说明),否则就不允许提交。

  $ git commit -m "hello world"  

上面代码的-m参数,就是用来指定 commit mesage 的。

如果一行不够,可以只执行git commit,就会跳出文本编译器,让你写多行。

  $ git commit  

基本上,你写什么都行(这里这里这里)。

但是,一般来说,commit message 应该清晰明了,说明本次提交的目的。

目前,社区有多种 Commit message 的写法规范。本文介绍Angular 规范(见上图),这是目前使用最广的写法,比较合理和系统化,并且有配套的工具。

一、Commit message 的作用

格式化的Commit message,有几个好处。

(1)提供更多的历史信息,方便快速浏览。

比如,下面的命令显示上次发布后的变动,每个commit占据一行。你只看行首,就知道某次 commit 的目的。

  $ git log <last tag> HEAD --pretty=format:%s  

(2)可以过滤某些commit(比如文档改动),便于快速查找信息。

比如,下面的命令仅仅显示本次发布新增加的功能。

  $ git log <last release> HEAD --grep feature  

(3)可以直接从commit生成Change log。

Change Log 是发布新版本时,用来说明与上一个版本差异的文档,详见后文。

二、Commit message 的格式

每次提交,Commit message 都包括三个部分:Header,Body 和 Footer。

  <type>(<scope>): <subject>  // 空一行  <body>  // 空一行  <footer>  

其中,Header 是必需的,Body 和 Footer 可以省略。

不管是哪一个部分,任何一行都不得超过72个字符(或100个字符)。这是为了避免自动换行影响美观。

2.1 Header

Header部分只有一行,包括三个字段:type(必需)、scope(可选)和subject(必需)。

(1)type

type用于说明 commit 的类别,只允许使用下面7个标识。

  • feat:新功能(feature)
  • fix:修补bug
  • docs:文档(documentation)
  • style: 格式(不影响代码运行的变动)
  • refactor:重构(即不是新增功能,也不是修改bug的代码变动)
  • test:增加测试
  • chore:构建过程或辅助工具的变动

如果typefeatfix,则该 commit 将肯定出现在 Change log 之中。其他情况(docschorestylerefactortest)由你决定,要不要放入 Change log,建议是不要。

(2)scope

scope用于说明 commit 影响的范围,比如数据层、控制层、视图层等等,视项目不同而不同。

(3)subject

subject是 commit 目的的简短描述,不超过50个字符。

  • 以动词开头,使用第一人称现在时,比如change,而不是changedchanges
  • 第一个字母小写
  • 结尾不加句号(.

2.2 Body

Body 部分是对本次 commit 的详细描述,可以分成多行。下面是一个范例。

  More detailed explanatory text, if necessary.  Wrap it to   about 72 characters or so.     Further paragraphs come after blank lines.    - Bullet points are okay, too  - Use a hanging indent  

有两个注意点。

(1)使用第一人称现在时,比如使用change而不是changedchanges

(2)应该说明代码变动的动机,以及与以前行为的对比。

2.3 Footer

Footer 部分只用于两种情况。

(1)不兼容变动

如果当前代码与上一个版本不兼容,则 Footer 部分以BREAKING CHANGE开头,后面是对变动的描述、以及变动理由和迁移方法。

  BREAKING CHANGE: isolate scope bindings definition has changed.        To migrate the code follow the example below:        Before:        scope: {        myAttr: 'attribute',      }        After:        scope: {        myAttr: '@',      }        The removed `inject` wasn't generaly useful for directives so there should be no code using it.  

(2)关闭 Issue

如果当前 commit 针对某个issue,那么可以在 Footer 部分关闭这个 issue 。

  Closes #234  

也可以一次关闭多个 issue 。

  Closes #123, #245, #992  

2.4 Revert

还有一种特殊情况,如果当前 commit 用于撤销以前的 commit,则必须以revert:开头,后面跟着被撤销 Commit 的 Header。

  revert: feat(pencil): add 'graphiteWidth' option    This reverts commit 667ecc1654a317a13331b17617d973392f415f02.  

Body部分的格式是固定的,必须写成This reverts commit &lt;hash>.,其中的hash是被撤销 commit 的 SHA 标识符。

如果当前 commit 与被撤销的 commit,在同一个发布(release)里面,那么它们都不会出现在 Change log 里面。如果两者在不同的发布,那么当前 commit,会出现在 Change log 的Reverts小标题下面。

三、Commitizen

Commitizen是一个撰写合格 Commit message 的工具。

安装命令如下。

  $ npm install -g commitizen  

然后,在项目目录里,运行下面的命令,使其支持 Angular 的 Commit message 格式。

  $ commitizen init cz-conventional-changelog --save --save-exact  

以后,凡是用到git commit命令,一律改为使用git cz。这时,就会出现选项,用来生成符合格式的 Commit message。

四、validate-commit-msg

validate-commit-msg 用于检查 Node 项目的 Commit message 是否符合格式。

它的安装是手动的。首先,拷贝下面这个JS文件,放入你的代码库。文件名可以取为validate-commit-msg.js

接着,把这个脚本加入 Git 的 hook。下面是在package.json里面使用 ghooks,把这个脚本加为commit-msg时运行。

    "config": {      "ghooks": {        "commit-msg": "./validate-commit-msg.js"      }    }  

然后,每次git commit的时候,这个脚本就会自动检查 Commit message 是否合格。如果不合格,就会报错。

  $ git add -A   $ git commit -m "edit markdown"   INVALID COMMIT MSG: does not match "<type>(<scope>): <subject>" ! was: edit markdown  

五、生成 Change log

如果你的所有 Commit 都符合 Angular 格式,那么发布新版本时, Change log 就可以用脚本自动生成(例1例2例3)。

生成的文档包括以下三个部分。

  • New features
  • Bug fixes
  • Breaking changes.

每个部分都会罗列相关的 commit ,并且有指向这些 commit 的链接。当然,生成的文档允许手动修改,所以发布前,你还可以添加其他内容。

conventional-changelog 就是生成 Change log 的工具,运行下面的命令即可。

  $ npm install -g conventional-changelog  $ cd my-project  $ conventional-changelog -p angular -i CHANGELOG.md -w  

上面命令不会覆盖以前的 Change log,只会在CHANGELOG.md的头部加上自从上次发布以来的变动。

如果你想生成所有发布的 Change log,要改为运行下面的命令。

  $ conventional-changelog -p angular -i CHANGELOG.md -w -r 0  

为了方便使用,可以将其写入package.jsonscripts字段。

  {    "scripts": {      "changelog": "conventional-changelog -p angular -i CHANGELOG.md -w -r 0"    }  }  

以后,直接运行下面的命令即可。

  $ npm run changelog  

(完)

文档信息

2016年1月3日星期日

阮一峰的网络日志

阮一峰的网络日志


网站的肥胖症危机

Posted: 03 Jan 2016 02:58 AM PST

最近,有一篇文章正在疯传。

它是上个月,Maciej Ceglowski在澳大利亚的一次演讲,名为《网站的肥胖症危机》(文本视频),反思了互联网开发的现状。

该文非常值得一读,Hacker News排行榜高居榜首,得到了1000多人的推荐。

下面就是我的中文节译版。

===============================

网站的肥胖症危机(节译版)

作者:Maciej Ceglowski

译者:阮一峰

原文网址:The Website Obesity Crisis

1.

大多数网站的主要内容是文本,更准确地说,是简短的文本。

文本本身并不大,但是展示它们的网页,正变得越来越大。Twitter展示单条评论(140个字符)的页面,超过900KB。Medium的一篇文章大约400个词,页面大小是1.2MB。

如果这种趋势持续下去,2020年,网页的体积平均将超过5MB,比一本俄罗斯长篇小说还大。比如,陀思妥耶夫斯基的《罪与罚》,文本压缩后不到800KB。

TechTimes.com有一篇报道,介绍Google正在为大网页做标记。但是,这篇报道的网页,体积为18MB,外加一个3MB的视频。

2015年5月,Facebook引入了"Instant Articles",帮助用户快速浏览新闻。但是,介绍这个功能的页面,体积为6.8MB,外加一个41MB的视频。你想了解这个功能的细节,唯一的方法就是去看这个视频。

2.

网页真的有必要这么大吗?明明200KB就足够,为什么要做成2MB?

因为我们要往里面塞很多不需要的东西:广告、高清图片、视频、用户追踪系统、社交媒体的代码......你不塞,公司就可能解雇你。

如今的时代,你跟雇主说,想做一张体积只有几百KB的网页,就好像跟SUV车主谈论省油的经济型轿车。

有人会说,这是免费内容的代价。但是,我想问,谁会从海量的互联网广告获利?广告主,还是消费者?真正获得暴利的是网络服务提供商和互联网广告公司,其他人都付出了巨大的成本。

3.

我们都忘了健康的网页,应该是什么样子。

  • 值得阅读的文本,配上结构良好的标签。
  • 适度的图片和视觉设计。
  • 一层CSS
  • 少量的JavaScript,只在必需时使用

但是,2015年真实的网页,却是下面这样。

  • 一层HTML
  • 一大堆垃圾
  • 顶部还有一层监控代码

4.

宽带和光纤上网并不解决问题,实际上还鼓励了人们往网页上添加更多的东西。

为了平衡网页体积,工程师想出了很多方法:首屏快速渲染、压缩文件、异步加载、批量HTTP请求、管道发送等等......

网站开发越来越依赖代码精简、压缩、缓存、服务器配置这些中间步骤,这使得找出错误越来越困难,成本越来越高。

5.

复杂性让聪明人上瘾。

即使我们知道复杂不是好事,但难以抵抗。复杂的东西总是显得很酷,让人情不自禁想继续干下去。

大多数网站都过度复杂了。

我们做的每件事,都使得创造网站或编辑网页变得困难。把一篇文章放上网,正在变得需要一个专家团队才能完成。

新手越来越难通过源码学习。我们抽走了人们学习互联网的梯子。

6.

其实只需要两步,就可以大大缩小网页体积,提高性能。

第一步,确保最重要的内容,首先下载和渲染;

第二步,就此结束。

你不需要那些多余的垃圾,对最简主义保持信心就行。

7.

让我们保持互联网是一个超链接构成的媒体,不要把它变成另一种东西。

(完)

文档信息

2015年12月24日星期四

阮一峰的网络日志

阮一峰的网络日志


Git 协作流程

Posted: 23 Dec 2015 10:15 PM PST

Git 作为一个源码管理系统,不可避免涉及到多人协作。

协作必须有一个规范的流程,让大家有效地合作,使得项目井井有条地发展下去。"协作流程"在英语里,叫做"workflow"或者"flow",原意是水流,比喻项目像水流那样,顺畅、自然地向前流动,不会发生冲击、对撞、甚至漩涡。

本文介绍三种广泛使用的协作流程:

  • Git flow
  • Github flow
  • Gitlab flow

如果你对Git还不是很熟悉,可以先阅读下面的文章。

一、功能驱动

本文的三种协作流程,有一个共同点:都采用"功能驱动式开发"(Feature-driven development,简称FDD)。

它指的是,需求是开发的起点,先有需求再有功能分支(feature branch)或者补丁分支(hotfix branch)。完成开发后,该分支就合并到主分支,然后被删除。

二、Git flow

最早诞生、并得到广泛采用的一种协作流程,就是Git flow

2.1 特点

它最主要的特点有两个。

首先,项目存在两个长期分支。

  • 主分支master
  • 开发分支develop

前者用于存放对外发布的版本,任何时候在这个分支拿到的,都是稳定的分布版;后者用于日常开发,存放最新的开发版。

其次,项目存在三种短期分支。

  • 功能分支(feature branch)
  • 补丁分支(hotfix branch)
  • 预发分支(release branch)

一旦完成开发,它们就会被合并进developmaster,然后被删除。

Git flow 的详细介绍,请阅读我翻译的中文版《Git 分支管理策略》

2.2 评价

Git flow的优点是清晰可控,缺点是相对复杂,需要同时维护两个长期分支。大多数工具都将master当作默认分支,可是开发是在develop分支进行的,这导致经常要切换分支,非常烦人。

更大问题在于,这个模式是基于"版本发布"的,目标是一段时间以后产出一个新版本。但是,很多网站项目是"持续发布",代码一有变动,就部署一次。这时,master分支和develop分支的差别不大,没必要维护两个长期分支。

三、Github flow

Github flow 是Git flow的简化版,专门配合"持续发布"。它是 Github.com 使用的协作流程。

3.1 流程

它只有一个长期分支,就是master,因此用起来非常简单。

官方推荐的流程如下。

第一步:根据需求,从master拉出新分支,不区分功能分支或补丁分支。

第二步:新分支开发完成后,或者需要讨论的时候,就向master发起一个pull reqest(简称PR)。

第三步:Pull Request既是一个通知,让别人注意到你的请求,又是一种对话机制,大家一起评审和讨论你的代码。对话过程中,你还可以不断提交代码。

第四步:你的Pull Request被接受,合并进master,重新部署后,原来你拉出来的那个分支就被删除。(先部署再合并也可。)

3.2 评价

Github flow 的最大优点就是简单,对于"持续发布"的产品,可以说是最合适的流程。

问题在于它的假设:master分支的更新与产品的发布是一致的。也就是说,master分支的最新代码,默认就是当前的线上代码。

可是,有些时候并非如此,代码合并进入master分支,并不代表它就能立刻发布。比如,苹果商店的APP提交审核以后,等一段时间才能上架。这时,如果还有新的代码提交,master分支就会与刚发布的版本不一致。另一个例子是,有些公司有发布窗口,只有指定时间才能发布,这也会导致线上版本落后于master分支。

上面这种情况,只有master一个主分支就不够用了。通常,你不得不在master分支以外,另外新建一个production分支跟踪线上版本。

四、Gitlab flow

Gitlab flow 是 Git flow 与 Github flow 的综合。它吸取了两者的优点,既有适应不同开发环境的弹性,又有单一主分支的简单和便利。它是 Gitlab.com 推荐的做法。

4.1 上游优先

Gitlab flow 的最大原则叫做"上游优先"(upsteam first),即只存在一个主分支master,它是所有其他分支的"上游"。只有上游分支采纳的代码变化,才能应用到其他分支。

Chromium项目就是一个例子,它明确规定,上游分支依次为:

  1. Linus Torvalds的分支
  2. 子系统(比如netdev)的分支
  3. 设备厂商(比如三星)的分支

4.2 持续发布

Gitlab flow 分成两种情况,适应不同的开发流程。

对于"持续发布"的项目,它建议在master分支以外,再建立不同的环境分支。比如,"开发环境"的分支是master,"预发环境"的分支是pre-production,"生产环境"的分支是production

开发分支是预发分支的"上游",预发分支又是生产分支的"上游"。代码的变化,必须由"上游"向"下游"发展。比如,生产环境出现了bug,这时就要新建一个功能分支,先把它合并到master,确认没有问题,再cherry-pickpre-production,这一步也没有问题,才进入production

只有紧急情况,才允许跳过上游,直接合并到下游分支。

4.3 版本发布

对于"版本发布"的项目,建议的做法是每一个稳定版本,都要从master分支拉出一个分支,比如2-3-stable2-4-stable等等。

以后,只有修补bug,才允许将代码合并到这些分支,并且此时要更新小版本号。

五、一些小技巧

5.1 Pull Request

功能分支合并进master分支,必须通过Pull Request(Gitlab里面叫做 Merge Request)。

前面说过,Pull Request本质是一种对话机制,你可以在提交的时候,@相关人员团队,引起他们的注意。

5.2 Protected branch

master分支应该受到保护,不是每个人都可以修改这个分支,以及拥有审批 Pull Request 的权力。

GithubGitlab 都提供"保护分支"(Protected branch)这个功能。

5.3 Issue

Issue 用于 Bug追踪和需求管理。建议先新建 Issue,再新建对应的功能分支。功能分支总是为了解决一个或多个 Issue。

功能分支的名称,可以与issue的名字保持一致,并且以issue的编号起首,比如"15-require-a-password-to-change-it"。

开发完成后,在提交说明里面,可以写上"fixes #14"或者"closes #67"。Github规定,只要commit message里面有下面这些动词 + 编号,就会关闭对应的issue。

  • close
  • closes
  • closed
  • fix
  • fixes
  • fixed
  • resolve
  • resolves
  • resolved

这种方式还可以一次关闭多个issue,或者关闭其他代码库的issue,格式是username/repository#issue_number

Pull Request被接受以后,issue关闭,原始分支就应该删除。如果以后该issue重新打开,新分支可以复用原来的名字。

5.4 Merge节点

Git有两种合并:一种是"直进式合并"(fast forward),不生成单独的合并节点;另一种是"非直进式合并"(none fast-forword),会生成单独节点。

前者不利于保持commit信息的清晰,也不利于以后的回滚,建议总是采用后者(即使用--no-ff参数)。只要发生合并,就要有一个单独的合并节点。

5.5 Squash 多个commit

为了便于他人阅读你的提交,也便于cherry-pick或撤销代码变化,在发起Pull Request之前,应该把多个commit合并成一个。(前提是,该分支只有你一个人开发,且没有跟master合并过。)

这可以采用rebase命令附带的squash操作,具体方法请参考我写的《Git 使用规范流程》

(完)

文档信息