From 7cf0e9c3047e7a528f21ab9faa62f3dc9e6886d6 Mon Sep 17 00:00:00 2001 From: Jerry Lee Date: Thu, 19 Jul 2018 14:41:48 +0800 Subject: [PATCH] format cleanup --- api-design-principles-from-qt/README.md | 2 +- .../README.md | 68 +++++------ gui-and-cli-principles/README.md | 12 +- how-to-ask-questions-the-smart-way/README.md | 4 +- how-to-ask-questions-the-smart-way/core.md | 66 +++++----- how-to-ask-questions-the-smart-way/others.md | 7 +- .../README.md | 114 +++++++++--------- python-philosophy/README.md | 14 +-- 8 files changed, 142 insertions(+), 145 deletions(-) diff --git a/api-design-principles-from-qt/README.md b/api-design-principles-from-qt/README.md index ded62dd..b0e9fa0 100644 --- a/api-design-principles-from-qt/README.md +++ b/api-design-principles-from-qt/README.md @@ -4,7 +4,7 @@ # `API`设计原则 - `Qt`官网的设计实践总结 -## 🍎 译序 +## 🍎 译序 `Qt`的设计水准在业界很有口碑,一致、易于掌握和强大的`API`是`Qt`最著名的优点之一。此文既是`Qt`官网上的`API`设计指导准则,也是`Qt`在`API`设计上的实践总结。虽然`Qt`用的是`C++`,但其中设计原则和思考是具有普适性的(如果你对`C++`还不精通,可以忽略与`C++`强相关或是过于细节的部分,仍然可以学习或梳理关于`API`设计最有价值的内容)。整个篇幅中有很多示例,是关于`API`设计一篇难得的好文章。 diff --git a/generic-io-api-in-java-and-api-design/README.md b/generic-io-api-in-java-and-api-design/README.md index c8a3ee7..7f9b7fa 100644 --- a/generic-io-api-in-java-and-api-design/README.md +++ b/generic-io-api-in-java-and-api-design/README.md @@ -1,7 +1,7 @@ 原文链接:[A generic input/output API in Java](https://dzone.com/articles/generic-inputoutput-api-java) - _Rickard Öberg_ (PS:[文章原始链路](http://www.jroller.com/rickard/entry/a_generic_input_output_api)已失效) 译文发在:[【译】Java的通用I/O API](http://oldratlee.com/474/tech/java/generic-io-api-in-java-and-api-design.html),2012-05-11 -## 🍎 译序 +## 🍎 译序 本文给出了一个通用`Java` `IO` `API`设计,并且有`API`的`Demo`代码。 @@ -10,27 +10,28 @@ 设计偏向是艺术,一个赏心悦目的设计,尤其是`API`设计,旁人看来多是妙手偶得的感觉,如果能有些章可循真是一件美事。 -给出 _**减少艺术的艺术工作量**_ 的方法的人是 **大师**。 +给出 _**减少艺术的艺术工作量**_ 的方法的人是 **大师**。 ❤️ -### 目录 +# `Java`的通用`I/O` `API`设计 + +------------------------------------------------------------------------------- + + -- [`Java`的通用`I/O` `API`设计](#java%E7%9A%84%E9%80%9A%E7%94%A8io-api%E8%AE%BE%E8%AE%A1) - - [API](#api) - - [标准化`I/O`](#%E6%A0%87%E5%87%86%E5%8C%96io) - - [拦截传输过程](#%E6%8B%A6%E6%88%AA%E4%BC%A0%E8%BE%93%E8%BF%87%E7%A8%8B) - - [Usage in the `Qi4j` `SPI`](#usage-in-the-qi4j-spi) - - [结论](#%E7%BB%93%E8%AE%BA) +- [API](#api) +- [标准化`I/O`](#%E6%A0%87%E5%87%86%E5%8C%96io) +- [拦截传输过程](#%E6%8B%A6%E6%88%AA%E4%BC%A0%E8%BE%93%E8%BF%87%E7%A8%8B) +- [Usage in the `Qi4j` `SPI`](#usage-in-the-qi4j-spi) +- [结论](#%E7%BB%93%E8%AE%BA) +- [译跋](#%E8%AF%91%E8%B7%8B) -`Java`的通用`I/O` `API`设计 -========================= - -![](input-output.jpg) +------------------------------------------------------------------------------- 上周处理了很多数据搬移,有原始`byte`形式的,也有`String`形式的,还有`SPI`和领域级对象形式。这些活让我觉得,以可伸缩、高性能、正确处理错误的方式把数据从一处搬到另一处,是非常有难度。我要一遍又一遍做一些事,比如从文件中读出`String`。 @@ -69,12 +70,11 @@ 行左边的数字是我标识的4个部分。 1. 客户代码,初始化了传输,要知道输入和输出的源。 -1. 从输入中读的代码。 -1. 辅助代码,用于跟踪整个过程。这些代码我希望能够重用,而不管是何种传输的类型。 -1. 最后这个部分是接收数据,写数据。这个代码,我要批量读写,可以在第2第4部分修改,改成一次处理多行。 +1. 从输入中读的代码。 +1. 辅助代码,用于跟踪整个过程。这些代码我希望能够重用,而不管是何种传输的类型。 +1. 最后这个部分是接收数据,写数据。这个代码,我要批量读写,可以在第2第4部分修改,改成一次处理多行。 -API ---------------------------------------- +## API 一旦明确上面划分的内容,剩下就只是为每个部分整理成一个接口,并保证在各种场景能方便使用。结果如下。 首先要有输入,即`Input`接口: @@ -110,7 +110,7 @@ public interface Sender } ``` -`Output`调用`sendTo`方法,传入一个`Receiver`,`Sender`使用这个`Receiver`来发送一个一个的数据。`Sender`在这个时候发起传输,把类型数据`T`传输到`Receiver`,一次一个。`Receiver`接口如下: +`Output`调用`sendTo`方法,传入一个`Receiver`,`Sender`使用这个`Receiver`来发送一个一个的数据。`Sender`在这个时候发起传输,把类型数据`T`传输到`Receiver`,一次一个。`Receiver`接口如下: ```java public interface Receiver @@ -124,8 +124,7 @@ public interface Receiver 这个简单的模式在发送方和接收方各有2个接口,并保持了以可伸缩、高性能和容错的方式传输数据的潜能。 -标准化`I/O` ---------------------------------------- +## 标准化`I/O` 上文的`API`定义了数据发送和接收的契约,然后可以制定几个输入输出的标准。比如:从文本文件中读取文本行后再写成文本文件。这个操作可以静态方法中,方便的重用。最后,拷贝文本文件可以写成: @@ -137,22 +136,21 @@ Inputs.text( source ).transferTo( Outputs.text(destination) ); 一行代码处理了读文件、写文件、资源清理和其它零零碎碎的操作。真心的赞!`transferTo`方法会抛出`IOException`,要向用户显示`Error`可以`catch`这个异常。但实际处理这些`Error`往往是,关闭文件,把没有写成功的文件删除,而这些`Input`、`Output`已经处理好了。我们再也不需要关心文件读写的细节! -拦截传输过程 ---------------------------------------- +## 拦截传输过程 上面处理了基本的`I/O`传输,我们常常还要做些其它的事。可能要计数一下传输了多少个数据,过滤一下数据,或者是每1000条数据做一下日志,又或者要看一下正在进行什么操作。既然输入输出已经分离,这些事变成在输入输出的协调代码中简单地插入一些逻辑。大部分协调代码有类似的功能,可以放到标准的工具方法中,更方便使用。 第一个标准修饰器是一个过滤器。实现时我用到了`Specification`。 ```java -public static +public static Output filter( final Specification specification, final Output output) { ... create an Output that filters items based on the Specification ... } ``` -`Specification`如下: +`Specification`如下: ```java interface Specification @@ -161,7 +159,7 @@ interface Specification } ``` -有了这个简单部件,我可以在传输时轻松地过滤掉那些不要出现在接收者端的数据。下面的例子删除文件中的空行: +有了这个简单部件,我可以在传输时轻松地过滤掉那些不要出现在接收者端的数据。下面的例子删除文件中的空行: ```java File source = ... @@ -175,14 +173,14 @@ Inputs.text( source ).transferTo( Transforms.filter(new Specification() }, Outputs.text(destination) ); ``` -第二个常见的操作是把数据从一个类型映射到另一个类型。就是处理要`Input`和`Output`的数据类型不同,要有方法把输入数据类型映射成输出的数据类型。下面例子的把`String`映射成`JSONObject`,操作方法会是这个样子: +第二个常见的操作是把数据从一个类型映射到另一个类型。就是处理要`Input`和`Output`的数据类型不同,要有方法把输入数据类型映射成输出的数据类型。下面例子的把`String`映射成`JSONObject`,操作方法会是这个样子: ```java public static Output map(final Function function, final Output output) ``` -`Function`定义是: +`Function`定义是: ```java interface Function @@ -211,27 +209,25 @@ Inputs.text( source ).transferTo( Transforms.map(counter, Outputs.text(destinati System.out.println("Nr of lines:"+counter.getCount()) ``` -Usage in the `Qi4j` `SPI` ---------------------------------------- +## Usage in the `Qi4j` `SPI` 【译者注,这一节说具体库`Qi4j`,略过】 -结论 ---------------------------------------- +## 结论 软件开发时,从一个输入到另一个输出的数据和对象的搬移很常见,可能在中间还要做些转换。通常都是用一些零散代码(`scratch`)来完成这些事,结果是代码错误和使用不当的模式。通过引入通用`I/O` `API`,恰当封闭和隔离,这个任务可以可以更轻松地以伸缩、高性能、无错误的方式完成,并且还可以在在需要额外功能时修饰实现。 这遍文章仅仅勾勒了这种使用方式,`API`和辅助类可以在`Qi4j Core 1.3-SNAPSHOT`中有(详见`Qi4j`的[主页](http://www.qi4j.org/))。理想状态是,在整个`Qi4j`使用中任何使用`I/O`的地方一开始按这种方式来。 > 【译注】`Qi4j`已经更名为`polygene`,在`Apache`上 -> - 官网 https://polygene.apache.org/ -> - GitHub仓库: https://github.com/apache/polygene-java +> - 官网 +> - GitHub仓库: 多谢你的阅读,希望你能有所收获 :-) -**-EOF-** +**_`-EOF-`_** -**译注:** +## 译跋 原文中只给出设计的 diff --git a/gui-and-cli-principles/README.md b/gui-and-cli-principles/README.md index ff263f2..4111e4e 100644 --- a/gui-and-cli-principles/README.md +++ b/gui-and-cli-principles/README.md @@ -1,7 +1,7 @@ 原文链接:[Subversion UI Shootout](http://onlamp.com/pub/a/onlamp/2005/03/10/svn_uis.html "Subversion UI Shootout"),2005-03-10 译文发在:[【译】GUI & CLI Principles](http://oldratlee.com/post/2012-11-04/gui-cli-principles),2012-11-04 -### 🍎 译序 +### 🍎 译序 文章[Subversion UI Shootout](http://onlamp.com/pub/a/onlamp/2005/03/10/svn_uis.html "Subversion UI Shootout")比较了多个`GUI` `SVN`工具以及命令行的优劣。 @@ -15,11 +15,11 @@ PS: 交互思考有相通的之处,下面的几篇说了不错的话题,也可以看看: -* [大众点评移动客户端的“轻”点评模式](http://ifredric.me/post/2012-10-31/dianping_test_2 "大众点评移动客户端的“轻”点评模式") -* [Chrome 浏览器的哪些设计符合「Don't make me think」原则 — 知乎](http://www.zhihu.com/question/20564451 "Chrome 浏览器的哪些设计符合「Don't make me think」原则") -你可能不知道常用的`Chrome`有哪些贴心的地方哦~ -* [PC 用户的哪些行为让你当时就震惊了? — 知乎](http://www.zhihu.com/question/20100408 "PC 用户的哪些行为让你当时就震惊了?") -这篇里会有很多让你震惊却又会心一笑的回答。 +- [大众点评移动客户端的“轻”点评模式](http://ifredric.me/post/2012-10-31/dianping_test_2 "大众点评移动客户端的“轻”点评模式") +- [Chrome 浏览器的哪些设计符合「Don't make me think」原则 — 知乎](http://www.zhihu.com/question/20564451 "Chrome 浏览器的哪些设计符合「Don't make me think」原则") + 你可能不知道常用的`Chrome`有哪些贴心的地方哦~ +- [PC 用户的哪些行为让你当时就震惊了? — 知乎](http://www.zhihu.com/question/20100408 "PC 用户的哪些行为让你当时就震惊了?") + 这篇里会有很多让你震惊却又会心一笑的回答。 ![GUI vs. CLI](cli_ne_gui.jpg "GUI vs. CLI") diff --git a/how-to-ask-questions-the-smart-way/README.md b/how-to-ask-questions-the-smart-way/README.md index 83587b1..e8dcddc 100644 --- a/how-to-ask-questions-the-smart-way/README.md +++ b/how-to-ask-questions-the-smart-way/README.md @@ -6,7 +6,7 @@ 提问的智慧 ==================== -![](questions.jpg) +![questions](questions.jpg) **艾瑞克 · 史蒂文 · 雷蒙德(_Eric Steven Raymond_)** [Thyrsus Enterprises](http://www.catb.org/~esr/) @@ -20,7 +20,7 @@ 目录 ---------------- -- [译文](#译文) +- [译文](#译文) - [免责声明](#免责声明) - [引言](#引言) - [如何做提问前的准备](core.md#如何做提问前的准备) diff --git a/how-to-ask-questions-the-smart-way/core.md b/how-to-ask-questions-the-smart-way/core.md index 6f665b5..be9dac3 100644 --- a/how-to-ask-questions-the-smart-way/core.md +++ b/how-to-ask-questions-the-smart-way/core.md @@ -37,13 +37,13 @@ 在通过电邮、新闻组或论坛提技术问题以前,做以下事情: -1. 尝试在你准备提问论坛的历史文档中搜索答案 -2. 尝试搜索互联网以找到答案 -3. 尝试阅读手册以找到答案 -4. 尝试阅读『常见问题文档(`FAQ`)』以找到答案 -5. 尝试自己检查或试验以找到答案 -6. 尝试请教懂行的朋友以找到答案 -7. 如果你是程序员,尝试阅读源代码以找到答案 +1. 尝试在你准备提问论坛的历史文档中搜索答案 +2. 尝试搜索互联网以找到答案 +3. 尝试阅读手册以找到答案 +4. 尝试阅读『常见问题文档(`FAQ`)』以找到答案 +5. 尝试自己检查或试验以找到答案 +6. 尝试请教懂行的朋友以找到答案 +7. 如果你是程序员,尝试阅读源代码以找到答案 提问时,请先表明你已做了上述事情,这将有助于建立你不是寄生虫与浪费别人时间的印象。最好再表述你从中 **_学到的东西_** ,我们喜欢回答那些表现出能从答案中学习的人。 @@ -72,10 +72,10 @@ 要对在哪提问留心,如果你做了下述事情,多半会被一笔勾销或被看成『失败者』: -* 张贴与论坛主题无关的问题 -* 在面向高级技术问题的论坛上张贴肤浅的问题,反之亦然 -* 在太多不同的新闻组同时张贴 -* 给既非熟人也没有义务解决你问题的人发送你私人的电邮 +- 张贴与论坛主题无关的问题 +- 在面向高级技术问题的论坛上张贴肤浅的问题,反之亦然 +- 在太多不同的新闻组同时张贴 +- 给既非熟人也没有义务解决你问题的人发送你私人的电邮 为保护通信的渠道不被无关的东西淹没,黑客会除掉那些没有找对地方的问题,你不会想让这种事落到自己头上的。 @@ -135,10 +135,10 @@ 当某个项目存在开发者邮件列表时,要向列表而不是其中的个别成员提问,即使你确信他能最好地回答你的问题。查一查项目的文档和主页,找到项目的邮件列表并使用它。采用这种办法有几个很好的理由: -* 向一个开发者提的好问题,对整个项目组也会有益。反之,如果你认为自己的问题对整个项目组来说太愚蠢,就没有理由去去骚扰单个开发者。 -* 向列表提问可以分散开发者的负担,单个开发者(尤其是项目领导)也许太忙以至于没法回答你的问题。 -* 大多数邮件列表都要存档,那些存档将被搜索引擎索引,如果你向列表提问并得到解答,将来其它人可以通过网页搜索找到你的问题和答案,也就不用再次发问了。 -* 如果某些问题经常被问到,开发者可以利用此信息改进文档或软件本身,以使其更清楚。如果只是私下提问,就没有人能看到最常见问题的完整场景。 +- 向一个开发者提的好问题,对整个项目组也会有益。反之,如果你认为自己的问题对整个项目组来说太愚蠢,就没有理由去去骚扰单个开发者。 +- 向列表提问可以分散开发者的负担,单个开发者(尤其是项目领导)也许太忙以至于没法回答你的问题。 +- 大多数邮件列表都要存档,那些存档将被搜索引擎索引,如果你向列表提问并得到解答,将来其它人可以通过网页搜索找到你的问题和答案,也就不用再次发问了。 +- 如果某些问题经常被问到,开发者可以利用此信息改进文档或软件本身,以使其更清楚。如果只是私下提问,就没有人能看到最常见问题的完整场景。 如果一个项目既有 『用户』 也有『开发者』(或 『黑客』)邮件列表或论坛,而你又不摆弄那些代码,向『用户』列表或论坛提问。不要假设自己会在开发者列表中受到欢迎,那些人多半会遭受你的噪音干扰。 @@ -213,26 +213,26 @@ 如果你用英语书写但它是你的第二语言,最好提醒潜在的回复者语言上可能的困难以便绕过这个问题,比如: -* 英语不是我的母语,请谅解拼写错误。 -* 如果您使用某某语言,请电邮/私聊我,也许我需要您的协助翻译我的问题。 -* 对于这个技术术语本身我很熟悉,但对于它的一些俚语或习惯表达方式就不太明白了。 -* 我已经同时用某某语及英语提问,如果您使用两者之一回复,我很乐意翻译。 +- 英语不是我的母语,请谅解拼写错误。 +- 如果您使用某某语言,请电邮/私聊我,也许我需要您的协助翻译我的问题。 +- 对于这个技术术语本身我很熟悉,但对于它的一些俚语或习惯表达方式就不太明白了。 +- 我已经同时用某某语及英语提问,如果您使用两者之一回复,我很乐意翻译。 使用易于读取且标准的文件格式发送问题 ------------------- 如果你人为地将问题搞得难以阅读,它多半会被忽略,人们更愿读易懂的问题,所以: -* 使用纯文本而不是`HTML`( [关闭`HTML`](http://www.birdhouse.org/etc/evilmail.html) 并不难) -* 使用`MIME`附件通常没有问题,前提是真正有内容(譬如附带的源文件或补丁),而不仅仅是邮件客户端程序生成的模板(譬如只是消息内容的拷贝)。 -* 不要发送整段只是单行句子但多次折回的邮件(这使得回复部分内容非常困难)。设想你的读者是在80个字符宽的文本终端阅读邮件,设置你的行折回点小于 80 列。 -* 但是,也 **_不要_** 用任何固定列折回数据(譬如日志文件拷贝或会话记录)。数据应该原样包含,使回复者确信他们看到的是与你看到的一样的东西。 -* 在英语论坛中,不要使用`MIME`可打印字符引用编码编码([`Quoted-Printable encoding`](https://zh.wikipedia.org/wiki/Quoted-printable))发送消息。这种编码对于张贴非`ASCII`语言可能是必须的,但很多邮件程序并不支持。 +- 使用纯文本而不是`HTML`( [关闭`HTML`](http://www.birdhouse.org/etc/evilmail.html) 并不难) +- 使用`MIME`附件通常没有问题,前提是真正有内容(譬如附带的源文件或补丁),而不仅仅是邮件客户端程序生成的模板(譬如只是消息内容的拷贝)。 +- 不要发送整段只是单行句子但多次折回的邮件(这使得回复部分内容非常困难)。设想你的读者是在80个字符宽的文本终端阅读邮件,设置你的行折回点小于 80 列。 +- 但是,也 **_不要_** 用任何固定列折回数据(譬如日志文件拷贝或会话记录)。数据应该原样包含,使回复者确信他们看到的是与你看到的一样的东西。 +- 在英语论坛中,不要使用`MIME`可打印字符引用编码编码([`Quoted-Printable encoding`](https://zh.wikipedia.org/wiki/Quoted-printable))发送消息。这种编码对于张贴非`ASCII`语言可能是必须的,但很多邮件程序并不支持。 当它们分断时,那些文本中四处散布的 『`=20`』符号既难看也分散注意力,甚至有可能破坏内容的语意。 -* **_永远不要_** 指望黑客们阅读使用封闭的专用格式编写的文档,诸如微软公司的`Word`或`Excel`文件等。 +- **_永远不要_** 指望黑客们阅读使用封闭的专用格式编写的文档,诸如微软公司的`Word`或`Excel`文件等。 大多数黑客对此的反应就像有人将还在冒热气的猪粪倒在你门口时你的反应一样。即使他们能够处理,也很厌恶这么做。 -* 如果你从使用视窗的电脑发送电子邮件,关闭问题颇多的微软『聪明引用』功能(在『工具』 -> 『自动纠正选项』的『输入时自动格式化』下去掉聪明引用的选框),以免在你的邮件中到处散布垃圾字符。 -* 在论坛,勿滥用『表情符号』和『`HTML`』功能(当它们提供时)。一两个表情符号通常没有问题,但花哨的彩色文本倾向于使人认为你是个无能之辈。过滥地使用表情符号、色彩和字体会使你看来像个傻笑的小姑娘。 +- 如果你从使用视窗的电脑发送电子邮件,关闭问题颇多的微软『聪明引用』功能(在『工具』 -> 『自动纠正选项』的『输入时自动格式化』下去掉聪明引用的选框),以免在你的邮件中到处散布垃圾字符。 +- 在论坛,勿滥用『表情符号』和『`HTML`』功能(当它们提供时)。一两个表情符号通常没有问题,但花哨的彩色文本倾向于使人认为你是个无能之辈。过滥地使用表情符号、色彩和字体会使你看来像个傻笑的小姑娘。 这通常不是个好主意,除非你只是对性而不是有用的回复更有兴趣。 如果你使用图形用户界面的邮件客户端程序(如网景公司的`Messenger`、微软公司的`Outlook`或者其它类似的),注意它们的缺省配置不一定满足这些要求。 @@ -241,12 +241,12 @@ 描述问题应准确且有内容 ------------------- -* 仔细、清楚地描述问题的症状 -* 描述问题发生的环境(主机、操作系统、应用程序,任何相关的),提供销售商的发行版和版本号(如:『`Fedora Core 7`』、『`Slackware 9.1`』等) -* 描述提问前做过的研究及其理解。 -* 描述提问前为确定问题而采取的诊断步骤。 -* 描述最近对计算机或软件配置的任何相关改变。 -* 如果可能,提供在可控环境下重现问题的方法。 +- 仔细、清楚地描述问题的症状 +- 描述问题发生的环境(主机、操作系统、应用程序,任何相关的),提供销售商的发行版和版本号(如:『`Fedora Core 7`』、『`Slackware 9.1`』等) +- 描述提问前做过的研究及其理解。 +- 描述提问前为确定问题而采取的诊断步骤。 +- 描述最近对计算机或软件配置的任何相关改变。 +- 如果可能,提供在可控环境下重现问题的方法。 尽最大努力预测黑客会提到的问题,并提前备好答案。 diff --git a/how-to-ask-questions-the-smart-way/others.md b/how-to-ask-questions-the-smart-way/others.md index a3b68bb..61efa42 100644 --- a/how-to-ask-questions-the-smart-way/others.md +++ b/how-to-ask-questions-the-smart-way/others.md @@ -78,9 +78,10 @@ 我的{程序、配置、`SQL`语句}不运行了 **答:** 这不是一个问题,我也没有兴趣去猜你有什么问题 —— 我有更要紧的事要做。看到这种东西,我的反应一般如下: -* 你还有什么补充吗? -* 噢,太糟了,希望你能搞定。 -* 这跟我究竟有什么关系? + +- 你还有什么补充吗? +- 噢,太糟了,希望你能搞定。 +- 这跟我究竟有什么关系? **问:** diff --git a/overlapping-experiment-infrastructure-more-better-faster-experimentation/README.md b/overlapping-experiment-infrastructure-more-better-faster-experimentation/README.md index d5143b6..0aeb302 100644 --- a/overlapping-experiment-infrastructure-more-better-faster-experimentation/README.md +++ b/overlapping-experiment-infrastructure-more-better-faster-experimentation/README.md @@ -42,9 +42,9 @@ 设计我们实验设施的目标是:_更多_、_更好_、_更快_。 -* **更多**:我们需要能同时进行多个实验的可扩展性。但是我们也需要灵活性:不同的实验需要不同的配置和不同的流量来衡量实验的统计意义上的效果显著性。有些实验只需要修改流量的一个子集,比如只是日语的流量,并需要取一个合理的流量规模。其它的实验有可能需要修改所有的流量,并对指标造成很大影响,这种才可以在小流量上进行测试。 -* **更好**:不合理的实验是不应该让它在线上流量进行的。合理的但是很差的实验(比如,有`Bug`的实验或是无意中产生的很差的实验结果)都应该能很快的被捕获并且停止它的进行。标准化的标价指标可以让所有的实验进行公平的比较:比如在计算`CTR`指标的时间,两个实验应该用相同的过滤器去掉爬虫流量。 -* **更快**:能够很容易并且很快地建立一个实验。容易到非工程师不需要写代码就可以创建一个实验。评价指标应该很快的被统计出来,以便分析。简单的迭代可以很快速地进行。理想状态是,实验系统不仅支持实验,并且可以控制放量,比如,以一种系统的和容易理解的方式对实验进行放量。 +- **更多**:我们需要能同时进行多个实验的可扩展性。但是我们也需要灵活性:不同的实验需要不同的配置和不同的流量来衡量实验的统计意义上的效果显著性。有些实验只需要修改流量的一个子集,比如只是日语的流量,并需要取一个合理的流量规模。其它的实验有可能需要修改所有的流量,并对指标造成很大影响,这种才可以在小流量上进行测试。 +- **更好**:不合理的实验是不应该让它在线上流量进行的。合理的但是很差的实验(比如,有`Bug`的实验或是无意中产生的很差的实验结果)都应该能很快的被捕获并且停止它的进行。标准化的标价指标可以让所有的实验进行公平的比较:比如在计算`CTR`指标的时间,两个实验应该用相同的过滤器去掉爬虫流量。 +- **更快**:能够很容易并且很快地建立一个实验。容易到非工程师不需要写代码就可以创建一个实验。评价指标应该很快的被统计出来,以便分析。简单的迭代可以很快速地进行。理想状态是,实验系统不仅支持实验,并且可以控制放量,比如,以一种系统的和容易理解的方式对实验进行放量。 为了达到这些设计的目标,我们不仅需要实验设施来进行更多的实验,并且需要一些工具和指导过程来支持更多和更快的实验。 @@ -85,14 +85,14 @@ 事实上,我们设计的更加灵活,我们不止是将参数划分子集,再将子集与层相关联。为了解释灵活性,我们引入了一些定义。在流量和系统参数的语境下,我们有三个关键的概念: -* **域**是指流量的一个划分(一部分流量的意思)。 -* **层**是指系统参数的一个子集。 -* **实验**是指在一个流量划分上,进行零个或多个参数的修改,并最后改变请求处理的过程。 +- **域**是指流量的一个划分(一部分流量的意思)。 +- **层**是指系统参数的一个子集。 +- **实验**是指在一个流量划分上,进行零个或多个参数的修改,并最后改变请求处理的过程。 域和层可以相互嵌套。域中包含层。层中包含实验,层中也可以包含域。在一个层中嵌套域可以使这一层中的参数在嵌套域中进行进一步划分。开始时,我们有默认的域和层,它有包含所有的流量和参数,在默认域和层中,比如我们可以: -* 简单地将参数分为三层(图2a),这种情况下,每个请求最多只会同时在三个实验中,每层一个,每个实验只能修改相应层的参数。 -* 我们可以先将流量分为两个域,一个域只有一个单一层(非重叠域),和一个有三个层的重叠域(见图2b),在这种情况下,每个请求会分到非重叠域或是重叠域。请求只能在非重叠域或重叠域其中之一。如果请求在重叠域,那么请求最多在一个实验中(这个实验可以改变参数集合中的任意参数的值),如果请求在重叠域,那么请求最多在三个实验中,每层一个实验。并且对于每个实验,只能使用对应层的参数。 +- 简单地将参数分为三层(图2a),这种情况下,每个请求最多只会同时在三个实验中,每层一个,每个实验只能修改相应层的参数。 +- 我们可以先将流量分为两个域,一个域只有一个单一层(非重叠域),和一个有三个层的重叠域(见图2b),在这种情况下,每个请求会分到非重叠域或是重叠域。请求只能在非重叠域或重叠域其中之一。如果请求在重叠域,那么请求最多在一个实验中(这个实验可以改变参数集合中的任意参数的值),如果请求在重叠域,那么请求最多在三个实验中,每层一个实验。并且对于每个实验,只能使用对应层的参数。 @@ -106,9 +106,9 @@ 另一个概念是**发布层**(`Launch layers`),发布层与前面介绍的实验层有下面区别: -* 发布层总是在默认域中(比如,它们有全部流量)。 -* 发布层是对参数的一个独立划分,比如,一个参数最多只能同时在一个发布层和最多一个正常层中(一个域中)。 -* 为了让发布层和正常层的重复参数配合起来。在发布层中的实验有着稍有不同的语法。特别是,在发布层的实验会为覆盖参数的默认值,作为新的默认值,换言之,如果没有正常实验层的实验覆盖了默认参数,那么在发布层的行为就像一个普通的实验,但如果实验层的实验覆盖了默认值,那么实验就会用这个覆盖的值,而不是系统的默认值,或是发布层实验中的参数值。 +- 发布层总是在默认域中(比如,它们有全部流量)。 +- 发布层是对参数的一个独立划分,比如,一个参数最多只能同时在一个发布层和最多一个正常层中(一个域中)。 +- 为了让发布层和正常层的重复参数配合起来。在发布层中的实验有着稍有不同的语法。特别是,在发布层的实验会为覆盖参数的默认值,作为新的默认值,换言之,如果没有正常实验层的实验覆盖了默认参数,那么在发布层的行为就像一个普通的实验,但如果实验层的实验覆盖了默认值,那么实验就会用这个覆盖的值,而不是系统的默认值,或是发布层实验中的参数值。 发布层的示例在图2c、2d中,通过发布层,我们能以一种标准通用的方式逐步灰度最终全量一个实验策略,且可以跟踪灰度过程中实验效果变化。 通常情况下,每有一个新特性要开始全量时都需要新建一个发布层,当这个新特性最终完成全量时,再将相应的发布层删除。并且因为发布层实验的流量一般都比较大,所以它们可以用于测试特性之间的相互影响,虽然理论上我们可以测试正常实验层的特性相互影响(比如,如果参数在同一层,我们可以手工设置创建实验,如果参数在不同层,我们观察实验的交集流量),但因为在正常层中,实验流量比较少,交集比较小,所以相互影响很难检测。 @@ -129,11 +129,11 @@ 在这个设施下,一个特性的评估和发布过程是类似如下过程的: -* 在合适的模块中,实现新的特性(包括`code review`、二进制推送、设置默认参数等等,和标准的工程实践一样)。 -* 创建一个灰度实验(通过数据推送方式),以保证特性可以正常工作,如果不能正常工作,那么可能就要重写代码修复这个问题。 -* 创建一个实验或是一组实验(通过数据推送的方式)来评估特性。注意配置实验涉及指定分配类型和相关的分配参数(比如:`cookie`取模),分配条件,和特性相关的参数。 -* 评估实验指标。根据实验结果,判断是否要进行新一轮的实验,即通过修改或创建新的实验,或甚至修改代码从根本上改变特性。 -* 如果特性可以发布,就进入发布过程:创建一个新的发布层和发布层实验,逐步的放量这个实验,并最终删除发布完的发布层,然后将发布层实验的相关参数设为系统默认参数。 +- 在合适的模块中,实现新的特性(包括`code review`、二进制推送、设置默认参数等等,和标准的工程实践一样)。 +- 创建一个灰度实验(通过数据推送方式),以保证特性可以正常工作,如果不能正常工作,那么可能就要重写代码修复这个问题。 +- 创建一个实验或是一组实验(通过数据推送的方式)来评估特性。注意配置实验涉及指定分配类型和相关的分配参数(比如:`cookie`取模),分配条件,和特性相关的参数。 +- 评估实验指标。根据实验结果,判断是否要进行新一轮的实验,即通过修改或创建新的实验,或甚至修改代码从根本上改变特性。 +- 如果特性可以发布,就进入发布过程:创建一个新的发布层和发布层实验,逐步的放量这个实验,并最终删除发布完的发布层,然后将发布层实验的相关参数设为系统默认参数。 # 5. 工具与流程 @@ -141,8 +141,8 @@ ## 5.1 工具 -* **数据文件检查:** 数据文件的其一优势是它们可以被自动检查错误,这可以避免一些不合理的实验运行。我们会自动检查法语错误(所有的必填字段都有并且合法),一致性和约束错误(比如,`id`的唯一性,根据所有的参数判断是否实验在正确的层,是否这一层有足够的流量来支持实验,流量约束检查,如果实验要求的流量已经被另一个实验使用了,等等,注意当可用的分配条件集合变大时,这些检查就变的复杂了),和基本的实验设置检查(是否实验有对比实验,并且对比实验在相同的层,是否对比实验与实验的流量分配方式和规模一致,等等)。 -* **实时监控:** 我们用实时监控来检测基本的指标(比如`CTR`),我们通过实时监控尽快地发现某个实验是不正常的,实验者可以设置监控指标的期望值区间(也有这些指标的默认波动区间),如果监控指标超出了期望的波动区间,那么会触发自动告警,然后实验者可以修改期望区间或停止他们的实验,或调整它们的实验参数值,但它允许实验者可以激进地对于可能的潜在的变化进行测试,因为错误或预期之外的影响会被很快检测到。 +- **数据文件检查:** 数据文件的其一优势是它们可以被自动检查错误,这可以避免一些不合理的实验运行。我们会自动检查法语错误(所有的必填字段都有并且合法),一致性和约束错误(比如,`id`的唯一性,根据所有的参数判断是否实验在正确的层,是否这一层有足够的流量来支持实验,流量约束检查,如果实验要求的流量已经被另一个实验使用了,等等,注意当可用的分配条件集合变大时,这些检查就变的复杂了),和基本的实验设置检查(是否实验有对比实验,并且对比实验在相同的层,是否对比实验与实验的流量分配方式和规模一致,等等)。 +- **实时监控:** 我们用实时监控来检测基本的指标(比如`CTR`),我们通过实时监控尽快地发现某个实验是不正常的,实验者可以设置监控指标的期望值区间(也有这些指标的默认波动区间),如果监控指标超出了期望的波动区间,那么会触发自动告警,然后实验者可以修改期望区间或停止他们的实验,或调整它们的实验参数值,但它允许实验者可以激进地对于可能的潜在的变化进行测试,因为错误或预期之外的影响会被很快检测到。 ## 5.2 实验设计与样本量 @@ -158,9 +158,9 @@ 在工程实践中,我们主要关注 \text{queries}_{\text{control}} ,但要通过 N 个和才能影响相关的实验指标,为了正确确定 N 值,我们需要知道: -* 实验所关注的指标是什么。 -* 对每个指标,我们想检测实验改变的敏感度(\theta)值是什么。比如,实验者想检测到2%的点击率变化。 -* 对每个指标,一个抽样单元(N=1)样本标准误差是(s),实验大小为 N 的标准误差为 s/\sqrt{N}。 +- 实验所关注的指标是什么。 +- 对每个指标,我们想检测实验改变的敏感度(\theta)值是什么。比如,实验者想检测到2%的点击率变化。 +- 对每个指标,一个抽样单元(N=1)样本标准误差是(s),实验大小为 N 的标准误差为 s/\sqrt{N}。 _Kohavi_ 假设实验与对照实验有相同的大小,比如 \text{queries}_{\text{experiment}}=2N ,那么必须 2N 大于等于 16(s/\theta)^2 才能满足最小变化检测需求,16这个值是由置信度( 1-\alpha ,通常为95%)和期望的统计功效( 1-\beta ,通常为80%)决定的。 @@ -196,10 +196,10 @@ _Kohavi_ 假设实验与对照实验有相同的大小,比如 【译注】:斯瓦希里语是当今非洲最常用的语言之一。 @@ -282,14 +282,14 @@ _Kohavi_ 假设实验与对照实验有相同的大小,比如