暖气片的故事和科学思维

谢天谢地,这几天北京终于开始供暖气啦!从寒冷的室外,打开一扇门,就可以回到温暖的家,这是南方人的我最喜欢北京的地方了。在温暖的房间里,把厚被子换成薄被子,感觉再也不用瑟瑟发抖了。

然而走进次卧的瞬间,我感觉到了一丝凉意。难道暖气片坏了?摸了一摸,发现没有热气。这可不行,次卧是 Jojo 和阿姨睡觉的地方,难怪 Jojo 这几天手脚冰冷的,也没睡好。

「打电话给暖气公司报修一下吧!」这是我的第一反应。但是阿姨阻止了我:「不用的,我以前也遇到过,热的比较慢,过一个星期就好了」。 「是吗?」有可能哦,可能就是这么设计的?让暖气慢慢热透整个暖气片?那就过几天再看看吧!

第二天,我又到次卧。还是一样的冷。不过暖气片有进步,热了两片,上次才一片。看来阿姨说的慢慢热起来,也是真的。 第三天的时候,还是冷,暖气片还只热了两片。

「到底是怎么回事呢?」
——「就是这样的,过一星期就好了。」
「那是为什么呢?为什么比别的房间慢呢?」
——「有的暖气片就是这样的。」

emmmm,但总有个原因的吧?

我决定在网上查一下。打开 Google ,输入「暖气片只有几片热」,按下回车。 果然,有人也遇到同样的问题!据说是因为暖气片里面有空气,打开排气阀,排掉就好了。Soga!

我有心理准备,这个暖气片不是好对付的。白色喷漆覆盖了整个暖气片。我小心翼翼地刮开排气阀上的漆,让他露出来。果然不是省油的灯,螺丝盖已经成了一个圆形,拧了半天纹丝不动。我只好用一个老虎钳死死夹住唯一有点平的一处,用蛮力去转。突然「咝——」的一声,空气出来了!太棒啦!不一会儿,水汽也跟着出来了。等到水往下滴的时候,我拧回了螺丝。

接下来就是检验成果的时候了,已经没有什么悬念:暖气片慢慢地热起来了,三片、四片……慢慢就热起来了。带着工具和骄傲,我开心地离开了次卧。

科学思维

多年的职业生涯,训练了我很多习惯。其中一个是「科学思维」:

如果一个问题,我们不知道背后的原因,说明我们对她还没有的正确认知。

在没有正确认知的情况下去解决问题,会事倍功半,甚至无功而返。

很多人遇到小宝宝哭,不分状况就抱起来晃,或者抱着到处走,转移注意力。这可以作为安抚的方法先应用,但倘若不去细听哭声的层次,对哭声背后的需求不去探究,这些临时的安抚措施往往都会失效,小宝宝会因为需求得不到满足而感到沮丧,哭得更厉害。

盲目去解决问题而不去探索原因,对我们自己的成长没有益处,有可能还是有害的。

我的工作 —— 程序员 —— 主要的工作内容就是解决问题。大家每天都在解决问题,但是不同的人成长速度还是不一样。成长快的人,解决问题的时候会想方设法了解问题产生的原因,从一个点切入,了解整个系统的知识。

why it works

说到这里,我就想起了另外一个故事。

瓷器的故事

我们日常生活中经常用到瓷器,我们都知道 China 既是中国也是瓷器。瓷器硬度、光滑度都不错,中国历代都不断改良,明朝时达到巅峰。然而大部分人包括我以前,都不知道,其实现代的瓷器却不是中国人发明的。中国的瓷器传承几百年,有一些工艺的沉淀,依照一定的方式去制作,就有可能做出好的瓷器。但成品率一直很低。即便在清朝供应宫廷使用的官窑,也很难保证产量。在以前,家里有瓷器的非富即贵。欧洲皇室疯狂追求瓷器,只能向中国进口。而我们今天,谁家里没有几个瓷碗呢?

瓷器的工艺一直都作为商业机密口口相传,限制在小范围内。传递过程中越来越玄学了,有些做法不知道原因,但是就是管用。比如周杰伦的《青花瓷》唱道「天青色等烟雨」,指的是天将雨未雨的时候,此时最适合制作青花瓷,但是不是空气湿度的原因?我们不知道,照做就行,等着这样的天气吧。

中国垄断了近千年的瓷器,产生了巨额的利益。欧洲人也希望能掌握这样的技术,用尽各种方法试图破解瓷器的秘密,其中以德国的故事最为曲折。

理性而执拗的德国人信赖科技兴邦,但在破译瓷器密码这件事儿上,他们依靠的是王权、科学和颇带投机色彩的炼金术士三位一体的强力组合。

1694年,奥古斯都王子即位成为萨克森选帝侯。奥古斯都恋瓷成癖,他拥有的瓷器达到35,798件,是西方世界最大规模的瓷器收藏。他批量购买康熙瓷,购买大盖罐、瓷盘、花瓶,总之就是景德镇生产的一切器物。当然了,都是通过荷兰的东印度公司购买的,付钱如流水。

当时德国有个哲学家、数学家——契恩豪斯。对他来说,瓷器是个值得认真研究的课题:白瓷不是天然存在的物质,需要经过化学反应的淬炼(这个神奇过程的奥秘已让无数英雄在实验室中折戟),而最终成型的这种坚硬的白色固体,居然可以让光穿透。契恩豪斯是个务实的人,他花了二十年时间观察玻璃作坊和彩陶工厂的操作,见识过那些配制、提炼和做化验检测的人。作为一个数学家,他能够看到规律如何呈现;也懂得钻研问题需要花时间,测试各种可能的排列组合。在国王的实验室,契恩豪斯日复一日锲而不舍地测试和研制瓷的配方。投入试验的矿物质价格不菲,打开窑炉和坩埚,只见又是一块烧结的疙瘩,又一个妙点子化为硬邦邦的废物,这种生活一定十分煎熬。他工作非常刻苦,每天站在巨大的燃烧镜前检测矿物质,毁掉了他的视力。

多次失败之后,契恩豪斯遇到了国王的炼金师波特格。波特格19岁时“在可靠的目击证人面前炼成了黄金”,自那之后争夺这个男孩的人“比争夺特洛伊美女海伦的人还要多”。他从柏林逃到了萨克森,在激烈的挣扎后被逮捕,随后被奥古斯都秘密关押。波特格没能给国王炼出金子,但契恩豪斯察觉到这个年轻的术士身上有一种化学的悟性,对物质具有直觉的感受力,也会走捷径。

数学家与炼金术士一起在“宛如地狱”的地下室工作了六年。温度高达几千摄氏度的灼热窑炉是个巨大的潜在危险。“他们每隔半个小时就看一下窑炉,像看护牲口似的,红红的火光让人赶紧跳开。里面太热了,他们的头发被烧焦,地板滚烫,他们脚上起了大水泡。”波特格活像“扫烟囱的人”,他的脑袋上缠着打湿的破布条。

在现存的波特格笔记本的一页上记载着1708年1月一次实验的情况,这次烧制测试了七种配方,按照不同的比例把某地出产的一种白色黏土与雪花石膏混合起来,每种编号下标明了具体的配比。德国人在严谨的反复实验上加乘了两倍甚至三倍于常人的努力,终于将想法变成现实。

1708年4月,奥古斯都签署法令并加盖印章,在德累斯顿建立了第一座瓷厂,这也是西方第一座瓷器制造厂。10月9日,契恩豪斯和波特格烧造了第一只真正的无釉瓷杯,第一只半透明白色器皿。德国至此在西方世界率先破解了秘术的玄机,拥有了制作瓷器的知识。这种全新的白色物质被称为Böttgerporzellan,“波特格瓷”。 德国人在麦森兴建了一座新工厂,生产由秘术师波特格发明的白瓷。麦森瓷成为御用精品,三百年来历久不衰且愈发精美贵重,至今依然是蜚声国际的顶级名瓷。

欧洲人擅长定量分析和比较试验,并且注重保留全部原始数据和实验报告,不仅代表了实验的严谨性,也能让后续人不用重新造车而直接受益。 目前欧洲人占据高端瓷器市场的 90%,其余被美国和日本瓜分,不仅令人唏嘘。

承认自己的无知,尊重实验,构成了科学思维。

如果回答不出「为什么」,就是无知。这个无知并无贬义。无知并不可耻,无知却故作有知才可耻。先假设一个原因,然后设计实验,通过严谨的实验,验证这个假设,如此,我们才获得了认知。

参考:

从新手到专家——技能成长模型

专家的特征

2009年1月15日,Sullenberger (也叫萨利) 在全美航空1549号班担任机长,飞机从纽约拉瓜迪亚机场起飞。五分钟后,在爬升过程中遭到飞鸟撞击,两架发动机全部熄火。此时只能就近寻求迫降,但高度不够,在没有动力的情况下,很难抵达附近的机场,如果处置不当,也很容易引起地面的巨大伤亡。危急时刻,萨利决定在哈德逊河上迫降,155人 全数生还

从起飞到迫降,短短六分钟。萨利凭借自己的经验、技术和准确的判断,拯救了机上所有人的生命。电影《萨利机长》还原了整个过程。在感慨紧张的英雄事迹之余,我们有幸看到一位真正的飞行专家是怎样行事的。

1

失去动力之后,仪表显示发动机损坏一个,另一个处于正常状态。然而萨利根据声音、气味等细节,确定是鸟袭,并且断定两个引擎均已损坏。这个判断非常关键,直接关系到是否能及时返航。如果还有动力,就可以回到拉瓜迪亚机场了。 专家重视细节,不盲从二手信息。

2

确定动力失灵后,萨利立即下意识地打开了辅助动力系统 (APU) ,然后让副机长杰夫拿出飞行手册,按手册指引操作。依据飞行手册,出现飞鸟撞击事故时,其中第15步是打开 APU,然而萨利却直接跳过了前面的步骤,直接打开 APU。如果没有 启动 APU,飞机将不能顺利滑翔。没有这一举动,迫降就不可能成功。此外,手册有三页长,到迫降时也只进行了一页半。如果完全遵循手册逐条进行,恐怕结局已经是机毁人亡了。专家重视情境,不盲从流程。流程甚至会阻碍专家行事。

3

与塔台沟通后,飞机掉头返航。塔台清除了机场的跑道提供迫降,但萨利回复道「unable」,因为此时飞机动力不足,是无法抵达出发机场的。随后塔台安排了附近另一个机场。返航过程中,萨利意识到仍然无法抵达动,只能迫降在水面上了,于是当机立断,迫降到哈德逊河上。不了解航空史的人可能不知道,水面迫降是几乎不可能成功的。在此之前,还没有大型客机水面迫降成功的案例。萨利机长在事后接受采访时,提到自己有非常大的把握能控制飞机,确信自己能做到水面降落。这样的自信,需要丰富的知识、经验。这离不开萨利机长的成长经历。

萨利 5 岁就知道自己想做一个飞行员,11 岁就读了所有能找到的跟飞行有关的书。16岁,萨利就跟着一个开农用飞机的驾驶员库克学习。之后萨利在美国空军学院学习了驾驶滑翔机,并在空军服役。退役后,他加入了太平洋西南航空(后并入美国航空)担任民航飞行员。在2009年的时候,他已经差不多有30年的民航驾驶经验了。滑翔机的学习经验非常重要,飞机在高空失去动力,也就相当于一架十几吨重的滑翔机。无独有偶,史上最成功的几次迫降事件,驾驶员都有滑翔机驾驶经验。 专家有丰富的知识积累。

4

萨利本身也是 NTSB (美国航空安全委员会)的调查员,在 2009 年之前就协助 NTSB 调查航空事故,访问空难家属。同时,他还会收集各种飞行事故的调查报告。这些研究对这次的迫降非常重要。飞机要在水上迫降成功,需要几个要点:

  • 水上迫降时应收起起落架
  • 飞机近水时的速度越小越好
  • 机翼的襟翼应在水下位置接水更有利

这些知识,正是从之前的空难调查报告中总结出来的。在这次事件中,帮助萨利做了正确的选择。专家会不断反思,从(自己或他人的)不足中总结。

5

我必须压抑本能的激动反应,来挽救机上的每一条人命。我很快思考各种可能性,然后选择唯一的可行方案。—— 萨利

除了大量的知识储备,危急关头的冷静和直觉,让萨利得以快速反应,在短时间内做出正确选择。 专家依赖直觉。

飞行当然是一种技能。但技能在我们的生活、工作中无处不在。 我们的生活中需要各种各样的技能,例如做饭,与人沟通,写程序,养育小孩等等。这些事情都需要我们具备一定的知识,并有运用这些知识的技巧。以编程为例,作为一个程序员,首先要学会产品经理的语言,能读懂需求文档,知道常见的一些需求实现方案,并将已知的方案组合,实现产品的需求;其次要学会计算机的语言,将方案转化成计算机程序,并且要留意工程性,写出代码还需要关注可维护性和性能。

三百六十行,行行出状元。总有一些大师、专家涌现在我们的视野中。即便没有那么遥远,我们的身边也不乏一些更会做菜的朋友、更会和宝宝交流的爸爸妈妈。他们是如何成长的?他们的成长经历了哪些阶段,不同的阶段又是如何跨越的呢?

技能提升的五个阶段

1980年,德雷弗斯兄弟(Dreyfus Stuart E, Dreyfus, Hubert L) 发表了一篇论文《A Five-Stage Model of the Mental Activities Involved in Directed Skill Acquisition》 (直译:直接技能的获取相关的五阶段精神活动模型),提出了技能成长的五个重要阶段。我在《程序员的思维修炼》这本书中第一次接触到这个模型,感觉茅塞顿开。这个模型套用到其他领域,也非常适用,很好地指导了我在各方面的成长,也让我在新接触新领域的时候,能知道如何更快提升自己。所以我决定把这个模型分享给更多的人。

好了,我们先来看看这个模型。

I. 新手

新手需要指令才能工作。这个指令需要明确地指导他,遇到情况 A 如何应对,遇到情况 B 又如何应对。比如煎荷包蛋时,加多少油,什么时候放鸡蛋。

新手和专家之间沟通比较困难。新手描述事情的方式往往简单,缺乏必要的信息,专家不敢轻易下结论;而专家给的建议往往是考虑全面,涉及了不同的情境,并且会给出更底层的原则,而这种原则容易让新手忽略情境盲目遵从,或者无法在需要的时候想起来。

II. 高级新手

不愿全盘思考。统计资料显示,多数人落在这个层级;当管理阶层分配工作给高级新手,他们认为每项工作一样重要,不明了优先层度,意味着他们无法认知每件工作的相关性。因此管理者认清,工作需给高级新手时,必须排列优先级。

高级新手已经脱离手册的帮助,能够独立完成项目了。但还缺乏全局观,表现为:不知道为什么要这样做,背后的原因是什么(知识广度和深度都不支持自己得出答案);也不知道这件事在全局中的意义,如这个项目在公司的战略意义,对用户又能提供什么价值。

另外,高级新手还只能解决已经解决过的问题,对于自己没有遇到过的问题,他们还是一筹莫展,通常他们会询问专家。

III. 胜任者

能解决问题,不管是已经解决过的,还是首次遇到的。他们知道在哪里能得到需要的信息。

IV. 精通者

  • 技能上:反思 — 能认知自己的技能与他人差异,能透过观察别人去认知自己的错误,形成比新手更快的学习速度。
  • 职位上:全局观 — 能明确知道自己的职位在整体系统上的位。

反思的特征让人的技能提升开始加速。所以精通者更接近初级专家,而不是高级胜任者。

V. 专家

专家凭直觉工作。专家是各个领域知识和信息的主要来源。他们总是不断地寻找更好的方法和方式去做事。他们有丰富的经验,可以在恰当的情境中选取和应用这些经验。他们著书、写文章、做巡回演讲。他们是当代的巫师。

总结

德雷弗斯技能模型指出了技能水平的不同阶段,不仅仅是知识的差异,更是思维模式的差异。对我来说,我知道了:

  • 当我新学习一个技能的时候,应该寻求情景无关的指令,快速入门;
  • 当我已经入门后,需要不断深挖,知道原理;
  • 当我需要进一步提升时,需要通过观察他人,反思自己;
  • 当我想成为专家时,我需要刻意练习,让自己形成直觉。

这里引出了 「刻意练习」 这个非常重要的概念,他是成为专家的唯一路径。下次我再写一偏,和大家交流刻意练习。

参考

Elixir 源码阅读笔记: Ex_doc

源码地址:https://github.com/elixir-lang/ex_doc ex_doc 是 elixir 代码生成文档的工具。

生成文档的方式有很多种:

  1. 利用语法解析分析源码;
  2. 利用语言提供的反射能力分析;

Elixir 提供的反射功能非常全面,利用 Code 模块提供的一些方法,可以获得目标模块的很多信息。

文档也是其中一种。例如我们有这样的一个模块:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
defmodule Example do

  @moduledoc """
  Example module for `ex_doc`.

  ## Examples

  ``\`
  iex> Example.hello
  :world
  ``\`
  """

  @doc """
  hello world!
  """
  def hello do
    :world
  end

end

我们可以通过 Code.get_docs/2 获得模块的文档。

1
2
3
4
5
iex> Code.get_docs Example, :all
[docs: [{ {:hello, 0}, 14, :def, [], "hello world!\n"}],
 moduledoc: {3,
  "Example module for `ex_doc`.\n\n## Examples\n\n```\niex> Example.hello\n:world\n```\n"},
 callback_docs: [], type_docs: []]

可以看到,Elixir 已经在模块的信息中保存了相关的文档信息。ExDoc 接下来做的是提取出来,并进行格式转换。其中用到非常多的反射的接口,大部分都在 Code 模块中提供了。

Daily Reading Note #1

这里记录了每天阅读代码记录的新知识。

List.keymember?/3

用来检查 Keyword List 中有没有对应的 key / value:

1
2
3
4
5
6
iex> List.keymember?([a: 1, b: 2], :a, 0)
true
iex> List.keymember?([a: 1, b: 2], 2, 1)
true
iex> List.keymember?([a: 1, b: 2], :c, 0)
false

:code.where_is_file/1

可以从 load_paths 中找到 beam 文件

1
2
iex> :code.where_is_file('Elixir.Kernel.beam')
'/usr/local/bin/../lib/elixir/ebin/Elixir.Kernel.beam'

Code.prepend_path/1

将一个路径加入 Elixir 的 load_paths

Code.eval_string/3

执行一段代码,并返回结果,以及结果 binding

1
2
iex> Code.eval_string "b = a + 1", [a: 1], file: __ENV__.file, line: __ENV__.line
{2, [a: 1, b: 2]}

Keyword.get_values/2

获取关键字列表中某个 key 对应的所有值,很适合用来获取参数

1
2
iex> Keyword.get_values [path: "a", path: "b/c"], :path
["a", "b/c"]

:binary.last/1String.last/1

获取字符串的最后一个字符

Kernel.struct/2

适合留下 struct 合法的 key,丢弃不合法的 key

1
2
3
4
5
iex> defmodule User do
...>  defstruct name: nil
...> end
...> struct %User{name: "qhwa"}, %{name: "Billy", age: 10}
%User{name: "Billy"}

Code.ensure_loaded?/1

可以判断一个 Module 是否已经加载进来了

Path.wildcard/1

等于 Ruby 的 Dir.glob

Path.basename/2

和 Ruby 一样,可以用来去掉扩展名

Kernel.function_exported?/3

判断一个模块是否暴露了方法名

1
2
iex> function_exported? Kernel, :struct, 2
true

Code.get_docs/2

如果编译时有 --docs 选项(默认是有的),可以从模块中得到文档。返回 {line, text}

1
2
3
iex> Code.get_docs Code, :moduledoc
{2,
 "Utilities for managing code compilation, code evaluation and code loading.\n\nThis module complements Erlang's [`:code` module](http://www.erlang.org/doc/man/code.html)\nto add behaviour which is specific to Elixir. Almost all of the functions in this module\nhave global side effects on the behaviour of Elixir.\n"}

Enum.at/1

module.info(:compile)

能获取一些编译信息

1
2
3
iex> Code.__info__(:compile)
[options: [:debug_info], version: '6.0.1', time: {2017, 1, 5, 10, 33, 52},
 source: '/Users/jose/OSS/elixir/lib/elixir/lib/code.ex']

Kernel.Typespec.beam_specs/1

可以获取一个模块的类型信息

1
2
iex> Kernel.Typespec.beam_specs(Code)
 ...

如何在管道中用 ||

1
a |> func() |> Kernel.||([]) |> IO.inspect

也谈谈月饼事件

mooncake

中秋前夕,阿里的月饼事件引爆了圈内网络(事件回顾)。事情从当事人双方的角度来说都有道理:

  • 程序员:只是利用技术替代人工抢,和正常点击按钮抢并没有差别。没想到服务器端有程序 bug,可以不停抢。发现问题后没有付款,赶快联系了行政要退还。
  • HR: 5个人抢了120多盒月饼,过分了。安全部门的职责就是防范「黑灰产」的,怎么能「监守自盗」!(淘宝上有阿里的中秋月饼在售卖)

我相信阿里有自己的立场和原则,不会为外界质疑所动,甚至他们 4 小时的复盘结果也是毫无愧谦之意。也有人颇为赞同这种「捍卫价值观」的做法。

但阿里在程序员的心目中一落千丈已经是不争的事实,外界技术人员对阿里的看法更清醒了,阿里不是一家技术驱动的公司,这里没有工程师文化的土壤。甚至连内部的程序员也对事件非常失望。在我印象里,上市之后,阿里对技术「开源」的态度急转而下。阿里已经成为一个竭尽全力避免风险的公司。这无可厚非,几乎没有一家公司像阿里这样拥有巨大的成功而又充满争议。

toy

然而懂点程序的人都能看得出问题所在。

  • 假设这5个人,每人只抢了一盒月饼,从性质上来说,还是不是一样的?显然不是,因为明显可以排除拿去售卖 的可能。但从阿里的官方说辞里面,显然是一样的性质:「技术压制」造成不公平,以及利用系统漏洞获利。那这种情况,是不是也开除?
  • 对比一下以下情况,你觉得性质上是否有差别:

    • 抢到 100 盒月饼,每盒都付款;
    • 抢到 100 盒月饼,每盒都付款,然后进行高价售卖;
    • 抢到 100 盒月饼,但是只买 1 盒,其余的主动退掉。

我再举一个真实的发生在我身上的案例。有一次,妹纸和她的小伙伴们想要去一个博物馆看一个很不错的展览,这个展览在她们的行业里是很火爆的,需要在博物馆的网站上抢票,虽然票是免费发放的,但是只能在一个时间后才开放。她们担心到时候人多抢不到票,于是让我帮忙看看。我试了一下,发现时间限制只是在网页前端做的,完全可以绕开。就用几条命令帮她们提前申请到票了。我没有觉得有愧于心,因为:

  • 我有机会领到更多的票,高价出售给别人,但我没有;
  • 我可以炫耀给别人,让博物馆因为这个漏洞遭受损失,但我没有;

可见,我是有机会成为所谓的「黑灰产」的,但我没有。如果因为我将来可能会犯错,而将我提前关起来,这个逻辑是不合理的。

以我 7 年多阿里生涯来看,真没看出来抢月饼是不符合哪条价值观了。以未曾发生的事情(安全部成了自我黑灰产)为处理依据,臆断别人的用意;为了达到杀鸡儆猴的效果,做出令人啼笑皆非的处理。这件事情拿不出哪条条例,连价值观的说法也难以服众。还说不是处罚太重?这恐怕是本年度程序员最被误解的事情了吧?

从阿里离职以来,本着价值观「随时随地维护公司形象」,我从未说过阿里什么坏话,但这件事情让我太看不下去了,看不下去程序员被如此误解,如此对待。

只想表达一个观点:这件事情的处理是不懂程序员的阿里管理层犯下的一个错误,但会有什么影响,历史会告诉我们。

Minifying-with-webpack

webpack bundling

When coding in a modern modular way with Webpack is done, it enters the deploy phrase. We minify assets to reduce the network transfer cost. While bundling is not necessary nowadays thanks to HTTP/2, minifying is still an important work, because in many cases we can reduce the size to transfer between servers and clients:

  • unreachable codes can be removed
  • comments code can be removed
  • images can be optimised losslessly
  • variable names can be shortened in Javascript
  • HTML and SVGs can be optimised

Here in Helijia, we use webpack to build mobile apps. Here are some notes from us on minifying with Webpack.

Minifying Javascript / CSS

Webpack is shipped with some builtin plugins which can do the job well. * Dedupe Plugin searches for similar files and deduplicated them in the output. * UglyfiJS Plugin uses the famous UglifyJS to minify JS and CSS assets.

Tip: Use webpack stats to review your modules

Webpack can generate packing stats in json format while packing. We use Webpack visualizer to visualize it to find which part of our works can be optimized. It helps a lot by providing an detail view inside the bundled file.

webpack viz

Minifying HTML

HTML webpack plugin can help minifying HTML. Here is an example config:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
new HtmlWebpackPlugin({
  template : path.join(__dirname, '../src/template.html.ejs'),
  filename : 'index.html',
  title    : '河狸家',
  inject   : 'body',
  minify   : {
    html5                          : true,
    collapseWhitespace             : true,
    minifyCSS                      : true,
    minifyJS                       : true,
    minifyURLs                     : false,
    removeAttributeQuotes          : true,
    removeComments                 : true,
    removeEmptyAttributes          : true,
    removeOptionalTags             : true,
    removeRedundantAttributes      : true,
    removeScriptTypeAttributes     : true,
    removeStyleLinkTypeAttributese : true,
    useShortDoctype                : true
  }
}),

Minifying SVG

we are currently using svgo-loader to keep SVGs clean.

before:

1
2
3
4
5
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 10 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;">
    <path id="Shape-Copy" d="M9.157,0.136L0.157,8.636C-0.052,8.834 -0.052,9.166 0.157,9.364L9.157,17.864C9.357,18.053 9.674,18.044 9.864,17.843C10.053,17.643 10.044,17.326 9.843,17.136L1.222,8.994L9.843,0.864C10.044,0.674 10.053,0.357 9.864,0.157C9.674,-0.044 9.357,-0.053 9.157,0.136Z" style="fill:rgb(189,157,98);"/>
</svg>

after:

1
<svg class="SVGInline-svg icon-svg" style="width: 1em;height: 1em;" viewBox="0 0 10 18" xmlns="http://www.w3.org/2000/svg" fill-rule="evenodd" clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414"><path d="M9.157.136l-9 8.5a.5.5 0 0 0 0 .728l9 8.5a.5.5 0 0 0 .686-.728l-8.62-8.142 8.62-8.13a.5.5 0 0 0-.686-.728z" fill="#bd9d62"></path></svg>

Minifying bitmap Images

Image-webpack-loader can save a lot bandwidth.

Conclusion

Webpack provides a powerful way of web developing, we use some plugins to automating the deployment. and there are more out of our view. So let’s keep exploring and make web development more enjoyable.

Setting Up Your React Project Is Easier Than You Think

I have heard some ones complaining that setting up a Webpack + React project is not an easy job. It’s only partly true. Webpack is not well documented at this moment, and has many powerful features and plugins. This makes configuration on Webpack seem difficult to new developers. To make things even worse, there are tons of ‘starter kit’ projects over there, most of which are lack of maintainance.

Yeoman

Indeed, it is super easy to setup a brand new Webpack + React project using Yeoman.

Yeoman is a scaffolding tool for web developers. With diffrent generators we can generate different kinds of web projects quickly by lines of command.

Here’s how it is done:

1
2
3
4
5
6
7
8
# install Yeoman command and generator
npm install -g yo
npm install -g generator-react-webpack

# generate project
mkdir test-project
cd test-project
yo react-webpack

Thanks to generator-react-webpack, after answering some questions from yo you will get your project setup in seconds.

Easy and quick, right? Here’s what we have got out of the box:

  • a project with Webpack and React configured;
  • a solution for diffrent environments both for runtime and webpack configuration;
  • a flux project structure;
  • an optional PostCSS setup

Also this generator can help generate React components via command line.

为什么写不出博客了

近几年以来,我写博客的数量每况愈下,从几个月一篇,到几个季度一篇,现在已经将近一年一篇了。。 (✖╭╮✖)

今天也是无意间提起,让我思考这个问题

为什么越来越不写博客了呢?

  1. 首先想到的是「懒」+「忙」

    但其实「没时间」是一个最不成立的理由,所谓的「没时间」往往是「优先级不高」的幌子罢了。很多时候不忙,心里知道有很多事情应该去做,却什么也不想做。阳明先生 说没有「知易行难」这件事,不「行」是由于「知」得还不彻底。是的,我没有在自己心里把写博客重视起来当一回事,当成一定非完成不可的的事情。

  2. 追求的事物已发生了转移?

    是以前对技术更感兴趣一些,现在不感兴趣了吗?似乎也不是,现在每天起来之后也会阅读技术文章。对技术反而比以前更执着了。

  3. 害怕写不好

    以前反而大胆一些,也不怕误导别人;现在反而一些就想写很多,想写一个系列,系统地传授一系列的知识,系统地引导别人掌握一个东西。想做的事情太大,又没有足够的精力去做,于是就这样了。

原因算是找到了。

Done better than perfect

写得差总比不写要好,其实我的 blog 访问量那么低,也误导不了几个人。何况我最近也想明白了,没有所谓的「误导」,每个人都需要有自己的判断力,看到别人的论点需要有甄别的能力。阅读是自己的权利,判断也是自己的义务。坚持推荐我认为「好」的事物,就算因此让别人走了弯路,相信这种弯路也会是一种积累的。

所以我决定,从这篇开始,要多写写一些短小的技术文章了。即便没有人看也没关系,回归当初写 blog 的心境,记录自己的成长。

为什么 Limits 不生效

最近遇到一个奇怪的问题,无法将服务器的最大文件打开数量提高。

服务器是 Ubuntu Server 14.04

1
2
3
4
5
6
$lsb_release -a
No LSB modules are available.
Distributor ID:   Ubuntu
Description:  Ubuntu 14.04.2 LTS
Release:  14.04
Codename: trusty

正常情况下, /etc/security/limits.conf 的改动,应该在下次访问时就生效才对。 但是总是没生效,查了很多资料,尝试了很多修改,终于成功了

记录一下填的坑:

1. 确保 pam 生效

/etc/pam.d/login 中,存在:

1
session required pam_limits.so

2. 确保 ssh 使用 pam

/etc/pam.d/sshd 中,存在:

1
session required pam_limits.so

/etc/ssh/ssd_config 中, 存在:

1
UsePAM yes

3. limits.conf 建议不要使用星号

官方 manual 以及网上的教程有很多都用了 * 符号,然而不是所有系统都认的,我最后发现问题就是在这里!

1
2
3
4
5
6
7
8
9
10
# 不兼容方式:
* soft nofile 51200
* hard nofild 51200

# 兼容方式
root soft nofile 51200
root hard nofile 51200

qhwa soft nofile 51200
qhwa hard nofile 51200

如何确认问题之所在

查看当前用户的 limits

1
ulimit -a

查看另外一个用户的 limits

1
2
# 注意 ulimit 不是命令,而是 shell 方法
sudo -u <USER> sh -c 'ulimit -a'

经过 ssh 查看用户的 limits

1
ssh example.com 'ulimit -a'

Capistrano-hostmenu: 只操作指定的服务器

想必大家都用过 capistrano 了,部署代码到多个服务器上的神器。 不过使用中有个不太方便的地方是,某些情况下只想部署到其中几台。

例如我们新增了一个功能,但只想找一台服务器先部署上去测试一下,成功后再部署到整个集群。

有好几种方式可以做到:

  1. 临时修改发布脚本(config/deploy.rbconfig/deploy/***.rb
  2. 使用 HOST 命令行环境变量,或者 --host 参数, 比如 HOST=example.com cap production deploy

第1种不好重用,第2种在 capistrano v3.3 以上版本已经失效了。

我把我们项目中用的 task 抽出来做成了一个 gem: capistrano-hostmenu 功能实在是太简单,没有什么好描述的。。

一图顶千言: